Skip to content

Commit 6be1d40

Browse files
Place build.db under the scratch directory
The build database should be shared between different triple builds to re-generate the build manifest correctly. Also this change splits llbuild target names for each triple to avoid cache invalidation when switching triple.
1 parent ad7b5ce commit 6be1d40

File tree

6 files changed

+132
-17
lines changed

6 files changed

+132
-17
lines changed

Sources/Build/BuildManifest/LLBuildManifestBuilder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ 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 {

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: 121 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,60 @@
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+
buildParameters: BuildParameters,
29+
cacheBuildManifest: Bool = false,
30+
packageGraphLoader: @escaping () -> ModulesGraph = { fatalError() },
31+
scratchDirectory: AbsolutePath,
32+
fs: any Basics.FileSystem,
33+
observabilityScope: ObservabilityScope
34+
) -> BuildOperation {
35+
return BuildOperation(
36+
productsBuildParameters: buildParameters,
37+
toolsBuildParameters: buildParameters,
38+
cacheBuildManifest: cacheBuildManifest,
39+
packageGraphLoader: packageGraphLoader,
40+
scratchDirectory: scratchDirectory,
41+
additionalFileRules: [],
42+
pkgConfigDirectories: [],
43+
dependenciesByRootPackageIdentity: [:],
44+
targetsByRootPackageIdentity: [:],
45+
outputStream: BufferedOutputByteStream(),
46+
logLevel: .info,
47+
fileSystem: fs,
48+
observabilityScope: observabilityScope
49+
)
50+
}
51+
2352
final class BuildOperationTests: XCTestCase {
2453
func testDetectUnexpressedDependencies() throws {
25-
let buildParameters = mockBuildParameters(shouldDisableLocalRpath: false)
54+
let scratchDirectory = AbsolutePath("/path/to/build")
55+
let triple = hostTriple
56+
let buildParameters = mockBuildParameters(
57+
buildPath: scratchDirectory.appending(triple.tripleString),
58+
shouldDisableLocalRpath: false,
59+
triple: triple
60+
)
2661

2762
let fs = InMemoryFileSystem(files: [
2863
"\(buildParameters.dataPath)/debug/Lunch.build/Lunch.d" : "/Best.framework"
2964
])
3065

3166
let observability = ObservabilitySystem.makeForTesting()
32-
let buildOp = BuildOperation(
33-
productsBuildParameters: buildParameters,
34-
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
67+
let buildOp = mockBuildOperation(
68+
buildParameters: buildParameters,
69+
scratchDirectory: scratchDirectory,
70+
fs: fs, observabilityScope: observability.topScope
4571
)
4672
buildOp.detectUnexpressedDependencies(
4773
availableLibraries: [
@@ -65,4 +91,85 @@ final class BuildOperationTests: XCTestCase {
6591
["target 'Lunch' has an unexpressed depedency on 'foo'"]
6692
)
6793
}
94+
95+
func testDetectProductTripleChange() throws {
96+
let observability = ObservabilitySystem.makeForTesting()
97+
let fs = InMemoryFileSystem(
98+
emptyFiles: "/Pkg/Sources/ATarget/foo.swift"
99+
)
100+
let packageGraph = try loadModulesGraph(
101+
fileSystem: fs,
102+
manifests: [
103+
.createRootManifest(
104+
displayName: "SwitchTriple",
105+
path: "/Pkg",
106+
targets: [
107+
TargetDescription(name: "ATarget"),
108+
]
109+
),
110+
],
111+
observabilityScope: observability.topScope
112+
)
113+
try withTemporaryDirectory { tmpDir in
114+
let scratchDirectory = tmpDir.appending(".build")
115+
let fs = localFileSystem
116+
let triples = try [Triple("x86_64-unknown-linux-gnu"), Triple("wasm32-unknown-wasi")]
117+
var llbuildManifestByTriple: [String: String] = [:]
118+
119+
// Perform initial builds for each triple
120+
for triple in triples {
121+
let buildParameters = mockBuildParameters(
122+
buildPath: scratchDirectory.appending(triple.tripleString),
123+
config: .debug,
124+
triple: triple
125+
)
126+
let buildOp = mockBuildOperation(
127+
buildParameters: 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+
buildParameters: buildParameters,
159+
cacheBuildManifest: true,
160+
packageGraphLoader: { packageGraph },
161+
scratchDirectory: scratchDirectory,
162+
fs: fs, observabilityScope: observability.topScope
163+
)
164+
// Generate llbuild manifest
165+
let _ = try buildOp.getBuildDescription()
166+
167+
// Ensure that llbuild manifest is updated to the expected one
168+
let actualManifest: String = try fs.readFileContents(buildParameters.llbuildManifest)
169+
let expectedManifest = try XCTUnwrap(llbuildManifestByTriple[triple.tripleString])
170+
XCTAssertEqual(actualManifest, expectedManifest)
171+
}
172+
}
173+
}
174+
}
68175
}

Tests/BuildTests/LLBuildManifestBuilderTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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)