diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 97ecf7616..a137d2052 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -3307,24 +3307,93 @@ extension Driver { } } + static private func validateProfilingGenerateArgs( + _ parsedOptions: inout ParsedOptions, + diagnosticEngine: DiagnosticsEngine + ) { + let genFlags: [Option] = [ + .profileGenerate, + .irProfileGenerate, + .irProfileGenerateEQ, + .csProfileGenerate, + .csProfileGenerateEQ, + ] + func resolveDualFormConflict(_ plain: Option, _ equalsForm: Option) { + if parsedOptions.hasArgument(plain), + parsedOptions.hasArgument(equalsForm) + { + diagnosticEngine.emit( + .error(Error.conflictingOptions(plain, equalsForm)), + location: nil + ) + providedGen.removeAll { $0 == equalsForm } + } + } + + var providedGen = genFlags.filter { parsedOptions.hasArgument($0) } + resolveDualFormConflict(.irProfileGenerate, .irProfileGenerateEQ) + resolveDualFormConflict(.csProfileGenerate, .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(.irProfileGenerateEQ), + let path = options.getLastArgument(.irProfileGenerateEQ)?.asSingle { + args.append("-fprofile-generate=\(path)") + } + + 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)") + } + + if options.contains(.irProfileUse), + let path = options.getLastArgument(.irProfileUse)?.asMultiple.last { + args.append("-fprofile-use=\(path)") + } + + return args + } + + internal func needsInstrumentedProfile(from parsedOptions: inout ParsedOptions) -> Bool { + parsedOptions.contains(.profileGenerate) || + parsedOptions.contains(.irProfileGenerate) || + parsedOptions.contains(.irProfileGenerateEQ) || + 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..1f13dc884 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -105,6 +105,8 @@ extension Option { public static let coveragePrefixMap: Option = Option("-coverage-prefix-map", .separate, attributes: [.frontend], metaVar: "", helpText: "Remap source paths in coverage info") public static let CrossModuleOptimization: Option = Option("-cross-module-optimization", .flag, attributes: [.helpHidden, .frontend], helpText: "Perform cross-module optimization") public static let crosscheckUnqualifiedLookup: Option = Option("-crosscheck-unqualified-lookup", .flag, attributes: [.frontend, .noDriver], helpText: "Compare legacy DeclContext- to ASTScope-based unqualified name lookup (for debugging)") + public static let csProfileGenerateEQ: Option = Option("-cs-profile-generate=", .joined, attributes: [.frontend, .noInteractive], metaVar: "", helpText: "Generate instrumented code to collect context sensitive 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 cxxInteropGettersSettersAsProperties: Option = Option("-cxx-interop-getters-setters-as-properties", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Import getters and setters as computed properties in Swift") public static let cxxInteropUseOpaquePointerForMoveonly: Option = Option("-cxx-interop-use-opaque-pointer-for-moveonly", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Testing flag that will be eliminated soon. Do not use.") public static let cxxInteroperabilityMode: Option = Option("-cxx-interoperability-mode=", .joined, attributes: [.frontend, .synthesizeInterface], helpText: "Enables C++ interoperability; pass 'default' to enable or 'off' to disable") @@ -643,6 +645,9 @@ extension Option { public static let swiftinterfaceCompilerVersion: Option = Option("-interface-compiler-version", .separate, attributes: [.helpHidden, .frontend], metaVar: "", helpText: "The version of the Swift compiler used to generate a .swiftinterface file") public static let internalizeAtLink: Option = Option("-internalize-at-link", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Allow internalizing public symbols and vtables at link time (assume all client code of public types is part of the same link unit, or that external symbols are explicitly requested via -exported_symbols_list)") public static let interpret: Option = Option("-interpret", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Immediate mode", group: .modes) + public static let irProfileGenerateEQ: Option = Option("-ir-profile-generate=", .joined, attributes: [.frontend, .noInteractive], metaVar: "", helpText: "Generate instrumented code to collect execution counts into /default.profraw (overridden by LLVM_PROFILE_FILE env var)") + 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 irProfileUse: Option = Option("-ir-profile-use=", .commaJoined, attributes: [.frontend, .noInteractive, .argumentIsPath], metaVar: "", helpText: "Supply an IR-level PGO profdata file to enable profile-guided optimization") public static let Isystem: Option = Option("-Isystem", .separate, attributes: [.frontend, .synthesizeInterface, .argumentIsPath], helpText: "Add directory to the system import search path") public static let I: Option = Option("-I", .joinedOrSeparate, attributes: [.frontend, .synthesizeInterface, .argumentIsPath], helpText: "Add directory to the import search path") public static let i: Option = Option("-i", .flag, group: .modes) @@ -1078,6 +1083,8 @@ extension Option { Option.coveragePrefixMap, Option.CrossModuleOptimization, Option.crosscheckUnqualifiedLookup, + Option.csProfileGenerateEQ, + Option.csProfileGenerate, Option.cxxInteropGettersSettersAsProperties, Option.cxxInteropUseOpaquePointerForMoveonly, Option.cxxInteroperabilityMode, @@ -1616,6 +1623,9 @@ extension Option { Option.swiftinterfaceCompilerVersion, Option.internalizeAtLink, Option.interpret, + Option.irProfileGenerateEQ, + Option.irProfileGenerate, + Option.irProfileUse, Option.Isystem, Option.I, Option.i, diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 33987889f..e8d46dc79 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))) @@ -4659,6 +4664,10 @@ final class SwiftDriverTests: XCTestCase { $1.expect(.error(Driver.Error.missingProfilingData(try toPath("profile.profdata").name))) } + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-ir-profile-use=profile.profdata"]) { + $1.expect(.error(Driver.Error.missingProfilingData(try toPath("profile.profdata").name))) + } + try withTemporaryDirectory { path in try localFileSystem.writeFileContents(path.appending(component: "profile.profdata"), bytes: .init()) try assertNoDriverDiagnostics(args: "swiftc", "-working-directory", path.pathString, "foo.swift", "-profile-use=profile.profdata") @@ -4677,6 +4686,45 @@ 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 withTemporaryDirectory { directoryPath in + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-profile-generate", "-ir-profile-generate=\(directoryPath)"]) { + $1.expect(.error(Driver.Error.conflictingOptions(.profileGenerate, .irProfileGenerateEQ))) + } + } + + 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))) + } + } + + try withTemporaryDirectory { directoryPath in + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-ir-profile-generate=\(directoryPath)", "-cs-profile-generate=\(directoryPath)"]) { + $1.expect(.error(Driver.Error.conflictingOptions(.irProfileGenerateEQ, .csProfileGenerateEQ))) + } + } + + try withTemporaryDirectory { directoryPath in + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-cs-profile-generate", "-cs-profile-generate=\(directoryPath)"]) { + $1.expect(.error(Driver.Error.conflictingOptions(.csProfileGenerate, .csProfileGenerateEQ))) + } + } + + try withTemporaryDirectory { directoryPath in + try assertDriverDiagnostics(args: ["swiftc", "foo.swift", "-ir-profile-generate", "-ir-profile-generate=\(directoryPath)"]) { + $1.expect(.error(Driver.Error.conflictingOptions(.irProfileGenerate, .irProfileGenerateEQ))) + } + } + } func testProfileSampleUseFrontendFlags() throws { @@ -4902,6 +4950,760 @@ 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 testIRProfileEqLinkerArgs() 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", "-ir-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("-fprofile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-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("-fprofile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-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("-fprofile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-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("-fprofile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-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("-fprofile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-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("-fprofile-generate=\(directoryPath)"))) + } + + do { + var driver = try Driver(args: ["swiftc", "-ir-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("-fprofile-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", "-ir-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 + + // -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=\(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", "-ir-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", "-ir-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 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')"))