Skip to content

Commit 2ec6fb1

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 b556563 commit 2ec6fb1

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
package 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
package func getLLBuildResourcesCmdName(config: String) -> String {

Sources/Build/BuildOperation.swift

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

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

@@ -113,6 +116,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build
113116
cacheBuildManifest: Bool,
114117
packageGraphLoader: @escaping () throws -> ModulesGraph,
115118
pluginConfiguration: PluginConfiguration? = .none,
119+
scratchDirectory: AbsolutePath,
116120
additionalFileRules: [FileRuleDescription],
117121
pkgConfigDirectories: [AbsolutePath],
118122
dependenciesByRootPackageIdentity: [PackageIdentity: [PackageIdentity]],
@@ -135,6 +139,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build
135139
self.packageGraphLoader = packageGraphLoader
136140
self.additionalFileRules = additionalFileRules
137141
self.pluginConfiguration = pluginConfiguration
142+
self.scratchDirectory = scratchDirectory
138143
self.pkgConfigDirectories = pkgConfigDirectories
139144
self.dependenciesByRootPackageIdentity = dependenciesByRootPackageIdentity
140145
self.rootPackageIdentityByTargetName = (try? Dictionary<String, PackageIdentity>(throwingUniqueKeysWithValues: targetsByRootPackageIdentity.lazy.flatMap { e in e.value.map { ($0, e.key) } })) ?? [:]
@@ -553,6 +558,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build
553558
toolsBuildParameters: pluginsBuildParameters,
554559
cacheBuildManifest: false,
555560
packageGraphLoader: { buildToolsGraph },
561+
scratchDirectory: pluginsBuildParameters.dataPath,
556562
additionalFileRules: self.additionalFileRules,
557563
pkgConfigDirectories: self.pkgConfigDirectories,
558564
dependenciesByRootPackageIdentity: [:],
@@ -723,7 +729,7 @@ package final class BuildOperation: PackageStructureDelegate, SPMBuildCore.Build
723729
)
724730
self.buildSystemDelegate = buildSystemDelegate
725731

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

Sources/CoreCommands/BuildSystemSupport.swift

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

Sources/swift-bootstrap/main.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ struct SwiftBootstrapBuildTool: ParsableCommand {
322322
toolsBuildParameters: buildParameters,
323323
cacheBuildManifest: false,
324324
packageGraphLoader: packageGraphLoader,
325+
scratchDirectory: scratchDirectory,
325326
additionalFileRules: [],
326327
pkgConfigDirectories: [],
327328
dependenciesByRootPackageIdentity: [:],

Tests/BuildTests/BuildOperationTests.swift

Lines changed: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import Build
1717
import PackageModel
1818

1919
import Basics
20+
import LLBuildManifest
21+
@_spi(DontAdoptOutsideOfSwiftPMExposedForBenchmarksAndTestsOnly)
22+
import PackageGraph
2023
import SPMTestSupport
2124

2225
import SPMBuildCore
@@ -26,28 +29,50 @@ import XCTest
2629
import class TSCBasic.BufferedOutputByteStream
2730
import class TSCBasic.InMemoryFileSystem
2831

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

3367
let fs = InMemoryFileSystem(files: [
3468
"\(buildParameters.dataPath)/debug/Lunch.build/Lunch.d" : "/Best.framework"
3569
])
3670

3771
let observability = ObservabilitySystem.makeForTesting()
38-
let buildOp = BuildOperation(
39-
productsBuildParameters: buildParameters,
40-
toolsBuildParameters: buildParameters,
41-
cacheBuildManifest: false,
42-
packageGraphLoader: { fatalError() },
43-
additionalFileRules: [],
44-
pkgConfigDirectories: [],
45-
dependenciesByRootPackageIdentity: [:],
46-
targetsByRootPackageIdentity: [:],
47-
outputStream: BufferedOutputByteStream(),
48-
logLevel: .info,
49-
fileSystem: fs,
50-
observabilityScope: observability.topScope
72+
let buildOp = mockBuildOperation(
73+
buildParameters: buildParameters,
74+
scratchDirectory: scratchDirectory,
75+
fs: fs, observabilityScope: observability.topScope
5176
)
5277
buildOp.detectUnexpressedDependencies(
5378
availableLibraries: [
@@ -68,4 +93,85 @@ final class BuildOperationTests: XCTestCase {
6893
["target 'Lunch' has an unexpressed depedency on 'foo'"]
6994
)
7095
}
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+
buildParameters: buildParameters,
130+
cacheBuildManifest: false,
131+
packageGraphLoader: { packageGraph },
132+
scratchDirectory: scratchDirectory,
133+
fs: fs, observabilityScope: observability.topScope
134+
)
135+
// Generate initial llbuild manifest
136+
let _ = try buildOp.getBuildDescription()
137+
// Record the initial llbuild manifest as expected one
138+
llbuildManifestByTriple[triple.tripleString] = try fs.readFileContents(buildParameters.llbuildManifest)
139+
}
140+
141+
XCTAssertTrue(fs.exists(scratchDirectory.appending("debug.yaml")))
142+
// FIXME: There should be a build database with manifest cache after the initial build.
143+
// The initial build usually triggered with `cacheBuildManifest=false` because llbuild
144+
// manifest file and description.json are not found. However, with `cacheBuildManifest=false`,
145+
// `BuildOperation` does not trigger "PackageStructure" build, thus the initial build does
146+
// not record the manifest cache. So "getBuildDescription" doesn't create build.db for the
147+
// initial planning and the second build always need full-planning.
148+
//
149+
// XCTAssertTrue(fs.exists(scratchDirectory.appending("build.db")))
150+
151+
// Perform incremental build several times and switch triple for each time
152+
for _ in 0..<4 {
153+
for triple in triples {
154+
let buildParameters = mockBuildParameters(
155+
buildPath: scratchDirectory.appending(triple.tripleString),
156+
config: .debug,
157+
triple: triple
158+
)
159+
let buildOp = mockBuildOperation(
160+
buildParameters: buildParameters,
161+
cacheBuildManifest: true,
162+
packageGraphLoader: { packageGraph },
163+
scratchDirectory: scratchDirectory,
164+
fs: fs, observabilityScope: observability.topScope
165+
)
166+
// Generate llbuild manifest
167+
let _ = try buildOp.getBuildDescription()
168+
169+
// Ensure that llbuild manifest is updated to the expected one
170+
let actualManifest: String = try fs.readFileContents(buildParameters.llbuildManifest)
171+
let expectedManifest = try XCTUnwrap(llbuildManifestByTriple[triple.tripleString])
172+
XCTAssertEqual(actualManifest, expectedManifest)
173+
}
174+
}
175+
}
176+
}
71177
}

Tests/BuildTests/LLBuildManifestBuilderTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,6 @@ final class LLBuildManifestBuilderTests: XCTestCase {
217217
let builder = LLBuildManifestBuilder(plan, fileSystem: fs, observabilityScope: scope)
218218
let manifest = try builder.generateManifest(at: "/manifest")
219219

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

0 commit comments

Comments
 (0)