diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 97ecf7616..b5cc54b03 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -3307,24 +3307,87 @@ extension Driver { } } + static private func validateProfilingGenerateArgs( + _ parsedOptions: inout ParsedOptions, + diagnosticEngine: DiagnosticsEngine + ) { + let genFlags: [Option] = [ + .profileGenerate, + .irProfileGenerate, + .csProfileGenerate, + .csProfileGenerateEq, + ] + + var providedGen = genFlags.filter { parsedOptions.hasArgument($0) } + if parsedOptions.hasArgument(.csProfileGenerate), + parsedOptions.hasArgument(.csProfileGenerateEq) + { + // If both forms were specified, report a clear conflict. + diagnosticEngine.emit( + .error(Error.conflictingOptions(.csProfileGenerate, .csProfileGenerateEq)), + location: nil + ) + providedGen.removeAll { $0 == .csProfileGenerateEq } + } + + guard providedGen.count >= 2 else { return } + for i in 1.. 1 { + for i in 0..<(providedUse.count - 1) { + for j in (i + 1)..= 2 { - for i in 1.. [String] { + var args: [String] = [] + + if options.contains(.profileGenerate) || options.contains(.irProfileGenerate) { + args.append("-fprofile-generate") + } + + if options.contains(.csProfileGenerate) { + args.append("-fcs-profile-generate") + } + + if options.contains(.csProfileGenerateEq), + let path = options.getLastArgument(.csProfileGenerateEq)?.asSingle { + args.append("-fcs-profile-generate=\(path)") + } + + return args + } + + internal func needsInstrumentedProfile(from parsedOptions: inout ParsedOptions) -> Bool { + parsedOptions.contains(.profileGenerate) || + parsedOptions.contains(.irProfileGenerate) || + parsedOptions.contains(.csProfileGenerate) || + parsedOptions.contains(.csProfileGenerateEq) + } } @_spi(Testing) public enum ToolchainError: Swift.Error { diff --git a/Sources/SwiftOptions/Options.swift b/Sources/SwiftOptions/Options.swift index c3c9360fb..cd2a24a82 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -776,6 +776,9 @@ extension Option { public static let printZeroStats: Option = Option("-print-zero-stats", .flag, attributes: [.helpHidden, .frontend], helpText: "Prints all stats even if they are zero") public static let profileCoverageMapping: Option = Option("-profile-coverage-mapping", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate coverage data for use with profiled execution counts") public static let profileGenerate: Option = Option("-profile-generate", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate instrumented code to collect execution counts") + public static let irProfileGenerate: Option = Option("-ir-profile-generate", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)") + public static let csProfileGenerate: Option = Option("-cs-profile-generate", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate instrumented code to collect context sensitive execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)") + public static let csProfileGenerateEq: Option = Option("-cs-profile-generate=", .joined, attributes: [.frontend, .noInteractive, .argumentIsPath], metaVar: "", helpText: "Generate instrumented code to collect context sensitive execution counts into /default.profraw (overridden by LLVM_PROFILE_FILE env var)") public static let profileSampleUse: Option = Option("-profile-sample-use=", .joined, attributes: [.frontend, .noInteractive, .argumentIsPath], metaVar: "", helpText: "Supply sampling-based profiling data from llvm-profdata to enable profile-guided optimization") public static let profileStatsEntities: Option = Option("-profile-stats-entities", .flag, attributes: [.helpHidden, .frontend], helpText: "Profile changes to stats in -stats-output-dir, subdivided by source entity") public static let profileStatsEvents: Option = Option("-profile-stats-events", .flag, attributes: [.helpHidden, .frontend], helpText: "Profile changes to stats in -stats-output-dir") @@ -1749,6 +1752,9 @@ extension Option { Option.printZeroStats, Option.profileCoverageMapping, Option.profileGenerate, + Option.irProfileGenerate, + Option.csProfileGenerate, + Option.csProfileGenerateEq, Option.profileSampleUse, Option.profileStatsEntities, Option.profileStatsEvents, diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 33987889f..aa02ba185 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -4649,6 +4649,11 @@ final class SwiftDriverTests: XCTestCase { $1.expect(.error(Driver.Error.missingProfilingData(try toPath("profile.profdata").name))) } + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-ir-profile-generate", "-profile-use=profile.profdata"]) { + $1.expect(.error(Driver.Error.conflictingOptions(.irProfileGenerate, .profileUse))) + $1.expect(.error(Driver.Error.missingProfilingData(try toPath("profile.profdata").name))) + } + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-profile-sample-use=profile1.profdata", "-profile-use=profile2.profdata"]) { $1.expect(.error(Driver.Error.conflictingOptions(.profileUse, .profileSampleUse))) $1.expect(.error(Driver.Error.missingProfilingData(try toPath("profile1.profdata").name))) @@ -4677,6 +4682,20 @@ final class SwiftDriverTests: XCTestCase { $1.expect(.error(Driver.Error.missingProfilingData(path.appending(component: "profile.profdata,profile2.profdata").pathString))) } } + + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-profile-generate", "-ir-profile-generate"]) { + $1.expect(.error(Driver.Error.conflictingOptions(.profileGenerate, .irProfileGenerate))) + } + + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-ir-profile-generate", "-cs-profile-generate"]) { + $1.expect(.error(Driver.Error.conflictingOptions(.irProfileGenerate, .csProfileGenerate))) + } + + try withTemporaryDirectory { directoryPath in + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-ir-profile-generate", "-cs-profile-generate=\(directoryPath)"]) { + $1.expect(.error(Driver.Error.conflictingOptions(.irProfileGenerate, .csProfileGenerateEq))) + } + } } func testProfileSampleUseFrontendFlags() throws { @@ -4902,6 +4921,571 @@ final class SwiftDriverTests: XCTestCase { } } + func testIRProfileLinkerArgs() throws { + var envVars = ProcessEnv.block + envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) + + do { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-target", "x86_64-apple-macosx10.9", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fprofile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-target", "x86_64-apple-ios7.1-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fprofile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-target", "arm64-apple-ios7.1", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fprofile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-target", "x86_64-apple-tvos9.0-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fprofile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-target", "arm64-apple-tvos9.0", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fprofile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-target", "i386-apple-watchos2.0-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fprofile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-target", "armv7k-apple-watchos2.0", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fprofile-generate"))) + } + + // FIXME: This will fail when run on macOS, because + // swift-autolink-extract is not present + #if os(Linux) || os(Android) || os(Windows) + for triple in ["aarch64-unknown-linux-android", "x86_64-unknown-linux-gnu"] { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-target", triple, "test.swift"]) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + if triple == "aarch64-unknown-linux-android" { + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-aarch64-android.a")) + } else { + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-x86_64.a")) + } + XCTAssert(plannedJobs[1].commandLine.contains { $0 == .flag("-u__llvm_profile_runtime") }) + } + #endif + + // -ir-profile-generate should add libclang_rt.profile for WebAssembly targets + try withTemporaryDirectory { resourceDir in + try localFileSystem.writeFileContents(resourceDir.appending(components: "wasi", "static-executable-args.lnk")) { + $0.send("garbage") + } + + var env = ProcessEnv.block + env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "//bin/swift-autolink-extract" + + for triple in ["wasm32-unknown-wasi", "wasm32-unknown-wasip1-threads"] { + var driver = try Driver(args: [ + "swiftc", "-ir-profile-generate", "-target", triple, "test.swift", + "-resource-dir", resourceDir.pathString + ], env: env) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-wasm32.a")) + } + } + + for explicitUseLd in [true, false] { + var args = ["swiftc", "-ir-profile-generate", "-target", "x86_64-unknown-windows-msvc", "test.swift"] + if explicitUseLd { + // Explicitly passing '-use-ld=lld' should still result in '-lld-allow-duplicate-weak'. + args.append("-use-ld=lld") + } + var driver = try Driver(args: args) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + + // rdar://131295678 - Make sure we force the use of lld and pass + // '-lld-allow-duplicate-weak'. + XCTAssert(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssert(linkCmds.contains([.flag("-Xlinker"), .flag("-lld-allow-duplicate-weak")])) + } + + // rdar://131295678 - Make sure we force the use of lld and pass + // '-lld-allow-duplicate-weak' even if the user requests something else. + do { + var driver = try Driver(args: ["swiftc", "-ir-profile-generate", "-use-ld=link", "-target", "x86_64-unknown-windows-msvc", "test.swift"]) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + + XCTAssertFalse(linkCmds.contains(.flag("-fuse-ld=link"))) + XCTAssertTrue(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssertTrue(linkCmds.contains(.flag("-lld-allow-duplicate-weak"))) + } + + do { + // If we're not building for profiling, don't add '-lld-allow-duplicate-weak'. + var driver = try Driver(args: ["swiftc", "-use-ld=lld", "-target", "x86_64-unknown-windows-msvc", "test.swift"]) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + XCTAssertTrue(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssertFalse(linkCmds.contains(.flag("-lld-allow-duplicate-weak"))) + } + } + + func testCSProfileLinkerArgs() throws { + var envVars = ProcessEnv.block + envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-target", "x86_64-apple-macosx10.9", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-target", "x86_64-apple-ios7.1-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-target", "arm64-apple-ios7.1", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-target", "x86_64-apple-tvos9.0-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-target", "arm64-apple-tvos9.0", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-target", "i386-apple-watchos2.0-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-target", "armv7k-apple-watchos2.0", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate"))) + } + + // FIXME: This will fail when run on macOS, because + // swift-autolink-extract is not present + #if os(Linux) || os(Android) || os(Windows) + for triple in ["aarch64-unknown-linux-android", "x86_64-unknown-linux-gnu"] { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-target", triple, "test.swift"]) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + if triple == "aarch64-unknown-linux-android" { + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-aarch64-android.a")) + } else { + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-x86_64.a")) + } + XCTAssert(plannedJobs[1].commandLine.contains { $0 == .flag("-u__llvm_profile_runtime") }) + } + #endif + + // -cs-profile-generate should add libclang_rt.profile for WebAssembly targets + try withTemporaryDirectory { resourceDir in + try localFileSystem.writeFileContents(resourceDir.appending(components: "wasi", "static-executable-args.lnk")) { + $0.send("garbage") + } + + var env = ProcessEnv.block + env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "//bin/swift-autolink-extract" + + for triple in ["wasm32-unknown-wasi", "wasm32-unknown-wasip1-threads"] { + var driver = try Driver(args: [ + "swiftc", "-cs-profile-generate", "-target", triple, "test.swift", + "-resource-dir", resourceDir.pathString + ], env: env) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-wasm32.a")) + } + } + + for explicitUseLd in [true, false] { + var args = ["swiftc", "-cs-profile-generate", "-target", "x86_64-unknown-windows-msvc", "test.swift"] + if explicitUseLd { + // Explicitly passing '-use-ld=lld' should still result in '-lld-allow-duplicate-weak'. + args.append("-use-ld=lld") + } + var driver = try Driver(args: args) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + + // rdar://131295678 - Make sure we force the use of lld and pass + // '-lld-allow-duplicate-weak'. + XCTAssert(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssert(linkCmds.contains([.flag("-Xlinker"), .flag("-lld-allow-duplicate-weak")])) + } + + // rdar://131295678 - Make sure we force the use of lld and pass + // '-lld-allow-duplicate-weak' even if the user requests something else. + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate", "-use-ld=link", "-target", "x86_64-unknown-windows-msvc", "test.swift"]) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + + XCTAssertFalse(linkCmds.contains(.flag("-fuse-ld=link"))) + XCTAssertTrue(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssertTrue(linkCmds.contains(.flag("-lld-allow-duplicate-weak"))) + } + + do { + // If we're not building for profiling, don't add '-lld-allow-duplicate-weak'. + var driver = try Driver(args: ["swiftc", "-use-ld=lld", "-target", "x86_64-unknown-windows-msvc", "test.swift"]) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + XCTAssertTrue(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssertFalse(linkCmds.contains(.flag("-lld-allow-duplicate-weak"))) + } + } + + func testCSEqProfileLinkerArgs() throws { + var envVars = ProcessEnv.block + envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) + try withTemporaryDirectory { directoryPath in + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", "x86_64-apple-macosx10.9", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", "x86_64-apple-ios7.1-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", "arm64-apple-ios7.1", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", "x86_64-apple-tvos9.0-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", "arm64-apple-tvos9.0", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", "i386-apple-watchos2.0-simulator", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", "armv7k-apple-watchos2.0", "test.swift"], + env: envVars) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fcs-profile-generate=\(directoryPath)"))) + } + + // FIXME: This will fail when run on macOS, because + // swift-autolink-extract is not present + #if os(Linux) || os(Android) || os(Windows) + for triple in ["aarch64-unknown-linux-android", "x86_64-unknown-linux-gnu"] { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", triple, "test.swift"]) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + if triple == "aarch64-unknown-linux-android" { + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-aarch64-android.a")) + } else { + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-x86_64.a")) + } + XCTAssert(plannedJobs[1].commandLine.contains { $0 == .flag("-u__llvm_profile_runtime") }) + } + #endif + + // -cs-profile-generate= should add libclang_rt.profile for WebAssembly targets + try withTemporaryDirectory { resourceDir in + try localFileSystem.writeFileContents(resourceDir.appending(components: "wasi", "static-executable-args.lnk")) { + $0.send("garbage") + } + + var env = ProcessEnv.block + env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "//bin/swift-autolink-extract" + + for triple in ["wasm32-unknown-wasi", "wasm32-unknown-wasip1-threads"] { + var driver = try Driver(args: [ + "swiftc", "-cs-profile-generate=\(directoryPath)", "-target", triple, "test.swift", + "-resource-dir", resourceDir.pathString + ], env: env) + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + XCTAssert(plannedJobs[1].commandLine.containsPathWithBasename("libclang_rt.profile-wasm32.a")) + } + } + + for explicitUseLd in [true, false] { + var args = ["swiftc", "-cs-profile-generate=\(directoryPath)", "-target", "x86_64-unknown-windows-msvc", "test.swift"] + if explicitUseLd { + // Explicitly passing '-use-ld=lld' should still result in '-lld-allow-duplicate-weak'. + args.append("-use-ld=lld") + } + var driver = try Driver(args: args) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + + // rdar://131295678 - Make sure we force the use of lld and pass + // '-lld-allow-duplicate-weak'. + XCTAssert(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssert(linkCmds.contains([.flag("-Xlinker"), .flag("-lld-allow-duplicate-weak")])) + } + + // rdar://131295678 - Make sure we force the use of lld and pass + // '-lld-allow-duplicate-weak' even if the user requests something else. + do { + var driver = try Driver(args: ["swiftc", "-cs-profile-generate=\(directoryPath)", "-use-ld=link", "-target", "x86_64-unknown-windows-msvc", "test.swift"]) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + + XCTAssertFalse(linkCmds.contains(.flag("-fuse-ld=link"))) + XCTAssertTrue(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssertTrue(linkCmds.contains(.flag("-lld-allow-duplicate-weak"))) + } + + do { + // If we're not building for profiling, don't add '-lld-allow-duplicate-weak'. + var driver = try Driver(args: ["swiftc", "-use-ld=lld", "-target", "x86_64-unknown-windows-msvc", "test.swift"]) + let plannedJobs = try driver.planBuild() + + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + + XCTAssertEqual(plannedJobs[1].kind, .link) + + let linkCmds = plannedJobs[1].commandLine + XCTAssertTrue(linkCmds.contains(.flag("-fuse-ld=lld"))) + XCTAssertFalse(linkCmds.contains(.flag("-lld-allow-duplicate-weak"))) + } + } + } + func testConditionalCompilationArgValidation() throws { try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-DFOO=BAR"]) { $1.expect(.warning("conditional compilation flags do not have values in Swift; they are either present or absent (rather than 'FOO=BAR')"))