Skip to content

Commit 8be50bf

Browse files
committed
WIP: swift-get-version
The changes in #6585 meant that we are no longer using LLBuild's built-in tracking of Swift compiler versions. We are lacking the infrastructure to really use that for the same purpose since we are now running the Swift compiler as a shell-tool, so this is adding a poor man's version of that. We have a task that writes the output of `swift -version` for a particular Swift compiler path to the build directory and all Swift tasks that are using that compiler depend on that file as an input. This should give us the desired behavior of the tasks re-running if the Swift version changes. This PR passes the existing test suite, but I need to add some tests for the particular scenario of re-running if the version changes. rdar://114047018
1 parent 667a006 commit 8be50bf

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

Sources/Build/LLBuildManifestBuilder.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public class LLBuildManifestBuilder {
5353
var buildParameters: BuildParameters { self.plan.buildParameters }
5454
var buildEnvironment: BuildEnvironment { self.buildParameters.buildEnvironment }
5555

56+
/// Mapping from Swift compiler path to Swift get version files.
57+
var swiftGetVersionFiles = [AbsolutePath: AbsolutePath]()
58+
5659
/// Create a new builder with a build plan.
5760
public init(
5861
_ plan: BuildPlan,
@@ -71,6 +74,8 @@ public class LLBuildManifestBuilder {
7174
/// Generate manifest at the given path.
7275
@discardableResult
7376
public func generateManifest(at path: AbsolutePath) throws -> BuildManifest {
77+
self.swiftGetVersionFiles.removeAll()
78+
7479
self.manifest.createTarget(TargetKind.main.targetName)
7580
self.manifest.createTarget(TargetKind.test.targetName)
7681
self.manifest.defaultTarget = TargetKind.main.targetName
@@ -608,6 +613,9 @@ extension LLBuildManifestBuilder {
608613
) throws -> [Node] {
609614
var inputs = target.sources.map(Node.file)
610615

616+
let swiftVersionFilePath = addSwiftGetVersionCommand(buildParameters: target.buildParameters)
617+
inputs.append(.file(swiftVersionFilePath))
618+
611619
// Add resources node as the input to the target. This isn't great because we
612620
// don't need to block building of a module until its resources are assembled but
613621
// we don't currently have a good way to express that resources should be built
@@ -733,6 +741,22 @@ extension LLBuildManifestBuilder {
733741
arguments: moduleWrapArgs
734742
)
735743
}
744+
745+
private func addSwiftGetVersionCommand(buildParameters: BuildParameters) -> AbsolutePath {
746+
let swiftCompilerPath = buildParameters.toolchain.swiftCompilerPath
747+
748+
// If we are already tracking this compiler, we can re-use the existing command by just returning the tracking file.
749+
if let swiftVersionFilePath = swiftGetVersionFiles[swiftCompilerPath] {
750+
return swiftVersionFilePath
751+
}
752+
753+
// Otherwise, come up with a path for the new file and generate a command to populate it.
754+
let swiftCompilerPathHash = String(swiftCompilerPath.pathString.hash, radix: 16, uppercase: true)
755+
let swiftVersionFilePath = buildParameters.buildPath.appending(component: "swift-version-\(swiftCompilerPathHash).txt")
756+
self.manifest.addSwiftGetVersionCommand(swiftCompilerPath: swiftCompilerPath, swiftVersionFilePath: swiftVersionFilePath)
757+
swiftGetVersionFiles[swiftCompilerPath] = swiftVersionFilePath
758+
return swiftVersionFilePath
759+
}
736760
}
737761

738762
extension SwiftDriver.Job {

Sources/LLBuildManifest/BuildManifest.swift

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@
1212

1313
import Basics
1414

15+
import class TSCBasic.Process
16+
1517
public protocol AuxiliaryFileType {
1618
static var name: String { get }
1719

1820
static func getFileContents(inputs: [Node]) throws -> String
1921
}
2022

2123
public enum WriteAuxiliary {
22-
public static let fileTypes: [AuxiliaryFileType.Type] = [LinkFileList.self, SourcesFileList.self]
24+
public static let fileTypes: [AuxiliaryFileType.Type] = [LinkFileList.self, SourcesFileList.self, SwiftGetVersion.self]
2325

2426
public struct LinkFileList: AuxiliaryFileType {
2527
public static let name = "link-file-list"
@@ -76,6 +78,28 @@ public enum WriteAuxiliary {
7678
return contents
7779
}
7880
}
81+
82+
public struct SwiftGetVersion: AuxiliaryFileType {
83+
public static let name = "swift-get-version"
84+
85+
// TODO: Should be always-out-of-date
86+
87+
public static func computeInputs(swiftCompilerPath: AbsolutePath) -> [Node] {
88+
return [.virtual(Self.name), .file(swiftCompilerPath)]
89+
}
90+
91+
public static func getFileContents(inputs: [Node]) throws -> String {
92+
guard let swiftCompilerPathString = inputs.first(where: { $0.kind == .file })?.name else {
93+
throw Error.unknownSwiftCompilerPath
94+
}
95+
let swiftCompilerPath = try AbsolutePath(validating: swiftCompilerPathString)
96+
return try TSCBasic.Process.checkNonZeroExit(args: swiftCompilerPath.pathString, "-version")
97+
}
98+
99+
private enum Error: Swift.Error {
100+
case unknownSwiftCompilerPath
101+
}
102+
}
79103
}
80104

81105
public struct BuildManifest {
@@ -173,6 +197,16 @@ public struct BuildManifest {
173197
commands[name] = Command(name: name, tool: tool)
174198
}
175199

200+
public mutating func addSwiftGetVersionCommand(
201+
swiftCompilerPath: AbsolutePath,
202+
swiftVersionFilePath: AbsolutePath
203+
) {
204+
let inputs = WriteAuxiliary.SwiftGetVersion.computeInputs(swiftCompilerPath: swiftCompilerPath)
205+
let tool = WriteAuxiliaryFile(inputs: inputs, outputFilePath: swiftVersionFilePath)
206+
let name = swiftVersionFilePath.pathString
207+
commands[name] = Command(name: name, tool: tool)
208+
}
209+
176210
public mutating func addPkgStructureCmd(
177211
name: String,
178212
inputs: [Node],

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -867,8 +867,9 @@ final class BuildPlanTests: XCTestCase {
867867
let llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope)
868868
try llbuild.generateManifest(at: yaml)
869869
let contents: String = try fs.readFileContents(yaml)
870+
let swiftGetVersionFilePath = try XCTUnwrap(llbuild.swiftGetVersionFiles.first?.value)
870871
XCTAssertMatch(contents, .contains("""
871-
inputs: ["\(Pkg.appending(components: "Sources", "exe", "main.swift").escapedPathString())","\(buildPath.appending(components: "PkgLib.swiftmodule").escapedPathString())","\(buildPath.appending(components: "exe.build", "sources").escapedPathString())"]
872+
inputs: ["\(Pkg.appending(components: "Sources", "exe", "main.swift").escapedPathString())","\(swiftGetVersionFilePath.escapedPathString())","\(buildPath.appending(components: "PkgLib.swiftmodule").escapedPathString())","\(buildPath.appending(components: "exe.build", "sources").escapedPathString())"]
872873
"""))
873874

874875
}
@@ -896,8 +897,9 @@ final class BuildPlanTests: XCTestCase {
896897
try llbuild.generateManifest(at: yaml)
897898
let contents: String = try fs.readFileContents(yaml)
898899
let buildPath = plan.buildParameters.dataPath.appending(components: "debug")
900+
let swiftGetVersionFilePath = try XCTUnwrap(llbuild.swiftGetVersionFiles.first?.value)
899901
XCTAssertMatch(contents, .contains("""
900-
inputs: ["\(Pkg.appending(components: "Sources", "exe", "main.swift").escapedPathString())","\(buildPath.appending(components: "exe.build", "sources").escapedPathString())"]
902+
inputs: ["\(Pkg.appending(components: "Sources", "exe", "main.swift").escapedPathString())","\(swiftGetVersionFilePath.escapedPathString())","\(buildPath.appending(components: "exe.build", "sources").escapedPathString())"]
901903
"""))
902904
}
903905
}
@@ -3894,14 +3896,15 @@ final class BuildPlanTests: XCTestCase {
38943896
let llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope)
38953897
try llbuild.generateManifest(at: yaml)
38963898
let contents: String = try fs.readFileContents(yaml)
3899+
let swiftGetVersionFilePath = try XCTUnwrap(llbuild.swiftGetVersionFiles.first?.value)
38973900

38983901
#if os(Windows)
38993902
let suffix = ".exe"
39003903
#else // FIXME(5472) - the suffix is dropped
39013904
let suffix = ""
39023905
#endif
39033906
XCTAssertMatch(contents, .contains("""
3904-
inputs: ["\(PkgA.appending(components: "Sources", "swiftlib", "lib.swift").escapedPathString())","\(buildPath.appending(components: "exe\(suffix)").escapedPathString())","\(buildPath.appending(components: "swiftlib.build", "sources").escapedPathString())"]
3907+
inputs: ["\(PkgA.appending(components: "Sources", "swiftlib", "lib.swift").escapedPathString())","\(swiftGetVersionFilePath.escapedPathString())","\(buildPath.appending(components: "exe\(suffix)").escapedPathString())","\(buildPath.appending(components: "swiftlib.build", "sources").escapedPathString())"]
39053908
outputs: ["\(buildPath.appending(components: "swiftlib.build", "lib.swift.o").escapedPathString())","\(buildPath.escapedPathString())
39063909
"""))
39073910
}
@@ -4804,10 +4807,11 @@ final class BuildPlanTests: XCTestCase {
48044807
let yaml = buildPath.appending("release.yaml")
48054808
let llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope)
48064809
try llbuild.generateManifest(at: yaml)
4810+
let swiftGetVersionFilePath = try XCTUnwrap(llbuild.swiftGetVersionFiles.first?.value)
48074811

48084812
let yamlContents: String = try fs.readFileContents(yaml)
48094813
let inputs: SerializedJSON = """
4810-
inputs: ["\(AbsolutePath("/Pkg/Snippets/ASnippet.swift"))","\(AbsolutePath("/Pkg/.build/debug/Lib.swiftmodule"))"
4814+
inputs: ["\(AbsolutePath("/Pkg/Snippets/ASnippet.swift"))","\(swiftGetVersionFilePath.escapedPathString())","\(AbsolutePath("/Pkg/.build/debug/Lib.swiftmodule"))"
48114815
"""
48124816
XCTAssertMatch(yamlContents, .contains(inputs.underlying))
48134817
}

0 commit comments

Comments
 (0)