Skip to content

Commit e9399c2

Browse files
Place build.db under the scratch directory (#7471)
### Motivation: The build database should be shared between different triple builds to re-generate the build manifest correctly. With the current implementation, the following build command sequence fails: ``` # 1st iteration: They both do not trigger "PackageStructure" build $ swift build --experimental-swift-sdk wasm32-unknown-wasi $ swift build # 2nd iteration: They both trigger "PackageStructure" build because there is description.json and debug.yaml $ swift build --experimental-swift-sdk wasm32-unknown-wasi $ swift build # 3rd iteration: Manifest cache entry in .build/wasm32-unknown-wasi/build.db created by 2nd iteration hits, # so do not update debug.yaml. <--- Incorrect $ swift build --experimental-swift-sdk wasm32-unknown-wasi ``` ### Modifications: This changes the llbuild build database to be placed under `.build/build.db` instead of `.build/<product triple>/build.db`. Also this change splits llbuild target names for each triple to avoid cache invalidation when switching triple. ### Result: `build.db` will be shared across product target triples, and SwiftPM will keep consistent cache state when switching triples.
1 parent 4dc41b0 commit e9399c2

File tree

7 files changed

+154
-33
lines changed

7 files changed

+154
-33
lines changed

Sources/Build/BuildManifest/LLBuildManifestBuilder.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -322,28 +322,29 @@ extension ResolvedModule {
322322
}
323323

324324
public func getLLBuildTargetName(buildParameters: BuildParameters) -> String {
325-
"\(self.name)-\(buildParameters.buildConfig)\(buildParameters.suffix(triple: self.buildTriple)).module"
325+
"\(self.name)-\(buildParameters.triple.tripleString)-\(buildParameters.buildConfig)\(buildParameters.suffix(triple: self.buildTriple)).module"
326326
}
327327

328328
public func getLLBuildResourcesCmdName(buildParameters: BuildParameters) -> String {
329-
"\(self.name)-\(buildParameters.buildConfig)\(buildParameters.suffix(triple: self.buildTriple)).module-resources"
329+
"\(self.name)-\(buildParameters.triple.tripleString)-\(buildParameters.buildConfig)\(buildParameters.suffix(triple: self.buildTriple)).module-resources"
330330
}
331331
}
332332

