From 58ce7ebfb38e8a1fae331b29ef2011e966e9594b Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Mon, 14 Jul 2025 10:52:25 -0700 Subject: [PATCH] Pass -platform_version to the linker when prelinking a target on Darwin platforms rdar://154346861 --- .../Tools/PrelinkedObjectLink.swift | 7 ++ Tests/SWBBuildSystemTests/LinkerTests.swift | 68 +++++++++++++++++++ .../PrelinkedObjectFileTests.swift | 10 +-- .../TaskConstructionTests.swift | 2 +- 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/Sources/SWBCore/SpecImplementations/Tools/PrelinkedObjectLink.swift b/Sources/SWBCore/SpecImplementations/Tools/PrelinkedObjectLink.swift index d651edfb..d2e17180 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/PrelinkedObjectLink.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/PrelinkedObjectLink.swift @@ -35,6 +35,13 @@ public final class PrelinkedObjectLinkSpec: CommandLineToolSpec, SpecImplementat var commandLine = [toolSpecInfo.toolPath.str] commandLine += ["-r", "-arch", arch] + if let buildPlatform = cbc.producer.sdk?.targetBuildVersionPlatform(sdkVariant: cbc.producer.sdkVariant), + let deploymentTargetMacro = cbc.producer.platform?.deploymentTargetMacro, + let minDeploymentTarget = cbc.scope.evaluate(deploymentTargetMacro).nilIfEmpty, + let sdkVersion = cbc.producer.sdk?.version { + commandLine += ["-platform_version", "\(buildPlatform.rawValue)", minDeploymentTarget, sdkVersion.canonicalDeploymentTargetForm.description] + } + // We do not pass the deployment target to the linker here. Instead the linker infers the platform and deployment target from the .o files being collected. We did briefly pass it to the linker to silence a linker warning - if we ever see issues here we should confer with the linker folks to make sure we do the right thing. See for more about the history here. let sysroot = cbc.scope.evaluate(BuiltinMacros.SDK_DIR) diff --git a/Tests/SWBBuildSystemTests/LinkerTests.swift b/Tests/SWBBuildSystemTests/LinkerTests.swift index 36833664..97f61702 100644 --- a/Tests/SWBBuildSystemTests/LinkerTests.swift +++ b/Tests/SWBBuildSystemTests/LinkerTests.swift @@ -360,4 +360,72 @@ fileprivate struct LinkerTests: CoreBasedTests { } } } + + @Test(.requireSDKs(.macOS)) + func prelinkingPropagatesPlatformVersion() async throws { + func createProject(_ tmpDir: Path, enableInterop: Bool) -> TestProject { + TestProject( + "TestProject", + sourceRoot: tmpDir, + groupTree: TestGroup( + "SomeFiles", + children: [ + TestFile("main.c"), + TestFile("lib.c"), + ]), + targets: [ + TestStandardTarget( + "cli", type: .commandLineTool, + buildConfigurations: [ + TestBuildConfiguration( + "Debug", + buildSettings: [ + "PRODUCT_NAME": "$(TARGET_NAME)", + "CODE_SIGNING_ALLOWED": "NO", + "GENERATE_PRELINK_OBJECT_FILE": "YES", + "GCC_SYMBOLS_PRIVATE_EXTERN": "NO", + "MACOSX_DEPLOYMENT_TARGET": "13.0", + ]) + ], + buildPhases: [ + TestSourcesBuildPhase(["main.c"]), + TestFrameworksBuildPhase([TestBuildFile(.target("lib"))]) + ], + dependencies: [TestTargetDependency("lib")] + ), + TestStandardTarget( + "lib", type: .staticLibrary, + buildConfigurations: [ + TestBuildConfiguration( + "Debug", + buildSettings: [ + "PRODUCT_NAME": "$(TARGET_NAME)", + "MACOSX_DEPLOYMENT_TARGET": "13.0", + ]) + ], + buildPhases: [ + TestSourcesBuildPhase(["lib.c"]) + ] + ), + ]) + } + + try await withTemporaryDirectory { tmpDir in + let testProject = createProject(tmpDir, enableInterop: true) + let tester = try await BuildOperationTester(getCore(), testProject, simulated: false) + let projectDir = tester.workspace.projects[0].sourceRoot + try await tester.fs.writeFileContents(projectDir.join("main.c")) { stream in + stream <<< "int foo(void); int main(void) { foo(); }" + } + try await tester.fs.writeFileContents(projectDir.join("lib.c")) { stream in + stream <<< "int foo(void) { return 42; }" + } + try await tester.checkBuild(runDestination: .macOS) { results in + results.checkNoDiagnostics() + results.checkTask(.matchRuleType("PrelinkedObjectLink")) { task in + task.checkCommandLineContainsUninterrupted(["-platform_version", "1", "13.0"]) + } + } + } + } } diff --git a/Tests/SWBTaskConstructionTests/PrelinkedObjectFileTests.swift b/Tests/SWBTaskConstructionTests/PrelinkedObjectFileTests.swift index 3c805622..45ee228d 100644 --- a/Tests/SWBTaskConstructionTests/PrelinkedObjectFileTests.swift +++ b/Tests/SWBTaskConstructionTests/PrelinkedObjectFileTests.swift @@ -73,7 +73,7 @@ fileprivate struct PrelinkedObjectFileTests: CoreBasedTests { results.checkTarget("AllLibraries") { target in // There should be tasks to create the prelinked object file and then the static library. results.checkTask(.matchTarget(target), .matchRuleType("PrelinkedObjectLink")) { task in - task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "x86_64", "-syslibroot", .equal(core.loadSDK(.macOS).path.str), "-exported_symbols_list", "Exports.exp", "-lWarningLibrary", "-lSomeLibrary", "-lAnotherLibrary", "-o", .equal("\(SRCROOT)/build/aProject.build/Debug/AllLibraries.build/Objects-normal/libAllLibraries.a-x86_64-prelink.o")]) + task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "x86_64", "-platform_version", "1", .any, .any, "-syslibroot", .equal(core.loadSDK(.macOS).path.str), "-exported_symbols_list", "Exports.exp", "-lWarningLibrary", "-lSomeLibrary", "-lAnotherLibrary", "-o", .equal("\(SRCROOT)/build/aProject.build/Debug/AllLibraries.build/Objects-normal/libAllLibraries.a-x86_64-prelink.o")]) } results.checkTask(.matchTarget(target), .matchRuleType("Libtool")) { task in task.checkCommandLineMatches([.suffix("libtool"), "-static", "-arch_only", "x86_64", "-D", "-syslibroot", .equal(core.loadSDK(.macOS).path.str), .equal("-L\(SRCROOT)/build/Debug"), "-filelist", .equal("\(SRCROOT)/build/aProject.build/Debug/AllLibraries.build/Objects-normal/x86_64/AllLibraries.LinkFileList"), "-dependency_info", "\(SRCROOT)/build/aProject.build/Debug/AllLibraries.build/Objects-normal/x86_64/AllLibraries_libtool_dependency_info.dat", "-o", .equal("\(SRCROOT)/build/Debug/libAllLibraries.a")]) @@ -102,7 +102,7 @@ fileprivate struct PrelinkedObjectFileTests: CoreBasedTests { results.checkTarget("AllLibraries") { target in // There should be tasks to create the prelinked object file and then the static library. results.checkTask(.matchTarget(target), .matchRuleType("PrelinkedObjectLink")) { task in - task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "x86_64", "-syslibroot", .equal(core.loadSDK(.macOS).path.str), "-exported_symbols_list", "Exports.exp", "-lWarningLibrary", "-lSomeLibrary", "-lAnotherLibrary", "-o", .equal("\(SRCROOT)/build/aProject.build/Debug/AllLibraries.build/Objects-normal/libAllLibraries.a-x86_64-prelink.o")]) + task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "x86_64", "-platform_version", "1", .any, .any, "-syslibroot", .equal(core.loadSDK(.macOS).path.str), "-exported_symbols_list", "Exports.exp", "-lWarningLibrary", "-lSomeLibrary", "-lAnotherLibrary", "-o", .equal("\(SRCROOT)/build/aProject.build/Debug/AllLibraries.build/Objects-normal/libAllLibraries.a-x86_64-prelink.o")]) } results.checkTask(.matchTarget(target), .matchRuleType("Libtool")) { task in task.checkCommandLineMatches([.suffix("libtool"), "-static", "-arch_only", "x86_64", "-D", "-syslibroot", .equal(core.loadSDK(.macOS).path.str), .equal("-L\(SRCROOT)/build/Debug/BuiltProducts"), "-filelist", .equal("\(SRCROOT)/build/aProject.build/Debug/AllLibraries.build/Objects-normal/x86_64/AllLibraries.LinkFileList"), "-dependency_info", "\(SRCROOT)/build/aProject.build/Debug/AllLibraries.build/Objects-normal/x86_64/AllLibraries_libtool_dependency_info.dat", "-o", "/tmp/aProject.dst/usr/local/lib/libAllLibraries.a"]) @@ -203,7 +203,7 @@ fileprivate struct PrelinkedObjectFileTests: CoreBasedTests { results.checkTarget("AllLibraries") { target in // There should be tasks to create the prelinked object file and then the static library. results.checkTask(.matchTarget(target), .matchRuleType("PrelinkedObjectLink")) { task in - task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "x86_64", "-syslibroot", .equal(core.loadSDK(.macOS).path.str), "-o", .equal("\(SRCROOT)/build/aProject.build/Debug-maccatalyst/AllLibraries.build/Objects-normal/libAllLibraries.a-x86_64-prelink.o")]) + task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "x86_64", "-platform_version", "6", .any, .any, "-syslibroot", .equal(core.loadSDK(.macOS).path.str), "-o", .equal("\(SRCROOT)/build/aProject.build/Debug-maccatalyst/AllLibraries.build/Objects-normal/libAllLibraries.a-x86_64-prelink.o")]) } results.checkTask(.matchTarget(target), .matchRuleType("Libtool")) { task in task.checkCommandLineMatches([.suffix("libtool"), "-static", "-arch_only", "x86_64", "-D", "-syslibroot", .equal(core.loadSDK(.macOS).path.str), .equal("-L\(SRCROOT)/build/Debug-maccatalyst"), "-L\(core.loadSDK(.macOS).path.str)/System/iOSSupport/usr/lib", "-L\(core.developerPath.path.str)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/maccatalyst", "-L\(core.loadSDK(.macOS).path.str)/System/iOSSupport/usr/lib", "-L\(core.developerPath.path.str)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/maccatalyst", "-filelist", .equal("\(SRCROOT)/build/aProject.build/Debug-maccatalyst/AllLibraries.build/Objects-normal/x86_64/AllLibraries.LinkFileList"), "-dependency_info", "\(SRCROOT)/build/aProject.build/Debug-maccatalyst/AllLibraries.build/Objects-normal/x86_64/AllLibraries_libtool_dependency_info.dat", "-o", .equal("\(SRCROOT)/build/Debug-maccatalyst/libAllLibraries.a")]) @@ -272,7 +272,7 @@ fileprivate struct PrelinkedObjectFileTests: CoreBasedTests { results.checkTarget("AllLibraries") { target in // There should be tasks to create the prelinked object file and then the static library. results.checkTask(.matchTarget(target), .matchRuleType("PrelinkedObjectLink")) { task in - task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "arm64", "-syslibroot", .equal(results.runDestinationSDK.path.str), "-o", .equal("\(SRCROOT)/build/aProject.build/Debug-iphoneos/AllLibraries.build/Objects-normal/libAllLibraries.a-arm64-prelink.o")]) + task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "arm64", "-platform_version", "2", .any, .any, "-syslibroot", .equal(results.runDestinationSDK.path.str), "-o", .equal("\(SRCROOT)/build/aProject.build/Debug-iphoneos/AllLibraries.build/Objects-normal/libAllLibraries.a-arm64-prelink.o")]) } results.checkTask(.matchTarget(target), .matchRuleType("Libtool")) { task in task.checkCommandLineMatches([.suffix("libtool"), "-static", "-arch_only", "arm64", "-D", "-syslibroot", .equal(results.runDestinationSDK.path.str), .equal("-L\(SRCROOT)/build/Debug-iphoneos"), "-filelist", .equal("\(SRCROOT)/build/aProject.build/Debug-iphoneos/AllLibraries.build/Objects-normal/arm64/AllLibraries.LinkFileList"), "-dependency_info", "\(SRCROOT)/build/aProject.build/Debug-iphoneos/AllLibraries.build/Objects-normal/arm64/AllLibraries_libtool_dependency_info.dat", "-o", .equal("\(SRCROOT)/build/Debug-iphoneos/libAllLibraries.a")]) @@ -340,7 +340,7 @@ fileprivate struct PrelinkedObjectFileTests: CoreBasedTests { results.checkTarget("AllLibraries") { target in // There should be tasks to create the prelinked object file and then the static library. results.checkTask(.matchTarget(target), .matchRuleType("PrelinkedObjectLink")) { task in - task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "x86_64", "-syslibroot", .equal(results.runDestinationSDK.path.str), "-o", .equal("\(SRCROOT)/build/aProject.build/Debug-iphonesimulator/AllLibraries.build/Objects-normal/libAllLibraries.a-x86_64-prelink.o")]) + task.checkCommandLineMatches([.suffix("ld"), "-r", "-arch", "x86_64", "-platform_version", "7", .any, .any, "-syslibroot", .equal(results.runDestinationSDK.path.str), "-o", .equal("\(SRCROOT)/build/aProject.build/Debug-iphonesimulator/AllLibraries.build/Objects-normal/libAllLibraries.a-x86_64-prelink.o")]) } results.checkTask(.matchTarget(target), .matchRuleType("Libtool")) { task in task.checkCommandLineMatches([.suffix("libtool"), "-static", "-arch_only", "x86_64", "-D", "-syslibroot", .equal(results.runDestinationSDK.path.str), .equal("-L\(SRCROOT)/build/Debug-iphonesimulator"), "-filelist", .equal("\(SRCROOT)/build/aProject.build/Debug-iphonesimulator/AllLibraries.build/Objects-normal/x86_64/AllLibraries.LinkFileList"), "-dependency_info", "\(SRCROOT)/build/aProject.build/Debug-iphonesimulator/AllLibraries.build/Objects-normal/x86_64/AllLibraries_libtool_dependency_info.dat", "-o", .equal("\(SRCROOT)/build/Debug-iphonesimulator/libAllLibraries.a")]) diff --git a/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift b/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift index a3931342..d2c11ff1 100644 --- a/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift +++ b/Tests/SWBTaskConstructionTests/TaskConstructionTests.swift @@ -895,7 +895,7 @@ fileprivate struct TaskConstructionTests: CoreBasedTests { } #expect(nonUniqueObjs[0] != nonUniqueObjs[1]) - task.checkCommandLineMatches([StringPattern.suffix("ld"), "-r", "-arch", "x86_64", "-syslibroot", .equal(core.loadSDK(.macOS).path.str), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile-Matched-Excluded.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile-Matched-Included.o"), .equal(nonUniqueObjs[0]), .equal(nonUniqueObjs[1]), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile_MRR.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/Lex.yy.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/y.tab.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/Script-Output-Custom-SourceFile.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/Script-Output-Standard-SourceFile.o"), "-o", .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/AppTarget-x86_64-prelink.o")]) + task.checkCommandLineMatches([StringPattern.suffix("ld"), "-r", "-arch", "x86_64", "-platform_version", "1", .any, .any, "-syslibroot", .equal(core.loadSDK(.macOS).path.str), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile-Matched-Excluded.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile-Matched-Included.o"), .equal(nonUniqueObjs[0]), .equal(nonUniqueObjs[1]), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile_MRR.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/Lex.yy.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/y.tab.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/Script-Output-Custom-SourceFile.o"), .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/Script-Output-Standard-SourceFile.o"), "-o", .equal("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/AppTarget-x86_64-prelink.o")]) task.checkInputs([ .path("\(SRCROOT)/build/aProject.build/Release/AppTarget.build/Objects-normal/x86_64/SourceFile.o"),