Skip to content

Commit 010745d

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 010745d

File tree

6 files changed

+131
-17
lines changed

6 files changed

+131
-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: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,59 @@
1414
@testable import PackageModel
1515

1616
import Basics
17+
import LLBuildManifest
18+
@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly)
19+
import PackageGraph
1720
import SPMTestSupport
1821
import XCTest
1922

2023
import class TSCBasic.BufferedOutputByteStream
2124
import class TSCBasic.InMemoryFileSystem
2225

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

2761
let fs = InMemoryFileSystem(files: [
2862
"\(buildParameters.dataPath)/debug/Lunch.build/Lunch.d" : "/Best.framework"
2963
])
3064

3165
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
66+
let buildOp = mockBuildOperation(
67+
buildParameters: buildParameters,
68+
scratchDirectory: scratchDirectory,
69+
fs: fs, observabilityScope: observability.topScope
4570
)
4671
buildOp.detectUnexpressedDependencies(
4772
availableLibraries: [
@@ -65,4 +90,85 @@ final class BuildOperationTests: XCTestCase {
6590
["target 'Lunch' has an unexpressed depedency on 'foo'"]
6691
)
6792
}
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+
buildParameters: buildParameters,
127+
cacheBuildManifest: false,
128+
packageGraphLoader: { packageGraph },
129+
scratchDirectory: scratchDirectory,
130+
fs: fs, observabilityScope: observability.topScope
131+
)
132+
// Generate initial llbuild manifest
133+
let _ = try buildOp.getBuildDescription()
134+
// Record the initial llbuild manifest as expected one
135+
llbuildManifestByTriple[triple.tripleString] = try fs.readFileContents(buildParameters.llbuildManifest)
136+
}
137+
138+
XCTAssertTrue(fs.exists(scratchDirectory.appending("debug.yaml")))
139+
// FIXME: There should be a build database with manifest cache after the initial build.
140+
// The initial build usually triggered with `cacheBuildManifest=false` because llbuild
141+
// manifest file and description.json are not found. However, with `cacheBuildManifest=false`,
142+
// `BuildOperation` does not trigger "PackageStructure" build, thus the initial build does
143+
// not record the manifest cache. So "getBuildDescription" doesn't create build.db for the
144+
// initial planning and the second build always need full-planning.
145+
//
146+
// XCTAssertTrue(fs.exists(scratchDirectory.appending("build.db")))
147+
148+
// Perform incremental build several times and switch triple for each time
149+
for _ in 0..<4 {
150+
for triple in triples {
151+
let buildParameters = mockBuildParameters(
152+
buildPath: scratchDirectory.appending(triple.tripleString),
153+
config: .debug,
154+
triple: triple
155+
)
156+
let buildOp = mockBuildOperation(
157+
buildParameters: buildParameters,
158+
cacheBuildManifest: true,
159+
packageGraphLoader: { packageGraph },
160+
scratchDirectory: scratchDirectory,
161+
fs: fs, observabilityScope: observability.topScope
162+
)
163+
// Generate llbuild manifest
164+
let _ = try buildOp.getBuildDescription()
165+
166+
// Ensure that llbuild manifest is updated to the expected one
167+
let actualManifest: String = try fs.readFileContents(buildParameters.llbuildManifest)
168+
let expectedManifest = try XCTUnwrap(llbuildManifestByTriple[triple.tripleString])
169+
XCTAssertEqual(actualManifest, expectedManifest)
170+
}
171+
}
172+
}
173+
}
68174
}

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)