diff --git a/Sources/SwiftDriver/IncrementalCompilation/BuildRecordInfo.swift b/Sources/SwiftDriver/IncrementalCompilation/BuildRecordInfo.swift index ea8efc299..ce6e94d24 100644 --- a/Sources/SwiftDriver/IncrementalCompilation/BuildRecordInfo.swift +++ b/Sources/SwiftDriver/IncrementalCompilation/BuildRecordInfo.swift @@ -109,8 +109,7 @@ import SwiftOptions var parsedOptions = parsedOptionsArg let hashInput = parsedOptions .filter { $0.option.affectsIncrementalBuild && $0.option.kind != .input} - .map { $0.option.spelling } - .sorted() + .map { $0.description } // The description includes the spelling of the option itself and, if present, its argument(s). .joined() return SHA256().hash(hashInput).hexadecimalRepresentation } diff --git a/Tests/SwiftDriverTests/IncrementalCompilationTests.swift b/Tests/SwiftDriverTests/IncrementalCompilationTests.swift index a0a847d0e..f026f41be 100644 --- a/Tests/SwiftDriverTests/IncrementalCompilationTests.swift +++ b/Tests/SwiftDriverTests/IncrementalCompilationTests.swift @@ -537,6 +537,74 @@ extension IncrementalCompilationTests { } } +// MARK: - Incremental argument hashing tests +extension IncrementalCompilationTests { + func testNullBuildWhenAddingAndRemovingArgumentsNotAffectingIncrementalBuilds() throws { + // Adding, removing, or changing the arguments of options which don't affect incremental builds should result in a null build. + try buildInitialState(extraArguments: ["-driver-batch-size-limit", "5", "-debug-diagnostic-names"]) + let driver = try checkNullBuild(extraArguments: ["-driver-batch-size-limit", "10", "-diagnostic-style", "swift"]) + let mandatoryJobs = try XCTUnwrap(driver.incrementalCompilationState?.mandatoryJobsInOrder) + XCTAssertTrue(mandatoryJobs.isEmpty) + } + + func testChangingOptionArgumentLeadsToRecompile() throws { + // If an option affects incremental builds, changing only the argument should trigger a full recompile. + try buildInitialState(extraArguments: ["-user-module-version", "1.0"]) + try doABuild( + "change user module version", + checkDiagnostics: true, + extraArguments: ["-user-module-version", "1.1"], + whenAutolinking: autolinkLifecycleExpectedDiags + ) { + enablingCrossModule + differentArgsPassed + disablingIncrementalDifferentArgsPassed + createdGraphFromSwiftdeps + findingBatchingCompiling("main", "other") + reading(deps: "main", "other") + schedLinking + } + } + + func testOptionReorderingLeadsToRecompile() throws { + // Reordering options which affect incremental builds should trigger a full recompile. + try buildInitialState(extraArguments: ["-warnings-as-errors", "-no-warnings-as-errors"]) + try doABuild( + "change user module version", + checkDiagnostics: true, + extraArguments: ["-no-warnings-as-errors", "-warnings-as-errors"], + whenAutolinking: autolinkLifecycleExpectedDiags + ) { + enablingCrossModule + differentArgsPassed + disablingIncrementalDifferentArgsPassed + createdGraphFromSwiftdeps + findingBatchingCompiling("main", "other") + reading(deps: "main", "other") + schedLinking + } + } + + func testArgumentReorderingLeadsToRecompile() throws { + // Reordering the arguments of an option which affect incremental builds should trigger a full recompile. + try buildInitialState(extraArguments: ["-Ifoo", "-Ibar"]) + try doABuild( + "change user module version", + checkDiagnostics: true, + extraArguments: ["-Ibar", "-Ifoo"], + whenAutolinking: autolinkLifecycleExpectedDiags + ) { + enablingCrossModule + differentArgsPassed + disablingIncrementalDifferentArgsPassed + createdGraphFromSwiftdeps + findingBatchingCompiling("main", "other") + reading(deps: "main", "other") + schedLinking + } + } +} + // MARK: - Incremental test stages extension IncrementalCompilationTests { /// Setup the initial post-build state. @@ -1390,6 +1458,12 @@ extension DiagVerifiable { @DiagsBuilder var disablingIncrementalCannotReadBuildRecord: [Diagnostic.Message] { "Incremental compilation: Disabling incremental build: could not read build record" } + @DiagsBuilder var differentArgsPassed: [Diagnostic.Message] { + "Incremental compilation: Incremental compilation has been disabled, because different arguments were passed to the compiler" + } + @DiagsBuilder var disablingIncrementalDifferentArgsPassed: [Diagnostic.Message] { + "Incremental compilation: Disabling incremental build: different arguments were passed to the compiler" + } @DiagsBuilder var missingMainDependencyEntry: [Diagnostic.Message] { .warning("ignoring -incremental; output file map has no master dependencies entry (\"swift-dependencies\" under \"\")") }