diff --git a/Sources/SwiftBuildSupport/Diagnostics+Extensions.swift b/Sources/SwiftBuildSupport/Diagnostics+Extensions.swift new file mode 100644 index 00000000000..06d112ae5d5 --- /dev/null +++ b/Sources/SwiftBuildSupport/Diagnostics+Extensions.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics + +extension Basics.Diagnostic { + package static var swiftBackDeployWarning: Self { + .warning( + """ + Swift compiler no longer supports statically linking the Swift libraries. They're included in the OS by \ + default starting with macOS Mojave 10.14.4 beta 3. For macOS Mojave 10.14.3 and earlier, there's an \ + optional "Swift 5 Runtime Support for Command Line Tools" package that can be downloaded from \"More Downloads\" \ + for Apple Developers at https://developer.apple.com/download/more/ + """ + ) + } +} diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index c92a598ea2c..eb5b7e9287d 100644 --- a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift +++ b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift @@ -891,6 +891,20 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { } } + for sanitizer in buildParameters.sanitizers.sanitizers { + self.observabilityScope.emit(debug:"Enabling \(sanitizer) sanitizer") + switch sanitizer { + case .address: + settings["ENABLE_ADDRESS_SANITIZER"] = "YES" + case .thread: + settings["ENABLE_THREAD_SANITIZER"] = "YES" + case .undefined: + settings["ENABLE_UNDEFINED_BEHAVIOR_SANITIZER"] = "YES" + case .fuzzer, .scudo: + throw StringError("\(sanitizer) is not currently supported with this build system.") + } + } + // FIXME: workaround for old Xcode installations such as what is in CI settings["LM_SKIP_METADATA_EXTRACTION"] = "YES" if let symbolGraphOptions { @@ -1028,7 +1042,7 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { } try settings.merge(Self.constructDebuggingSettingsOverrides(from: buildParameters.debuggingParameters), uniquingKeysWith: reportConflict) try settings.merge(Self.constructDriverSettingsOverrides(from: buildParameters.driverParameters), uniquingKeysWith: reportConflict) - try settings.merge(Self.constructLinkerSettingsOverrides(from: buildParameters.linkingParameters), uniquingKeysWith: reportConflict) + try settings.merge(self.constructLinkerSettingsOverrides(from: buildParameters.linkingParameters, triple: buildParameters.triple), uniquingKeysWith: reportConflict) try settings.merge(Self.constructTestingSettingsOverrides(from: buildParameters.testingParameters), uniquingKeysWith: reportConflict) try settings.merge(Self.constructAPIDigesterSettingsOverrides(from: buildParameters.apiDigesterMode), uniquingKeysWith: reportConflict) @@ -1115,7 +1129,10 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { return settings } - private static func constructLinkerSettingsOverrides(from parameters: BuildParameters.Linking) -> [String: String] { + private func constructLinkerSettingsOverrides( + from parameters: BuildParameters.Linking, + triple: Triple, + ) -> [String: String] { var settings: [String: String] = [:] if parameters.linkerDeadStrip { @@ -1133,7 +1150,19 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem { break } - // TODO: shouldLinkStaticSwiftStdlib + if triple.isDarwin() && parameters.shouldLinkStaticSwiftStdlib { + self.observabilityScope.emit(.swiftBackDeployWarning) + } else { + if parameters.shouldLinkStaticSwiftStdlib { + settings["SWIFT_FORCE_STATIC_LINK_STDLIB"] = "YES" + } else { + settings["SWIFT_FORCE_STATIC_LINK_STDLIB"] = "NO" + } + } + + if let resourcesPath = self.buildParameters.toolchain.swiftResourcesPath(isStatic: parameters.shouldLinkStaticSwiftStdlib) { + settings["SWIFT_RESOURCE_DIR"] = resourcesPath.pathString + } return settings } diff --git a/Sources/_InternalTestSupport/MockBuildTestHelper.swift b/Sources/_InternalTestSupport/MockBuildTestHelper.swift index 66fc707243d..bcb24f05a18 100644 --- a/Sources/_InternalTestSupport/MockBuildTestHelper.swift +++ b/Sources/_InternalTestSupport/MockBuildTestHelper.swift @@ -92,7 +92,8 @@ public func mockBuildParameters( linkTimeOptimizationMode: BuildParameters.LinkTimeOptimizationMode? = nil, omitFramePointers: Bool? = nil, enableXCFrameworksOnLinux: Bool = false, - prepareForIndexing: BuildParameters.PrepareForIndexingMode = .off + prepareForIndexing: BuildParameters.PrepareForIndexingMode = .off, + sanitizers: [Sanitizer] = [], ) -> BuildParameters { try! BuildParameters( destination: destination, @@ -104,6 +105,7 @@ public func mockBuildParameters( buildSystemKind: buildSystemKind, pkgConfigDirectories: [], workers: 3, + sanitizers: EnabledSanitizers(Set(sanitizers)), indexStoreMode: indexStoreMode, prepareForIndexing: prepareForIndexing, enableXCFrameworksOnLinux: enableXCFrameworksOnLinux, @@ -120,7 +122,7 @@ public func mockBuildParameters( linkTimeOptimizationMode: linkTimeOptimizationMode, shouldDisableLocalRpath: shouldDisableLocalRpath, shouldLinkStaticSwiftStdlib: shouldLinkStaticSwiftStdlib - ) + ), ) } diff --git a/Sources/_InternalTestSupport/SwiftTesting+Tags.swift b/Sources/_InternalTestSupport/SwiftTesting+Tags.swift index e5374633046..a398efeadfb 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Tags.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Tags.swift @@ -28,6 +28,13 @@ extension Tag.Platform { @Tag public static var FileSystem: Tag } +extension Tag.FunctionalArea { + @Tag public static var PIF: Tag + @Tag public static var IndexMode: Tag + @Tag public static var Sanitizer: Tag + @Tag public static var LinkSwiftStaticStdlib: Tag +} + extension Tag.Feature { public enum Command {} public enum CommandLineArguments {} @@ -192,8 +199,3 @@ extension Tag.Feature.Product { @Tag public static var Execute: Tag @Tag public static var Link: Tag } - -extension Tag.FunctionalArea { - @Tag public static var PIF: Tag - @Tag public static var IndexMode: Tag -} diff --git a/Tests/CommandsTests/Sanitizer+ExtensionsTests.swift b/Tests/CommandsTests/Sanitizer+ExtensionsTests.swift index c7008f269b1..6db32201f71 100644 --- a/Tests/CommandsTests/Sanitizer+ExtensionsTests.swift +++ b/Tests/CommandsTests/Sanitizer+ExtensionsTests.swift @@ -15,7 +15,8 @@ import enum PackageModel.Sanitizer @Suite( .tags( - Tag.TestSize.small, + .TestSize.small, + .FunctionalArea.Sanitizer, ), ) struct SanitizerExtensionTests { diff --git a/Tests/SwiftBuildSupportTests/PIFBuilderTests.swift b/Tests/SwiftBuildSupportTests/PIFBuilderTests.swift index 97831830612..a0f672136f3 100644 --- a/Tests/SwiftBuildSupportTests/PIFBuilderTests.swift +++ b/Tests/SwiftBuildSupportTests/PIFBuilderTests.swift @@ -180,7 +180,7 @@ struct PIFBuilderTests { let releaseConfig = try pif.workspace .project(named: "BasicExecutable") .target(named: "Executable") - .buildConfig(named: "Release") + .buildConfig(named: .release) for platform in ProjectModel.BuildSettings.Platform.allCases { let search_paths = releaseConfig.impartedBuildProperties.settings[.LIBRARY_SEARCH_PATHS, platform] diff --git a/Tests/SwiftBuildSupportTests/SwiftBuildSystemTests.swift b/Tests/SwiftBuildSupportTests/SwiftBuildSystemTests.swift index 469278229bb..6dc30c43edc 100644 --- a/Tests/SwiftBuildSupportTests/SwiftBuildSystemTests.swift +++ b/Tests/SwiftBuildSupportTests/SwiftBuildSystemTests.swift @@ -113,6 +113,25 @@ func withInstantiatedSwiftBuildSystem( } } +extension PackageModel.Sanitizer { + var hasSwiftBuildSupport: Bool { + switch self { + case .address, .thread, .undefined: true + case .fuzzer, .scudo: false + } + } + + var swiftBuildSettingName: String? { + switch self { + case .address: "ENABLE_ADDRESS_SANITIZER" + case .thread: "ENABLE_THREAD_SANITIZER" + case .undefined: "ENABLE_UNDEFINED_BEHAVIOR_SANITIZER" + case .fuzzer, .scudo: nil + } + + } +} + @Suite( .tags( .TestSize.medium, @@ -120,6 +139,145 @@ func withInstantiatedSwiftBuildSystem( ) struct SwiftBuildSystemTests { + @Suite( + .tags( + .FunctionalArea.Sanitizer, + ) + ) + struct SanitizerTests { + + @Test( + arguments: PackageModel.Sanitizer.allCases.filter { $0.hasSwiftBuildSupport }, + ) + func sanitizersSettingSetCorrectBuildRequest( + sanitizer: Sanitizer, + ) async throws { + try await withInstantiatedSwiftBuildSystem( + fromFixture: "PIFBuilder/Simple", + buildParameters: mockBuildParameters( + destination: .host, + sanitizers: [sanitizer], + ), + ) { swiftBuild, session, observabilityScope, buildParameters in + let buildSettings: SWBBuildParameters = try await swiftBuild.makeBuildParameters( + session: session, + symbolGraphOptions: nil, + setToolchainSetting: false, // Set this to false as SwiftBuild checks the toolchain path + ) + + let synthesizedArgs = try #require(buildSettings.overrides.synthesized) + + let swbSettingName = try #require(sanitizer.swiftBuildSettingName) + #expect(synthesizedArgs.table[swbSettingName] == "YES") + } + + } + + @Test( + arguments: PackageModel.Sanitizer.allCases.filter { !$0.hasSwiftBuildSupport }, + ) + func unsupportedSanitizersRaisesError( + sanitizer: Sanitizer, + ) async throws { + try await withInstantiatedSwiftBuildSystem( + fromFixture: "PIFBuilder/Simple", + buildParameters: mockBuildParameters( + destination: .host, + sanitizers: [sanitizer], + ), + ) { swiftBuild, session, observabilityScope, buildParameters in + await #expect(throws: (any Error).self) { + try await swiftBuild.makeBuildParameters( + session: session, + symbolGraphOptions: nil, + setToolchainSetting: false, // Set this to false as SwiftBuild checks the toolchain path + ) + } + } + } + } + + @Suite( + .tags( + .FunctionalArea.LinkSwiftStaticStdlib, + ), + ) + struct SwiftStaticStdlibSettingTests { + @Test + func makingBuildParametersRaisesAWarningWhenRunOnDarwin() async throws { + // GIVEN we have a Darwin triple + let triple = try Triple("x86_64-apple-macosx") + // AND we want to statically link Swift sdtlib + let shouldLinkStaticSwiftStdlib = true + try await withInstantiatedSwiftBuildSystem( + fromFixture: "PIFBuilder/Simple", + buildParameters: mockBuildParameters( + destination: .host, + shouldLinkStaticSwiftStdlib: shouldLinkStaticSwiftStdlib, + triple: triple, + ), + ) { swiftBuild, session, observabilityScope, buildParameters in + // WHEN we make the build parameter + let _: SWBBuildParameters = try await swiftBuild.makeBuildParameters( + session: session, + symbolGraphOptions: nil, + setToolchainSetting: false, // Set this to false as SwiftBuild checks the toolchain path + ) + + // THEN we expect a warning to be emitted + let warnings = observabilityScope.diagnostics.filter { + $0.severity == .warning + } + #expect(warnings.count == 1) + + let diagnostic = try #require(warnings.first) + // AND we expect the diagnostic message, severity and description to be as expected + #expect(diagnostic.message == Basics.Diagnostic.swiftBackDeployWarning.message) + #expect(diagnostic.severity == Basics.Diagnostic.swiftBackDeployWarning.severity) + #expect(diagnostic.description == Basics.Diagnostic.swiftBackDeployWarning.description) + } + } + + @Test( + arguments: [ + (shouldLinkStaticSwiftStdlib: true, expectedValue: "YES"), + (shouldLinkStaticSwiftStdlib: false, expectedValue: "NO"), + ] + ) + func swiftStaticStdLibSettingIsSetCorrectly( + shouldLinkStaticSwiftStdlib: Bool, + expectedValue: String + ) async throws { + // GIVEN we have a non-darwin triple AND we want statically link Swift sdtlib or not + let nonDarwinTriple = try Triple("i686-pc-windows-cygnus") + try await withInstantiatedSwiftBuildSystem( + fromFixture: "PIFBuilder/Simple", + buildParameters: mockBuildParameters( + destination: .host, + shouldLinkStaticSwiftStdlib: shouldLinkStaticSwiftStdlib, + triple: nonDarwinTriple, + ), + ) { swiftBuild, session, observabilityScope, buildParameters in + // WHEN we make the build parameter + let buildSettings = try await swiftBuild.makeBuildParameters( + session: session, + symbolGraphOptions: nil, + setToolchainSetting: false, // Set this to false as SwiftBuild checks the toolchain path + ) + + // THEN we don't expect any warnings to be emitted + let warnings = observabilityScope.diagnostics.filter { + $0.severity == .warning + } + #expect(warnings.isEmpty) + + // AND we expect the build setting to be set correctly + let synthesizedArgs = try #require(buildSettings.overrides.synthesized) + #expect(synthesizedArgs.table["SWIFT_FORCE_STATIC_LINK_STDLIB"] == expectedValue) + } + } + } + @Test( arguments: BuildParameters.IndexStoreMode.allCases, // arguments: [BuildParameters.IndexStoreMode.on],