Skip to content

Commit 0d295b7

Browse files
committed
Wire up support for module wrapping on non-Darwin platforms
1 parent 6cf6a44 commit 0d295b7

File tree

9 files changed

+193
-11
lines changed

9 files changed

+193
-11
lines changed

Sources/SWBAndroidPlatform/Plugin.swift

-3
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,6 @@ struct AndroidSDKRegistryExtension: SDKRegistryExtension {
8383
let defaultProperties: [String: PropertyListItem] = [
8484
"SDK_STAT_CACHE_ENABLE": "NO",
8585

86-
// Workaround to avoid `-add_ast_path` on Linux, apparently this needs to perform some "swift modulewrap" step instead.
87-
"GCC_GENERATE_DEBUGGING_SYMBOLS": .plString("NO"),
88-
8986
// Workaround to avoid `-dependency_info` on Linux.
9087
"LD_DEPENDENCY_INFO_FILE": .plString(""),
9188

Sources/SWBCore/SDKRegistry.swift

-3
Original file line numberDiff line numberDiff line change
@@ -749,9 +749,6 @@ public final class SDKRegistry: SDKRegistryLookup, CustomStringConvertible, Send
749749
switch operatingSystem {
750750
case .linux:
751751
defaultProperties = [
752-
// Workaround to avoid `-add_ast_path` on Linux, apparently this needs to perform some "swift modulewrap" step instead.
753-
"GCC_GENERATE_DEBUGGING_SYMBOLS": .plString("NO"),
754-
755752
// Workaround to avoid `-dependency_info` on Linux.
756753
"LD_DEPENDENCY_INFO_FILE": .plString(""),
757754

Sources/SWBCore/Settings/BuiltinMacros.swift

+2
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,7 @@ public final class BuiltinMacros {
974974
public static let SUPPORTS_TEXT_BASED_API = BuiltinMacros.declareBooleanMacro("SUPPORTS_TEXT_BASED_API")
975975
public static let SWIFT_AUTOLINK_EXTRACT_OUTPUT_PATH = BuiltinMacros.declarePathMacro("SWIFT_AUTOLINK_EXTRACT_OUTPUT_PATH")
976976
public static let PLATFORM_REQUIRES_SWIFT_AUTOLINK_EXTRACT = BuiltinMacros.declareBooleanMacro("PLATFORM_REQUIRES_SWIFT_AUTOLINK_EXTRACT")
977+
public static let PLATFORM_REQUIRES_SWIFT_MODULEWRAP = BuiltinMacros.declareBooleanMacro("PLATFORM_REQUIRES_SWIFT_MODULEWRAP")
977978
public static let SWIFT_ABI_CHECKER_BASELINE_DIR = BuiltinMacros.declareStringMacro("SWIFT_ABI_CHECKER_BASELINE_DIR")
978979
public static let SWIFT_ABI_CHECKER_EXCEPTIONS_FILE = BuiltinMacros.declareStringMacro("SWIFT_ABI_CHECKER_EXCEPTIONS_FILE")
979980
public static let SWIFT_ABI_GENERATION_TOOL_OUTPUT_DIR = BuiltinMacros.declareStringMacro("SWIFT_ABI_GENERATION_TOOL_OUTPUT_DIR")
@@ -2124,6 +2125,7 @@ public final class BuiltinMacros {
21242125
SUPPORTS_TEXT_BASED_API,
21252126
SWIFT_AUTOLINK_EXTRACT_OUTPUT_PATH,
21262127
PLATFORM_REQUIRES_SWIFT_AUTOLINK_EXTRACT,
2128+
PLATFORM_REQUIRES_SWIFT_MODULEWRAP,
21272129
SWIFT_ABI_CHECKER_BASELINE_DIR,
21282130
SWIFT_ABI_CHECKER_EXCEPTIONS_FILE,
21292131
SWIFT_ABI_GENERATION_TOOL_OUTPUT_DIR,

Sources/SWBCore/Settings/Settings.swift

+1
Original file line numberDiff line numberDiff line change
@@ -2519,6 +2519,7 @@ private class SettingsBuilder {
25192519

25202520
sdkTable.push(BuiltinMacros.DYNAMIC_LIBRARY_EXTENSION, literal: imageFormat.dynamicLibraryExtension)
25212521
sdkTable.push(BuiltinMacros.PLATFORM_REQUIRES_SWIFT_AUTOLINK_EXTRACT, literal: imageFormat.requiresSwiftAutolinkExtract)
2522+
sdkTable.push(BuiltinMacros.PLATFORM_REQUIRES_SWIFT_MODULEWRAP, literal: imageFormat.requiresSwiftModulewrap)
25222523
}
25232524

25242525
// Add additional SDK default settings.

Sources/SWBCore/Specs/Tools/LinkerTools.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ public final class LdLinkerSpec : GenericLinkerSpec, SpecIdentifierType, @unchec
584584
// If we are linking Swift and build for debugging, pass the right .swiftmodule file for the current architecture to the
585585
// linker. This is needed so that debugging these modules works correctly. Note that `swiftModulePaths` will be empty for
586586
// anything but static archives and object files, because dynamic libraries and frameworks do not require this.
587-
if isLinkUsingSwift && cbc.scope.evaluate(BuiltinMacros.GCC_GENERATE_DEBUGGING_SYMBOLS) {
587+
if isLinkUsingSwift && cbc.scope.evaluate(BuiltinMacros.GCC_GENERATE_DEBUGGING_SYMBOLS) && !cbc.scope.evaluate(BuiltinMacros.PLATFORM_REQUIRES_SWIFT_MODULEWRAP) {
588588
for library in libraries {
589589
if let swiftModulePath = library.swiftModulePaths[cbc.scope.evaluate(BuiltinMacros.CURRENT_ARCH)] {
590590
commandLine += ["-Xlinker", "-add_ast_path", "-Xlinker", swiftModulePath.str]

Sources/SWBCore/Specs/Tools/SwiftCompiler.swift

+6-1
Original file line numberDiff line numberDiff line change
@@ -2127,6 +2127,11 @@ public final class SwiftCompilerSpec : CompilerSpec, SpecIdentifierType, SwiftDi
21272127
return (inputs, outputs)
21282128
}()
21292129

2130+
if cbc.scope.evaluate(BuiltinMacros.PLATFORM_REQUIRES_SWIFT_MODULEWRAP) && cbc.scope.evaluate(BuiltinMacros.GCC_GENERATE_DEBUGGING_SYMBOLS) {
2131+
let moduleWrapOutput = Path(moduleFilePath.withoutSuffix + ".o")
2132+
moduleOutputPaths.append(moduleWrapOutput)
2133+
}
2134+
21302135
// Add const metadata outputs to extra compilation outputs
21312136
if await supportConstSupplementaryMetadata(cbc, delegate, compilationMode: compilationMode) {
21322137
// If using whole module optimization then we use the -master.swiftconstvalues file from the sole compilation task.
@@ -2963,7 +2968,7 @@ public final class SwiftCompilerSpec : CompilerSpec, SpecIdentifierType, SwiftDi
29632968
// be a source-less target which just contains object files in it's framework phase.
29642969
let currentPlatformFilter = PlatformFilter(scope)
29652970
let containsSources = (producer.configuredTarget?.target as? StandardTarget)?.sourcesBuildPhase?.buildFiles.filter { currentPlatformFilter.matches($0.platformFilters) }.isEmpty == false
2966-
if containsSources && inputFileTypes.contains(where: { $0.conformsTo(identifier: "sourcecode.swift") }) && scope.evaluate(BuiltinMacros.GCC_GENERATE_DEBUGGING_SYMBOLS) {
2971+
if containsSources && inputFileTypes.contains(where: { $0.conformsTo(identifier: "sourcecode.swift") }) && scope.evaluate(BuiltinMacros.GCC_GENERATE_DEBUGGING_SYMBOLS) && !scope.evaluate(BuiltinMacros.PLATFORM_REQUIRES_SWIFT_MODULEWRAP) {
29672972
let moduleName = scope.evaluate(BuiltinMacros.SWIFT_MODULE_NAME)
29682973
let moduleFileDir = scope.evaluate(BuiltinMacros.PER_ARCH_MODULE_FILE_DIR)
29692974
let moduleFilePath = moduleFileDir.join(moduleName + ".swiftmodule")

Sources/SWBUtil/ProcessInfo.swift

+9
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,15 @@ extension ImageFormat {
200200
return false
201201
}
202202
}
203+
204+
public var requiresSwiftModulewrap: Bool {
205+
switch self {
206+
case .macho:
207+
return false
208+
default:
209+
return true
210+
}
211+
}
203212
}
204213

205214
extension FixedWidthInteger {

Sources/SWBWebAssemblyPlatform/Plugin.swift

-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ struct WebAssemblySDKRegistryExtension: SDKRegistryExtension {
6161
let defaultProperties: [String: PropertyListItem] = [
6262
"SDK_STAT_CACHE_ENABLE": "NO",
6363

64-
// Workaround to avoid `-add_ast_path` on WebAssembly, apparently this needs to perform some "swift modulewrap" step instead.
65-
"GCC_GENERATE_DEBUGGING_SYMBOLS": .plString("NO"),
66-
6764
"GENERATE_TEXT_BASED_STUBS": "NO",
6865
"GENERATE_INTERMEDIATE_TEXT_BASED_STUBS": "NO",
6966

Tests/SWBBuildSystemTests/BuildOperationTests.swift

+174
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,180 @@ fileprivate struct BuildOperationTests: CoreBasedTests {
222222
}
223223
}
224224

225+
@Test(.requireSDKs(.host), .requireThreadSafeWorkingDirectory)
226+
func debuggableCommandLineTool() async throws {
227+
try await withTemporaryDirectory { (tmpDir: Path) in
228+
let testProject = try await TestProject(
229+
"TestProject",
230+
sourceRoot: tmpDir,
231+
groupTree: TestGroup(
232+
"SomeFiles",
233+
children: [
234+
TestFile("main.swift"),
235+
TestFile("dynamic.swift"),
236+
TestFile("static.swift"),
237+
]),
238+
buildConfigurations: [
239+
TestBuildConfiguration("Debug", buildSettings: [
240+
"ARCHS": "$(ARCHS_STANDARD)",
241+
"CODE_SIGNING_ALLOWED": ProcessInfo.processInfo.hostOperatingSystem() == .macOS ? "YES" : "NO",
242+
"CODE_SIGN_IDENTITY": "-",
243+
"CODE_SIGN_ENTITLEMENTS": "Entitlements.plist",
244+
"DEFINES_MODULE": "YES",
245+
"PRODUCT_NAME": "$(TARGET_NAME)",
246+
"SDKROOT": "$(HOST_PLATFORM)",
247+
"SUPPORTED_PLATFORMS": "$(HOST_PLATFORM)",
248+
"SWIFT_VERSION": swiftVersion,
249+
"GCC_GENERATE_DEBUGGING_SYMBOLS": "YES",
250+
])
251+
],
252+
targets: [
253+
TestStandardTarget(
254+
"tool",
255+
type: .commandLineTool,
256+
buildConfigurations: [
257+
TestBuildConfiguration("Debug", buildSettings: [
258+
"LD_RUNPATH_SEARCH_PATHS": "@loader_path/",
259+
])
260+
],
261+
buildPhases: [
262+
TestSourcesBuildPhase(["main.swift"]),
263+
TestFrameworksBuildPhase([
264+
TestBuildFile(.target("dynamiclib")),
265+
TestBuildFile(.target("staticlib")),
266+
])
267+
],
268+
dependencies: [
269+
"dynamiclib",
270+
"staticlib",
271+
]
272+
),
273+
TestStandardTarget(
274+
"dynamiclib",
275+
type: .dynamicLibrary,
276+
buildConfigurations: [
277+
TestBuildConfiguration("Debug", buildSettings: [
278+
"DYLIB_INSTALL_NAME_BASE": "$ORIGIN",
279+
"DYLIB_INSTALL_NAME_BASE[sdk=macosx*]": "@rpath",
280+
281+
// FIXME: Find a way to make these default
282+
"EXECUTABLE_PREFIX": "lib",
283+
"EXECUTABLE_PREFIX[sdk=windows*]": "",
284+
])
285+
],
286+
buildPhases: [
287+
TestSourcesBuildPhase(["dynamic.swift"]),
288+
]
289+
),
290+
TestStandardTarget(
291+
"staticlib",
292+
type: .staticLibrary,
293+
buildConfigurations: [
294+
TestBuildConfiguration("Debug", buildSettings: [
295+
// FIXME: Find a way to make these default
296+
"EXECUTABLE_PREFIX": "lib",
297+
"EXECUTABLE_PREFIX[sdk=windows*]": "",
298+
])
299+
],
300+
buildPhases: [
301+
TestSourcesBuildPhase(["static.swift"]),
302+
]
303+
),
304+
])
305+
let core = try await getCore()
306+
let tester = try await BuildOperationTester(core, testProject, simulated: false)
307+
308+
let projectDir = tester.workspace.projects[0].sourceRoot
309+
310+
try await tester.fs.writeFileContents(projectDir.join("main.swift")) { stream in
311+
stream <<< "import dynamiclib\n"
312+
stream <<< "import staticlib\n"
313+
stream <<< "dynamicLib()\n"
314+
stream <<< "dynamicLib()\n"
315+
stream <<< "staticLib()\n"
316+
stream <<< "print(\"Hello world\")\n"
317+
}
318+
319+
try await tester.fs.writeFileContents(projectDir.join("dynamic.swift")) { stream in
320+
stream <<< "public func dynamicLib() { }"
321+
}
322+
323+
try await tester.fs.writeFileContents(projectDir.join("static.swift")) { stream in
324+
stream <<< "public func staticLib() { }"
325+
}
326+
327+
try await tester.fs.writePlist(projectDir.join("Entitlements.plist"), .plDict([:]))
328+
329+
let provisioningInputs = [
330+
"dynamiclib": ProvisioningTaskInputs(identityHash: "-", signedEntitlements: .plDict([:]), simulatedEntitlements: .plDict([:])),
331+
"staticlib": ProvisioningTaskInputs(identityHash: "-", signedEntitlements: .plDict([:]), simulatedEntitlements: .plDict([:])),
332+
"tool": ProvisioningTaskInputs(identityHash: "-", signedEntitlements: .plDict([:]), simulatedEntitlements: .plDict([:]))
333+
]
334+
335+
let destination: RunDestinationInfo = .host
336+
try await tester.checkBuild(runDestination: destination, persistent: true, signableTargets: Set(provisioningInputs.keys), signableTargetInputs: provisioningInputs) { results in
337+
results.checkNoErrors()
338+
if core.hostOperatingSystem.imageFormat.requiresSwiftModulewrap {
339+
try results.checkTask(.matchTargetName("tool"), .matchRulePattern(["WriteAuxiliaryFile", .suffix("LinkFileList")])) { task in
340+
let auxFileAction = try #require(task.action as? AuxiliaryFileTaskAction)
341+
let contents = try tester.fs.read(auxFileAction.context.input).asString
342+
let files = contents.components(separatedBy: "\n").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }
343+
#expect(files.count == 2)
344+
#expect(files[0].hasSuffix("tool.o"))
345+
#expect(files[1].hasSuffix("main.o"))
346+
}
347+
let toolWrap = try #require(results.getTask(.matchTargetName("tool"), .matchRuleType("SwiftModuleWrap")))
348+
try results.checkTask(.matchTargetName("tool"), .matchRuleType("Ld")) { task in
349+
try results.checkTaskFollows(task, toolWrap)
350+
}
351+
352+
try results.checkTask(.matchTargetName("dynamiclib"), .matchRulePattern(["WriteAuxiliaryFile", .suffix("LinkFileList")])) { task in
353+
let auxFileAction = try #require(task.action as? AuxiliaryFileTaskAction)
354+
let contents = try tester.fs.read(auxFileAction.context.input).asString
355+
let files = contents.components(separatedBy: "\n").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }
356+
#expect(files.count == 2)
357+
#expect(files[0].hasSuffix("dynamiclib.o"))
358+
#expect(files[1].hasSuffix("dynamic.o"))
359+
}
360+
let dylibWrap = try #require(results.getTask(.matchTargetName("dynamiclib"), .matchRuleType("SwiftModuleWrap")))
361+
try results.checkTask(.matchTargetName("dynamiclib"), .matchRuleType("Ld")) { task in
362+
try results.checkTaskFollows(task, dylibWrap)
363+
}
364+
365+
try results.checkTask(.matchTargetName("staticlib"), .matchRulePattern(["WriteAuxiliaryFile", .suffix("LinkFileList")])) { task in
366+
let auxFileAction = try #require(task.action as? AuxiliaryFileTaskAction)
367+
let contents = try tester.fs.read(auxFileAction.context.input).asString
368+
let files = contents.components(separatedBy: "\n").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }
369+
#expect(files.count == 2)
370+
#expect(files[0].hasSuffix("staticlib.o"))
371+
#expect(files[1].hasSuffix("static.o"))
372+
}
373+
let staticWrap = try #require(results.getTask(.matchTargetName("staticlib"), .matchRuleType("SwiftModuleWrap")))
374+
try results.checkTask(.matchTargetName("staticlib"), .matchRuleType("Libtool")) { task in
375+
try results.checkTaskFollows(task, staticWrap)
376+
}
377+
}
378+
379+
let toolchain = try #require(try await getCore().toolchainRegistry.defaultToolchain)
380+
let environment: [String: String]
381+
if destination.platform == "linux" {
382+
environment = ["LD_LIBRARY_PATH": toolchain.path.join("usr/lib/swift/linux").str]
383+
} else {
384+
environment = [:]
385+
}
386+
387+
let executionResult = try await Process.getOutput(url: URL(fileURLWithPath: projectDir.join("build").join("Debug\(destination.builtProductsDirSuffix)").join(core.hostOperatingSystem.imageFormat.executableName(basename: "tool")).str), arguments: [], environment: environment)
388+
#expect(executionResult.exitStatus == .exit(0))
389+
if core.hostOperatingSystem == .windows {
390+
#expect(String(decoding: executionResult.stdout, as: UTF8.self) == "Hello world\r\n")
391+
} else {
392+
#expect(String(decoding: executionResult.stdout, as: UTF8.self) == "Hello world\n")
393+
}
394+
#expect(String(decoding: executionResult.stderr, as: UTF8.self) == "")
395+
}
396+
}
397+
}
398+
225399
/// Check that environment variables are propagated from the user environment correctly.
226400
@Test(.requireSDKs(.host), .skipHostOS(.windows), .requireSystemPackages(apt: "yacc", yum: "byacc"))
227401
func userEnvironment() async throws {

0 commit comments

Comments
 (0)