Skip to content

Commit a3541b8

Browse files
kateinoigakukunxedin
authored andcommitted
Place build.db under the scratch directory (#7471)
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: ``` $ swift build --experimental-swift-sdk wasm32-unknown-wasi $ swift build $ swift build --experimental-swift-sdk wasm32-unknown-wasi $ swift build $ swift build --experimental-swift-sdk wasm32-unknown-wasi ``` 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. `build.db` will be shared across product target triples, and SwiftPM will keep consistent cache state when switching triples. (cherry picked from commit e9399c2)
1 parent a9c04c4 commit a3541b8

File tree

7 files changed

+158
-37
lines changed

7 files changed

+158
-37
lines changed

Sources/Build/BuildManifest/LLBuildManifestBuilder.swift

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -317,33 +317,34 @@ extension TargetBuildDescription {
317317
}
318318

319319
extension ResolvedModule {
320-
package func getCommandName(buildParameters: BuildParameters) -> String {
320+
public func getCommandName(buildParameters: BuildParameters) -> String {
321321
"C." + self.getLLBuildTargetName(buildParameters: buildParameters)
322322
}
323323

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

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

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

Tests/BuildTests/BuildPlanTests.swift

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

52275227
let contents: String = try fs.readFileContents(yaml)
5228+
let triple = result.plan.destinationBuildParameters.triple.tripleString
52285229

52295230
if result.plan.destinationBuildParameters.triple.isWindows() {
52305231
XCTAssertMatch(
52315232
contents,
52325233
.contains("""
5233-
"C.rary-debug.a":
5234+
"C.rary-\(triple)-debug.a":
52345235
tool: shell
52355236
inputs: ["\(
52365237
buildPath.appending(components: "rary.build", "rary.swift.o")
@@ -5261,7 +5262,7 @@ final class BuildPlanTests: XCTestCase {
52615262
contents,
52625263
.contains(
52635264
"""
5264-
"C.rary-debug.a":
5265+
"C.rary-\(triple)-debug.a":
52655266
tool: shell
52665267
inputs: ["\(
52675268
buildPath.appending(components: "rary.build", "rary.swift.o")
@@ -5289,7 +5290,7 @@ final class BuildPlanTests: XCTestCase {
52895290
contents,
52905291
.contains(
52915292
"""
5292-
"C.rary-debug.a":
5293+
"C.rary-\(triple)-debug.a":
52935294
tool: shell
52945295
inputs: ["\(
52955296
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
@@ -72,8 +72,8 @@ final class LLBuildManifestBuilderTests: XCTestCase {
7272

7373
var basicReleaseCommandNames = [
7474
AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString,
75-
"<exe-release.exe>",
76-
"C.exe-release.exe",
75+
"<exe-\(buildParameters.triple)-release.exe>",
76+
"C.exe-\(buildParameters.triple)-release.exe",
7777
]
7878

7979
XCTAssertEqual(
@@ -101,11 +101,11 @@ final class LLBuildManifestBuilderTests: XCTestCase {
101101
llbuild = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: observability.topScope)
102102
try llbuild.createProductCommand(buildProduct)
103103

104-
let entitlementsCommandName = "C.exe-debug.exe-entitlements"
104+
let entitlementsCommandName = "C.exe-\(buildParameters.triple)-debug.exe-entitlements"
105105
var basicDebugCommandNames = [
106106
AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString,
107-
"<exe-debug.exe>",
108-
"C.exe-debug.exe",
107+
"<exe-\(buildParameters.triple)-debug.exe>",
108+
"C.exe-\(buildParameters.triple)-debug.exe",
109109
]
110110

111111
XCTAssertEqual(
@@ -131,7 +131,7 @@ final class LLBuildManifestBuilderTests: XCTestCase {
131131
XCTAssertEqual(
132132
entitlementsCommand.outputs,
133133
[
134-
.virtual("exe-debug.exe-CodeSigning"),
134+
.virtual("exe-\(buildParameters.triple)-debug.exe-CodeSigning"),
135135
]
136136
)
137137

@@ -157,8 +157,8 @@ final class LLBuildManifestBuilderTests: XCTestCase {
157157

158158
basicReleaseCommandNames = [
159159
AbsolutePath("/path/to/build/\(buildParameters.triple)/release/exe.product/Objects.LinkFileList").pathString,
160-
"<exe-release.exe>",
161-
"C.exe-release.exe",
160+
"<exe-\(buildParameters.triple)-release.exe>",
161+
"C.exe-\(buildParameters.triple)-release.exe",
162162
]
163163

164164
XCTAssertEqual(
@@ -188,8 +188,8 @@ final class LLBuildManifestBuilderTests: XCTestCase {
188188

189189
basicDebugCommandNames = [
190190
AbsolutePath("/path/to/build/\(buildParameters.triple)/debug/exe.product/Objects.LinkFileList").pathString,
191-
"<exe-debug.exe>",
192-
"C.exe-debug.exe",
191+
"<exe-\(buildParameters.triple)-debug.exe>",
192+
"C.exe-\(buildParameters.triple)-debug.exe",
193193
]
194194

195195
XCTAssertEqual(
@@ -215,6 +215,6 @@ final class LLBuildManifestBuilderTests: XCTestCase {
215215
let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope)
216216
let manifest = try builder.generateManifest(at: "/manifest")
217217

218-
XCTAssertNotNil(manifest.commands["C.SwiftSyntax-debug-tool.module"])
218+
XCTAssertNotNil(manifest.commands["C.SwiftSyntax-aarch64-unknown-linux-gnu-debug-tool.module"])
219219
}
220220
}

0 commit comments

Comments
 (0)