Skip to content

Commit 0a658dd

Browse files
committed
Infer [system] based on install path when generating clang modulemaps
rdar://144797825
1 parent a44c74b commit 0a658dd

File tree

4 files changed

+81
-10
lines changed

4 files changed

+81
-10
lines changed

Sources/SWBCore/Settings/BuiltinMacros.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,7 @@ public final class BuiltinMacros {
851851
public static let MODULEMAP_FILE_CONTENTS = BuiltinMacros.declareStringMacro("MODULEMAP_FILE_CONTENTS")
852852
public static let MODULEMAP_PATH = BuiltinMacros.declareStringMacro("MODULEMAP_PATH")
853853
public static let MODULEMAP_PRIVATE_FILE = BuiltinMacros.declareStringMacro("MODULEMAP_PRIVATE_FILE")
854+
public static let GENERATED_MODULEMAPS_USE_SYSTEM = BuiltinMacros.declareBooleanMacro("GENERATED_MODULEMAPS_USE_SYSTEM")
854855
public static let MODULES_FOLDER_PATH = BuiltinMacros.declarePathMacro("MODULES_FOLDER_PATH")
855856
public static let MODULE_VERIFIER_KIND = BuiltinMacros.declareEnumMacro("MODULE_VERIFIER_KIND") as EnumMacroDeclaration<ModuleVerifierKind>
856857
public static let MODULE_VERIFIER_LSV = BuiltinMacros.declareBooleanMacro("MODULE_VERIFIER_LSV")
@@ -1916,6 +1917,7 @@ public final class BuiltinMacros {
19161917
MODULEMAP_FILE_CONTENTS,
19171918
MODULEMAP_PATH,
19181919
MODULEMAP_PRIVATE_FILE,
1920+
GENERATED_MODULEMAPS_USE_SYSTEM,
19191921
MODULES_FOLDER_PATH,
19201922
MODULE_CACHE_DIR,
19211923
MODULE_NAME,

Sources/SWBCore/Settings/Settings.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,17 +2048,18 @@ private class SettingsBuilder {
20482048
// FIXME: Arguably we should emit a warning if the optimization settings are out of sync, as the user may be getting weird results. It's not clear if there are lots of old projects which might spuriously get such a warning, and this isn't a new state of affairs.
20492049
table.push(BuiltinMacros.IS_UNOPTIMIZED_BUILD, literal: (scope.evaluate(BuiltinMacros.GCC_OPTIMIZATION_LEVEL) == "0" || scope.evaluate(BuiltinMacros.SWIFT_OPTIMIZATION_LEVEL) == "-Onone"))
20502050

2051+
let privateInstallPaths = scope.evaluate(BuiltinMacros.__KNOWN_SPI_INSTALL_PATHS).map { Path($0) }
2052+
let publicInstallPaths = [
2053+
Path("/System/Library/Frameworks"),
2054+
Path("/System/Library/SubFrameworks"),
2055+
Path("/usr/lib"),
2056+
Path("/System/iOSSupport/System/Library/Frameworks"),
2057+
Path("/System/iOSSupport/System/Library/SubFrameworks"),
2058+
Path("/System/iOSSupport/usr/lib"),]
2059+
20512060
// If unset, infer the default SWIFT_LIBRARY_LEVEL from the INSTALL_PATH.
20522061
if scope.evaluateAsString(BuiltinMacros.SWIFT_LIBRARY_LEVEL).isEmpty &&
20532062
scope.evaluate(BuiltinMacros.MACH_O_TYPE) == "mh_dylib" {
2054-
let privateInstallPaths = scope.evaluate(BuiltinMacros.__KNOWN_SPI_INSTALL_PATHS).map { Path($0) }
2055-
let publicInstallPaths = [
2056-
Path("/System/Library/Frameworks"),
2057-
Path("/System/Library/SubFrameworks"),
2058-
Path("/usr/lib"),
2059-
Path("/System/iOSSupport/System/Library/Frameworks"),
2060-
Path("/System/iOSSupport/System/Library/SubFrameworks"),
2061-
Path("/System/iOSSupport/usr/lib"),]
20622063
let installPath = scope.evaluate(BuiltinMacros.INSTALL_PATH)
20632064

20642065
if table.contains(BuiltinMacros.SKIP_INSTALL) {
@@ -2073,6 +2074,16 @@ private class SettingsBuilder {
20732074
// Else, leave it to the compiler's default.
20742075
}
20752076

2077+
// If unset, infer the default SWIFT_LIBRARY_LEVEL from the INSTALL_PATH.
2078+
if scope.evaluateAsString(BuiltinMacros.GENERATED_MODULEMAPS_USE_SYSTEM).isEmpty {
2079+
let systemInstallPaths = publicInstallPaths + privateInstallPaths
2080+
let installPath = scope.evaluate(BuiltinMacros.INSTALL_PATH)
2081+
2082+
if systemInstallPaths.contains(where: { $0.isAncestorOrEqual(of: installPath) }) {
2083+
table.push(BuiltinMacros.GENERATED_MODULEMAPS_USE_SYSTEM, literal: true)
2084+
}
2085+
}
2086+
20762087
// Overrides specific to building for Mac Catalyst.
20772088
if platform?.familyName == "macOS", let sdkVariant, sdkVariant.isMacCatalyst {
20782089
// macCatalyst does not support ODR, but Foundation supports ODR APIs in Mac Catalyst loading the resources from the app bundle itself. Therefore, we simply disable ODR for Mac Catalyst.

Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/ModuleMapTaskProducer.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,10 @@ final class ModuleMapTaskProducer: PhasedTaskProducer, TaskProducer {
370370
//
371371
// NOTE: This is unrelated to the "MODULE_NAME" build setting, which is much older and used by kexts.
372372
let moduleName = scope.evaluate(BuiltinMacros.PRODUCT_MODULE_NAME)
373+
let isSystem = scope.evaluate(BuiltinMacros.GENERATED_MODULEMAPS_USE_SYSTEM)
373374

374375
// Create the trivial synthesized module map.
375-
outputStream <<< "framework module \(try moduleName.asModuleIdentifierString()) {\n"
376+
outputStream <<< "framework module \(try moduleName.asModuleIdentifierString()) \(isSystem ? "[system] " : ""){\n"
376377
outputStream <<< " umbrella header \"\(umbrellaHeaderName.asCStringLiteralContent)\"\n"
377378
outputStream <<< " export *\n"
378379
outputStream <<< "\n"
@@ -393,12 +394,13 @@ final class ModuleMapTaskProducer: PhasedTaskProducer, TaskProducer {
393394

394395
let interfaceHeaderName = scope.evaluate(BuiltinMacros.SWIFT_OBJC_INTERFACE_HEADER_NAME)
395396
assert(!interfaceHeaderName.isEmpty) // implied by exportsSwiftObjCAPI
397+
let isSystem = scope.evaluate(BuiltinMacros.GENERATED_MODULEMAPS_USE_SYSTEM)
396398

397399
// Swift only module map contents is a top level framework module. Swift contents
398400
// for a mixed module map is a submodule of the top level framework module (whose
399401
// name had better be PRODUCT_MODULE_NAME or things are going to get weird).
400402
if moduleInfo.forSwiftOnly {
401-
outputStream <<< "framework module \(try moduleName.asModuleIdentifierString()) {\n"
403+
outputStream <<< "framework module \(try moduleName.asModuleIdentifierString()) \(isSystem ? "[system] " : ""){\n"
402404
} else {
403405
outputStream <<< "\n"
404406
outputStream <<< "module \(try moduleName.asModuleIdentifierString()).Swift {\n"

Tests/SWBTaskConstructionTests/ModuleMapTaskConstructionTests.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,62 @@ fileprivate struct ModuleMapTaskConstructionTests: CoreBasedTests {
696696
}
697697
}
698698

699+
@Test(.requireSDKs(.macOS))
700+
func moduleMapGenerationSystemInference() async throws {
701+
let testProject = TestProject(
702+
"Project",
703+
groupTree: TestGroup(
704+
"Group",
705+
children: [
706+
TestFile("CookieCutter.h"),
707+
TestFile("CSource.c"),
708+
]),
709+
buildConfigurations: [
710+
TestBuildConfiguration("Debug", buildSettings: [
711+
"DEFINES_MODULE": "YES",
712+
"PRODUCT_NAME": "$(TARGET_NAME)",
713+
"GENERATE_INFOPLIST_FILE": "YES",
714+
"INSTALL_PATH": "/System/Library/Frameworks",
715+
])
716+
],
717+
targets: [
718+
TestStandardTarget(
719+
"CookieCutter",
720+
type: .framework,
721+
buildPhases: [
722+
TestHeadersBuildPhase([
723+
TestBuildFile("CookieCutter.h", headerVisibility: .public),
724+
]),
725+
TestSourcesBuildPhase([
726+
"CSource.c",
727+
]),
728+
]
729+
),
730+
])
731+
let tester = try await TaskConstructionTester(getCore(), testProject)
732+
let SRCROOT = tester.workspace.projects[0].sourceRoot.str
733+
734+
await tester.checkBuild() { results in
735+
results.checkTarget("CookieCutter") { target in
736+
let generatedModuleMap = "\(SRCROOT)/build/Project.build/Debug/CookieCutter.build/module.modulemap"
737+
let installedModuleMap = "\(SRCROOT)/build/Debug/CookieCutter.framework/Versions/A/Modules/module.modulemap"
738+
739+
let copyModuleMap = TaskCondition.matchRule(["Copy", installedModuleMap, generatedModuleMap])
740+
741+
results.checkWriteAuxiliaryFileTask(.matchTarget(target), .matchRule(["WriteAuxiliaryFile", generatedModuleMap])) { task, contents in
742+
#expect(contents == (OutputByteStream()
743+
<<< "framework module CookieCutter [system] {\n"
744+
<<< " umbrella header \"CookieCutter.h\"\n"
745+
<<< " export *\n"
746+
<<< "\n"
747+
<<< " module * { export * }\n"
748+
<<< "}\n").bytes)
749+
}
750+
}
751+
results.checkNoDiagnostics()
752+
}
753+
}
754+
699755
@Test(.requireSDKs(.macOS))
700756
func staticModuleMapContents() async throws {
701757
let testProject = try await TestProject(

0 commit comments

Comments
 (0)