diff --git a/Sources/SwiftBuildSupport/SwiftBuildSystem.swift b/Sources/SwiftBuildSupport/SwiftBuildSystem.swift index c92a598ea2c..fa365043c0c 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 { 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..9bb6b5eeaf3 100644 --- a/Sources/_InternalTestSupport/SwiftTesting+Tags.swift +++ b/Sources/_InternalTestSupport/SwiftTesting+Tags.swift @@ -28,6 +28,12 @@ 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 +} + extension Tag.Feature { public enum Command {} public enum CommandLineArguments {} @@ -192,8 +198,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..161c94eec1a 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,64 @@ 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 + ) + } + } + } + } + @Test( arguments: BuildParameters.IndexStoreMode.allCases, // arguments: [BuildParameters.IndexStoreMode.on],