From 3f24e348b61b6cc840208d6f5518da4af662219e Mon Sep 17 00:00:00 2001 From: Sina Mahdavi Date: Wed, 2 Jul 2025 14:25:31 -0700 Subject: [PATCH] Add a multiArg -scanner-prefix-map-paths option and fix multiArg option parsing --- Sources/SwiftDriver/Driver/Driver.swift | 22 ++++++++++++------- .../ExplicitDependencyBuildPlanner.swift | 6 +++-- .../ModuleDependencyScanning.swift | 9 ++++---- .../SwiftDriver/Jobs/FrontendJobHelpers.swift | 5 +++-- Sources/SwiftOptions/OptionParsing.swift | 4 +--- Sources/SwiftOptions/Options.swift | 6 +++-- .../SwiftDriverTests/CachingBuildTests.swift | 7 +++--- 7 files changed, 35 insertions(+), 24 deletions(-) diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index 666998618..2f05071e5 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -296,7 +296,7 @@ public struct Driver { return ($0.key, $0.value) } do { - guard isFrontendArgSupported(.scannerPrefixMap) else { + guard isFrontendArgSupported(.scannerPrefixMapPaths) else { return [] } if let sdkMapping = scannerPrefixMapSDK, @@ -4007,14 +4007,20 @@ extension Driver { static func computeScanningPrefixMapper(_ parsedOptions: inout ParsedOptions) throws -> [AbsolutePath: AbsolutePath] { var mapping: [AbsolutePath: AbsolutePath] = [:] - for opt in parsedOptions.arguments(for: .scannerPrefixMap) { - let pluginArg = opt.argument.asSingle.split(separator: "=", maxSplits: 1) - if pluginArg.count != 2 { - throw Error.invalidArgumentValue(Option.scannerPrefixMap.spelling, opt.argument.asSingle) + for opt in parsedOptions.arguments(for: .scannerPrefixMapPaths, .scannerPrefixMap) { + if opt.option == .scannerPrefixMapPaths { + let key = try AbsolutePath(validating: opt.argument.asMultiple[0]) + let value = try AbsolutePath(validating: opt.argument.asMultiple[1]) + mapping[key] = value + } else { + let pluginArg = opt.argument.asSingle.split(separator: "=", maxSplits: 1) + if pluginArg.count != 2 { + throw Error.invalidArgumentValue(Option.scannerPrefixMap.spelling, opt.argument.asSingle) + } + let key = try AbsolutePath(validating: String(pluginArg[0])) + let value = try AbsolutePath(validating: String(pluginArg[1])) + mapping[key] = value } - let key = try AbsolutePath(validating: String(pluginArg[0])) - let value = try AbsolutePath(validating: String(pluginArg[1])) - mapping[key] = value } return mapping } diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift index 281b14089..232639c17 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ExplicitDependencyBuildPlanner.swift @@ -216,7 +216,8 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT // Add prefix mapping. The option is cache invariant so it can be added without affecting cache key. for (key, value) in prefixMap { commandLine.appendFlag("-cache-replay-prefix-map") - commandLine.appendFlag(value.pathString + "=" + key.pathString) + commandLine.appendFlag(value.pathString) + commandLine.appendFlag(key.pathString) } jobs.append(Job( @@ -277,7 +278,8 @@ public typealias ExternalTargetModuleDetailsMap = [ModuleDependencyId: ExternalT // Add prefix mapping. The option is cache invariant so it can be added without affecting cache key. for (key, value) in prefixMap { commandLine.appendFlag("-cache-replay-prefix-map") - commandLine.appendFlag(value.pathString + "=" + key.pathString) + commandLine.appendFlag(value.pathString) + commandLine.appendFlag(key.pathString) } jobs.append(Job( diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift index 21dae1a14..9eb3c0726 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift @@ -125,11 +125,12 @@ public extension Driver { commandLine.appendFlag(.scannerModuleValidation) } - if isFrontendArgSupported(.scannerPrefixMap) { - // construct `-scanner-prefix-mapper` for scanner. + if isFrontendArgSupported(.scannerPrefixMapPaths) { + // construct `-scanner-prefix-map-paths` for scanner. for (key, value) in prefixMapping { - commandLine.appendFlag(.scannerPrefixMap) - commandLine.appendFlag(key.pathString + "=" + value.pathString) + commandLine.appendFlag(.scannerPrefixMapPaths) + commandLine.appendFlag(key.pathString) + commandLine.appendFlag(value.pathString) } } diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index da3d151eb..db9fec682 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -1031,10 +1031,11 @@ extension Driver { } public mutating func addCacheReplayMapping(to commandLine: inout [Job.ArgTemplate]) { - if isCachingEnabled && isFrontendArgSupported(.scannerPrefixMap) { + if isCachingEnabled && isFrontendArgSupported(.scannerPrefixMapPaths) { for (key, value) in prefixMapping { commandLine.appendFlag("-cache-replay-prefix-map") - commandLine.appendFlag(value.pathString + "=" + key.pathString) + commandLine.appendFlag(value.pathString) + commandLine.appendFlag(key.pathString) } } } diff --git a/Sources/SwiftOptions/OptionParsing.swift b/Sources/SwiftOptions/OptionParsing.swift index bda78d5b9..a434e5947 100644 --- a/Sources/SwiftOptions/OptionParsing.swift +++ b/Sources/SwiftOptions/OptionParsing.swift @@ -182,10 +182,8 @@ extension OptionTable { throw OptionParseError.missingArgument( index: index - 1, argument: argument) } - parsedOptions.addOption(option, argument: .multiple(Array())) - arguments[index..", helpText: "Remap paths when replaying outputs from cache") + public static let cacheReplayPrefixMap: Option = Option("-cache-replay-prefix-map", .multiArg, attributes: [.frontend, .noDriver, .cacheInvariant], metaVar: " ", helpText: "Remap paths when replaying outputs from cache", numArgs: 2) public static let candidateModuleFile: Option = Option("-candidate-module-file", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "", helpText: "Specify Swift module may be ready to use for an interface") public static let casBackendMode: Option = Option("-cas-backend-mode=", .joined, attributes: [.frontend, .noDriver], metaVar: "native|casid|verify", helpText: "CASBackendMode for output kind") public static let casBackend: Option = Option("-cas-backend", .flag, attributes: [.frontend, .noDriver], helpText: "Enable using CASBackend for object file output") @@ -833,7 +833,8 @@ extension Option { public static let scannerOutputDir: Option = Option("-scanner-output-dir", .separate, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Directory for generated files from swift dependency scanner") public static let scannerPrefixMapSdk: Option = Option("-scanner-prefix-map-sdk", .separate, attributes: [], metaVar: "", helpText: "Remap paths within SDK reported by dependency scanner") public static let scannerPrefixMapToolchain: Option = Option("-scanner-prefix-map-toolchain", .separate, attributes: [], metaVar: "", helpText: "Remap paths within toolchain directory reported by dependency scanner") - public static let scannerPrefixMap: Option = Option("-scanner-prefix-map", .separate, attributes: [.frontend], metaVar: "", helpText: "Remap paths reported by dependency scanner") + public static let scannerPrefixMap: Option = Option("-scanner-prefix-map", .separate, attributes: [], metaVar: "", helpText: "Remap paths reported by dependency scanner") + public static let scannerPrefixMapPaths: Option = Option("-scanner-prefix-map-paths", .multiArg, attributes: [.frontend], metaVar: " ", helpText: "Remap paths reported by dependency scanner", numArgs: 2) public static let sdkModuleCachePath: Option = Option("-sdk-module-cache-path", .separate, attributes: [.frontend, .doesNotAffectIncrementalBuild, .argumentIsPath], helpText: "Specifies the module cache path for explicitly-built SDK modules") public static let sdk: Option = Option("-sdk", .separate, attributes: [.frontend, .synthesizeInterface, .argumentIsPath], metaVar: "", helpText: "Compile against ") public static let serializeBreakingChangesPath: Option = Option("-serialize-breaking-changes-path", .separate, attributes: [.noInteractive, .argumentIsPath], metaVar: "", helpText: "Serialize breaking changes found by the API digester to ") @@ -1801,6 +1802,7 @@ extension Option { Option.scannerPrefixMapSdk, Option.scannerPrefixMapToolchain, Option.scannerPrefixMap, + Option.scannerPrefixMapPaths, Option.sdkModuleCachePath, Option.sdk, Option.serializeBreakingChangesPath, diff --git a/Tests/SwiftDriverTests/CachingBuildTests.swift b/Tests/SwiftDriverTests/CachingBuildTests.swift index e1d4d419f..e4f77aa80 100644 --- a/Tests/SwiftDriverTests/CachingBuildTests.swift +++ b/Tests/SwiftDriverTests/CachingBuildTests.swift @@ -895,7 +895,7 @@ final class CachingBuildTests: XCTestCase { main.nativePathString(escaped: true)] + sdkArgumentsForTesting, env: env, interModuleDependencyOracle: dependencyOracle) - guard driver.isFrontendArgSupported(.scannerPrefixMap) else { + guard driver.isFrontendArgSupported(.scannerPrefixMapPaths) else { throw XCTSkip("frontend doesn't support prefix map") } let scanLibPath = try XCTUnwrap(driver.getSwiftScanLibPath()) @@ -903,8 +903,9 @@ final class CachingBuildTests: XCTestCase { let resolver = try ArgsResolver(fileSystem: localFileSystem) let scannerCommand = try driver.dependencyScannerInvocationCommand().1.map { try resolver.resolve($0) } - XCTAssertTrue(scannerCommand.contains("-scanner-prefix-map")) - XCTAssertTrue(scannerCommand.contains(try testInputsPath.description + "=/^src")) + XCTAssertTrue(scannerCommand.contains("-scanner-prefix-map-paths")) + XCTAssertTrue(scannerCommand.contains(try testInputsPath.description)) + XCTAssertTrue(scannerCommand.contains("/^src")) let jobs = try driver.planBuild() for job in jobs {