333333
extension ResolvedProduct {
334334
public func getLLBuildTargetName(buildParameters: BuildParameters) throws -> String {
335+
let triple = buildParameters.triple.tripleString
335336
let config = buildParameters.buildConfig
336337
let suffix = buildParameters.suffix(triple: self.buildTriple)
337-
let potentialExecutableTargetName = "\(name)-\(config)\(suffix).exe"
338-
let potentialLibraryTargetName = "\(name)-\(config)\(suffix).dylib"
338+
let potentialExecutableTargetName = "\(name)-\(triple)-\(config)\(suffix).exe"
339+
let potentialLibraryTargetName = "\(name)-\(triple)-\(config)\(suffix).dylib"
339340

340341
switch type {
341342
case .library(.dynamic):
342343
return potentialLibraryTargetName
343344
case .test:
344-
return "\(name)-\(config)\(suffix).test"
345+
return "\(name)-\(triple)-\(config)\(suffix).test"
345346
case .library(.static):
346-
return "\(name)-\(config)\(suffix).a"
347+
return "\(name)-\(triple)-\(config)\(suffix).a"
347348
case .library(.automatic):
348349
throw InternalError("automatic library not supported")
349350
case .executable, .snippet:

Sources/Build/BuildOperation.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
5252
/// the plugin configuration for build plugins
5353
let pluginConfiguration: PluginConfiguration?
5454

55+
/// The path to scratch space (.build) directory.
56+
let scratchDirectory: AbsolutePath
57+
5558
/// The llbuild build delegate reference.
5659
private var buildSystemDelegate: BuildOperationBuildSystemDelegateHandler?
5760

@@ -114,6 +117,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
114117
cacheBuildManifest: Bool,
115118
packageGraphLoader: @escaping () throws -> ModulesGraph,
116119
pluginConfiguration: PluginConfiguration? = .none,
120+
scratchDirectory: AbsolutePath,
117121
additionalFileRules: [FileRuleDescription],
118122
pkgConfigDirectories: [AbsolutePath],
119123
dependenciesByRootPackageIdentity: [PackageIdentity: [PackageIdentity]],
@@ -136,6 +140,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
136140
self.packageGraphLoader = packageGraphLoader
137141
self.additionalFileRules = additionalFileRules
138142
self.pluginConfiguration = pluginConfiguration
143+
self.scratchDirectory = scratchDirectory
139144
self.pkgConfigDirectories = pkgConfigDirectories
140145
self.dependenciesByRootPackageIdentity = dependenciesByRootPackageIdentity
141146
self.rootPackageIdentityByTargetName = (try? Dictionary<String, PackageIdentity>(throwingUniqueKeysWithValues: targetsByRootPackageIdentity.lazy.flatMap { e in e.value.map { ($0, e.key) } })) ?? [:]
@@ -554,6 +559,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
554559
toolsBuildParameters: pluginsBuildParameters,
555560
cacheBuildManifest: false,
556561
packageGraphLoader: { buildToolsGraph },
562+
scratchDirectory: pluginsBuildParameters.dataPath,
557563
additionalFileRules: self.additionalFileRules,
558564
pkgConfigDirectories: self.pkgConfigDirectories,
559565
dependenciesByRootPackageIdentity: [:],
@@ -724,7 +730,7 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
724730
)
725731
self.buildSystemDelegate = buildSystemDelegate
726732

727-
let databasePath = self.productsBuildParameters.dataPath.appending("build.db").pathString
733+
let databasePath = self.scratchDirectory.appending("build.db").pathString
728734
let buildSystem = SPMLLBuild.BuildSystem(
729735
buildFile: self.productsBuildParameters.llbuildManifest.pathString,
730736
databaseFile: databasePath,

Sources/CoreCommands/BuildSystemSupport.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ private struct NativeBuildSystemFactory: BuildSystemFactory {
4949
workDirectory: try self.swiftCommandState.getActiveWorkspace().location.pluginWorkingDirectory,
5050
disableSandbox: self.swiftCommandState.shouldDisableSandbox
5151
),
52+
scratchDirectory: self.swiftCommandState.scratchDirectory,
5253
additionalFileRules: FileRuleDescription.swiftpmFileTypes,
5354
pkgConfigDirectories: self.swiftCommandState.options.locations.pkgConfigDirectories,
5455
dependenciesByRootPackageIdentity: rootPackageInfo.dependencies,

Sources/swift-bootstrap/main.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand {
319319
toolsBuildParameters: buildParameters,
320320
cacheBuildManifest: false,
321321
packageGraphLoader: packageGraphLoader,
322+
scratchDirectory: scratchDirectory,
322323
additionalFileRules: [],
323324
pkgConfigDirectories: [],
324325
dependenciesByRootPackageIdentity: [:],

Tests/BuildTests/BuildOperationTests.swift

Lines changed: 123 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,62 @@
1414
@testable import PackageModel
1515

1616
import Basics
17+
import LLBuildManifest
18+
@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly)
19+
import PackageGraph
20+
import SPMBuildCore
1721
import SPMTestSupport
1822
import XCTest
1923

2024
import class TSCBasic.BufferedOutputByteStream
2125
import class TSCBasic.InMemoryFileSystem
2226

27+
private func mockBuildOperation(
28+
productsBuildParameters: BuildParameters,
29+
toolsBuildParameters: BuildParameters,
30+
cacheBuildManifest: Bool = false,
31+
packageGraphLoader: @escaping () -> ModulesGraph = { fatalError() },
32+
scratchDirectory: AbsolutePath,
33+
fs: any Basics.FileSystem,
34+
observabilityScope: ObservabilityScope
35+
) -> BuildOperation {
36+
return BuildOperation(
37+
productsBuildParameters: productsBuildParameters,
38+
toolsBuildParameters: toolsBuildParameters,
39+
cacheBuildManifest: cacheBuildManifest,
40+
packageGraphLoader: packageGraphLoader,
41+
scratchDirectory: scratchDirectory,
42+
additionalFileRules: [],
43+
pkgConfigDirectories: [],
44+
dependenciesByRootPackageIdentity: [:],
45+
targetsByRootPackageIdentity: [:],
46+
outputStream: BufferedOutputByteStream(),
47+
logLevel: .info,
48+
fileSystem: fs,
49+
observabilityScope: observabilityScope
50+
)
51+
}
52+
2353
final class BuildOperationTests: XCTestCase {
2454
func testDetectUnexpressedDependencies() throws {
25-
let buildParameters = mockBuildParameters(shouldDisableLocalRpath: false)
55+
let scratchDirectory = AbsolutePath("/path/to/build")
56+
let triple = hostTriple
57+
let buildParameters = mockBuildParameters(
58+
buildPath: scratchDirectory.appending(triple.tripleString),
59+
shouldDisableLocalRpath: false,
60+
triple: triple
61+
)
2662

2763
let fs = InMemoryFileSystem(files: [
2864
"\(buildParameters.dataPath)/debug/Lunch.build/Lunch.d" : "/Best.framework"
2965
])
3066

3167
let observability = ObservabilitySystem.makeForTesting()
32-
let buildOp = BuildOperation(
68+
let buildOp = mockBuildOperation(
3369
productsBuildParameters: buildParameters,
3470
toolsBuildParameters: buildParameters,
35-
cacheBuildManifest: false,
36-
packageGraphLoader: { fatalError() },
37-
additionalFileRules: [],
38-
pkgConfigDirectories: [],
39-
dependenciesByRootPackageIdentity: [:],
40-
targetsByRootPackageIdentity: [:],
41-
outputStream: BufferedOutputByteStream(),
42-
logLevel: .info,
43-
fileSystem: fs,
44-
observabilityScope: observability.topScope
71+
scratchDirectory: scratchDirectory,
72+
fs: fs, observabilityScope: observability.topScope
4573
)
4674
buildOp.detectUnexpressedDependencies(
4775
availableLibraries: [
@@ -65,4 +93,87 @@ final class BuildOperationTests: XCTestCase {
6593
["target 'Lunch' has an unexpressed depedency on 'foo'"]
6694
)
6795
}
96+
97+
func testDetectProductTripleChange() throws {
98+
let observability = ObservabilitySystem.makeForTesting()
99+
let fs = InMemoryFileSystem(
100+
emptyFiles: "/Pkg/Sources/ATarget/foo.swift"
101+
)
102+
let packageGraph = try loadModulesGraph(
103+
fileSystem: fs,
104+
manifests: [
105+
.createRootManifest(
106+
displayName: "SwitchTriple",
107+
path: "/Pkg",
108+
targets: [
109+
TargetDescription(name: "ATarget"),
110+
]
111+
),
112+
],
113+
observabilityScope: observability.topScope
114+
)
115+
try withTemporaryDirectory { tmpDir in
116+
let scratchDirectory = tmpDir.appending(".build")
117+
let fs = localFileSystem
118+
let triples = try [Triple("x86_64-unknown-linux-gnu"), Triple("wasm32-unknown-wasi")]
119+
var llbuildManifestByTriple: [String: String] = [:]
120+
121+
// Perform initial builds for each triple
122+
for triple in triples {
123+
let buildParameters = mockBuildParameters(
124+
buildPath: scratchDirectory.appending(triple.tripleString),
125+
config: .debug,
126+
triple: triple
127+
)
128+
let buildOp = mockBuildOperation(
129+
productsBuildParameters: buildParameters,
130+
toolsBuildParameters: buildParameters,
131+
cacheBuildManifest: false,
132+
packageGraphLoader: { packageGraph },
133+
scratchDirectory: scratchDirectory,
134+
fs: fs, observabilityScope: observability.topScope
135+
)
136+
// Generate initial llbuild manifest
137+
let _ = try buildOp.getBuildDescription()
138+
// Record the initial llbuild manifest as expected one
139+
llbuildManifestByTriple[triple.tripleString] = try fs.readFileContents(buildParameters.llbuildManifest)
140+
}
141+
142+
XCTAssertTrue(fs.exists(scratchDirectory.appending("debug.yaml")))
143+
// FIXME: There should be a build database with manifest cache after the initial build.
144+
// The initial build usually triggered with `cacheBuildManifest=false` because llbuild
145+
// manifest file and description.json are not found. However, with `cacheBuildManifest=false`,
146+
// `BuildOperation` does not trigger "PackageStructure" build, thus the initial build does
147+
// not record the manifest cache. So "getBuildDescription" doesn't create build.db for the
148+
// initial planning and the second build always need full-planning.
149+
//
150+
// XCTAssertTrue(fs.exists(scratchDirectory.appending("build.db")))
151+
152+
// Perform incremental build several times and switch triple for each time
153+
for _ in 0..<4 {
154+
for triple in triples {
155+
let buildParameters = mockBuildParameters(
156+
buildPath: scratchDirectory.appending(triple.tripleString),
157+
config: .debug,
158+
triple: triple
159+
)
160+
let buildOp = mockBuildOperation(
161+
productsBuildParameters: buildParameters,
162+
toolsBuildParameters: buildParameters,
163+
cacheBuildManifest: true,
164+
packageGraphLoader: { packageGraph },
165+
scratchDirectory: scratchDirectory,
166+
fs: fs, observabilityScope: observability.topScope
167+
)
168+
// Generate llbuild manifest
169+
let _ = try buildOp.getBuildDescription()
170+
171+
// Ensure that llbuild manifest is updated to the expected one
172+
let actualManifest: String = try fs.readFileContents(buildParameters.llbuildManifest)
173+
let expectedManifest = try XCTUnwrap(llbuildManifestByTriple[triple.tripleString])
174+
XCTAssertEqual(actualManifest, expectedManifest)
175+
}
176+
}
177+
}
178+
}
68179
}

Tests/BuildTests/BuildPlanTests.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5227,12 +5227,13 @@ final class BuildPlanTests: XCTestCase {
52275227
try llbuild.generateManifest(at: yaml)
52285228

52295229
let contents: String = try fs.readFileContents(yaml)
5230+
let triple = result.plan.destinationBuildParameters.triple.tripleString
52305231

52315232
if result.plan.destinationBuildParameters.triple.isWindows() {
52325233
XCTAssertMatch(
52335234
contents,
52345235
.contains("""
5235-
"C.rary-debug.a":
5236+
"C.rary-\(triple)-debug.a":
52365237
tool: shell
52375238
inputs: ["\(
52385239
buildPath.appending(components: "rary.build", "rary.swift.o")
@@ -5263,7 +5264,7 @@ final class BuildPlanTests: XCTestCase {
52635264
contents,
52645265
.contains(
52655266
"""
5266-
"C.rary-debug.a":
5267+
"C.rary-\(triple)-debug.a":
52675268
tool: shell
52685269
inputs: ["\(
52695270
buildPath.appending(components: "rary.build", "rary.swift.o")
@@ -5291,7 +5292,7 @@ final class BuildPlanTests: XCTestCase {
52915292
contents,
52925293
.contains(
52935294
"""
5294-
"C.rary-debug.a":
5295+
"C.rary-\(triple)-debug.a":
52955296
tool: shell
52965297
inputs: ["\(
52975298
buildPath.appending(components: "rary.build", "rary.swift.o")

Tests/BuildTests/LLBuildManifestBuilderTests.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ final class LLBuildManifestBuilderTests: XCTestCase {
7575

7676
var basicReleaseCommandNames = [
7777
AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString,
78-
"<exe-release.exe>",
79-
"C.exe-release.exe",
78+
"<exe-\(buildParameters.triple)-release.exe>",
79+
"C.exe-\(buildParameters.triple)-release.exe",
8080
]
8181

8282
XCTAssertEqual(
@@ -104,11 +104,11 @@ final class LLBuildManifestBuilderTests: XCTestCase {
104104
llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope)
105105
try llbuild.createProductCommand(buildProduct)
106106

107-
let entitlementsCommandName = "C.exe-debug.exe-entitlements"
107+
let entitlementsCommandName = "C.exe-\(buildParameters.triple)-debug.exe-entitlements"
108108
var basicDebugCommandNames = [
109109
AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString,
110-
"<exe-debug.exe>",
111-
"C.exe-debug.exe",
110+
"<exe-\(buildParameters.triple)-debug.exe>",
111+
"C.exe-\(buildParameters.triple)-debug.exe",
112112
]
113113

114114
XCTAssertEqual(
@@ -134,7 +134,7 @@ final class LLBuildManifestBuilderTests: XCTestCase {
134134
XCTAssertEqual(
135135
entitlementsCommand.outputs,
136136
[
137-
.virtual("exe-debug.exe-CodeSigning"),
137+
.virtual("exe-\(buildParameters.triple)-debug.exe-CodeSigning"),
138138
]
139139
)
140140

@@ -160,8 +160,8 @@ final class LLBuildManifestBuilderTests: XCTestCase {
160160

161161
basicReleaseCommandNames = [
162162
AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString,
163-
"<exe-release.exe>",
164-
"C.exe-release.exe",
163+
"<exe-\(buildParameters.triple)-release.exe>",
164+
"C.exe-\(buildParameters.triple)-release.exe",
165165
]
166166

167167
XCTAssertEqual(
@@ -191,8 +191,8 @@ final class LLBuildManifestBuilderTests: XCTestCase {
191191

192192
basicDebugCommandNames = [
193193
AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString,
194-
"<exe-debug.exe>",
195-
"C.exe-debug.exe",
194+
"<exe-\(buildParameters.triple)-debug.exe>",
195+
"C.exe-\(buildParameters.triple)-debug.exe",
196196
]
197197

198198
XCTAssertEqual(
@@ -218,6 +218,6 @@ final class LLBuildManifestBuilderTests: XCTestCase {
218218
let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope)
219219
let manifest = try builder.generateManifest(at: "/manifest")
220220

221-
XCTAssertNotNil(manifest.commands["C.SwiftSyntax-debug-tool.module"])
221+
XCTAssertNotNil(manifest.commands["C.SwiftSyntax-aarch64-unknown-linux-gnu-debug-tool.module"])
222222
}
223223
}

0 commit comments

Comments
 (0)