Skip to content

Commit 2792f32

Browse files
committed
[Commands] Add Workspace support for loading current manifest tree.
- The versions in the manifests are not properly preserved yet...
1 parent a292dce commit 2792f32

File tree

3 files changed

+145
-5
lines changed

3 files changed

+145
-5
lines changed

Sources/Commands/Workspace.swift

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
*/
1010

1111
import Basic
12+
import PackageLoading
13+
import PackageModel
1214
import SourceControl
1315
import Utility
1416

@@ -80,7 +82,10 @@ public class Workspace {
8082

8183
/// The path for working repository clones (checkouts).
8284
let checkoutsPath: AbsolutePath
83-
85+
86+
/// The manifest loader to use.
87+
let manifestLoader: ManifestLoaderProtocol
88+
8489
/// The checkout manager.
8590
private let checkoutManager: CheckoutManager
8691

@@ -92,7 +97,7 @@ public class Workspace {
9297
return AnySequence<ManagedDependency>(dependencyMap.values)
9398
}
9499

95-
/// Create a new manager for the package at the given path.
100+
/// Create a new workspace for the package at the given path.
96101
///
97102
/// This will automatically load the persisted state for the package, if
98103
/// present. If the state isn't present then a default state will be
@@ -101,10 +106,17 @@ public class Workspace {
101106
/// - Parameters:
102107
/// - path: The path of the root package.
103108
/// - dataPath: The path for the workspace data files, if explicitly provided.
109+
/// - manifestLoader: The manifest loader.
104110
/// - Throws: If the state was present, but could not be loaded.
105-
public init(rootPackage path: AbsolutePath, dataPath: AbsolutePath? = nil) throws {
111+
public init(
112+
rootPackage path: AbsolutePath,
113+
dataPath: AbsolutePath? = nil,
114+
manifestLoader: ManifestLoaderProtocol
115+
) throws {
106116
self.rootPackagePath = path
107117
self.dataPath = dataPath ?? path.appending(component: ".build")
118+
self.manifestLoader = manifestLoader
119+
108120
let repositoriesPath = self.dataPath.appending(component: "repositories")
109121
self.checkoutManager = CheckoutManager(path: repositoriesPath, provider: GitRepositoryProvider())
110122
self.checkoutsPath = self.dataPath.appending(component: "checkouts")
@@ -195,7 +207,42 @@ public class Workspace {
195207

196208
return path
197209
}
198-
210+
211+
/// Load the manifests for the current dependency tree.
212+
///
213+
/// This will load the manifests for the root package as well as all the
214+
/// current dependencies from the working checkouts.
215+
///
216+
/// Throws: If the root manifest could not be loaded.
217+
func loadDependencyManifests() throws -> (root: Manifest, dependencies: [Manifest]) {
218+
// Load the root manifest.
219+
let rootManifest = try manifestLoader.load(packagePath: rootPackagePath, baseURL: rootPackagePath.asString, version: nil)
220+
221+
// Compute the transitive closure of available dependencies.
222+
let dependencies = transitiveClosure([KeyedPair(rootManifest, key: rootManifest.url)]) { node in
223+
return node.item.package.dependencies.flatMap{ dependency in
224+
// Check if this dependency is available.
225+
guard let managedDependency = dependencyMap[RepositorySpecifier(url: dependency.url)] else {
226+
return nil
227+
}
228+
229+
// If so, load its manifest.
230+
//
231+
// This should *never* fail, because we should only have ever
232+
// got this checkout via loading its manifest successfully.
233+
//
234+
// FIXME: Nevertheless, we should handle this failure explicitly.
235+
//
236+
// FIXME: We need to have the correct version to pass here.
237+
let manifest: Manifest = try! manifestLoader.load(packagePath: checkoutsPath.appending(managedDependency.subpath), baseURL: managedDependency.repository.url, version: nil)
238+
239+
return KeyedPair(manifest, key: manifest.url)
240+
}
241+
}
242+
243+
return (root: rootManifest, dependencies: dependencies.map{ $0.item })
244+
}
245+
199246
// MARK: Persistence
200247

201248
// FIXME: A lot of the persistence mechanism here is copied from

Sources/TestSupport/MockManifestLoader.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public struct MockManifestLoader: ManifestLoaderProtocol {
5555
version: PackageDescription.Version?,
5656
fileSystem: FileSystem?
5757
) throws -> PackageModel.Manifest {
58-
if let result = manifests[Key(url: baseURL, version: version!)] {
58+
if let result = manifests[Key(url: baseURL, version: version)] {
5959
return result
6060
}
6161
throw MockManifestLoaderError.unknownRequest

Tests/CommandsTests/WorkspaceTests.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,29 @@ import XCTest
1212

1313
import Basic
1414
import Commands
15+
import PackageDescription
16+
import PackageLoading
17+
import PackageModel
1518
import SourceControl
1619
import Utility
1720

21+
import struct TestSupport.MockManifestLoader
22+
1823
import TestSupport
1924

2025
@testable import class Commands.Workspace
2126

27+
private let sharedManifestLoader = ManifestLoader(resources: Resources())
28+
29+
extension Workspace {
30+
convenience init(rootPackage path: AbsolutePath) throws {
31+
try self.init(rootPackage: path, manifestLoader: sharedManifestLoader)
32+
}
33+
}
34+
35+
private let v1: Version = "1.0.0"
36+
private let v2: Version = "2.0.0"
37+
2238
final class WorkspaceTests: XCTestCase {
2339
func testBasics() throws {
2440
mktmpdir { path in
@@ -83,7 +99,84 @@ final class WorkspaceTests: XCTestCase {
8399
}
84100
}
85101

102+
func testDependencyManifestLoading() {
103+
// We mock up the following dep graph:
104+
//
105+
// Root
106+
// \ A: checked out (@v1)
107+
// \ AA: checked out (@v2)
108+
// \ B: missing
109+
110+
mktmpdir { path in
111+
// Create the test repositories, we don't need them to have actual
112+
// contents (the manifests are mocked).
113+
var repos: [String: RepositorySpecifier] = [:]
114+
for name in ["A", "AA"] {
115+
let repoPath = path.appending(component: name)
116+
try makeDirectories(repoPath)
117+
initGitRepo(repoPath, tag: "initial")
118+
repos[name] = RepositorySpecifier(url: repoPath.asString)
119+
}
120+
121+
// Create the mock manifests.
122+
let rootManifest = Manifest(
123+
path: AbsolutePath("/UNUSED"),
124+
url: path.asString,
125+
package: PackageDescription.Package(
126+
name: "Root",
127+
dependencies: [
128+
.Package(url: repos["A"]!.url, majorVersion: 1),
129+
.Package(url: "//B", majorVersion: 1)
130+
]),
131+
products: [],
132+
version: nil
133+
)
134+
let aManifest = Manifest(
135+
path: AbsolutePath("/UNUSED"),
136+
url: repos["A"]!.url,
137+
package: PackageDescription.Package(
138+
name: "A",
139+
dependencies: [
140+
.Package(url: repos["AA"]!.url, majorVersion: 2)
141+
]),
142+
products: [],
143+
version: v1
144+
)
145+
let aaManifest = Manifest(
146+
path: AbsolutePath("/UNUSED"),
147+
url: repos["AA"]!.url,
148+
package: PackageDescription.Package(
149+
name: "AA"),
150+
products: [],
151+
version: v2
152+
)
153+
let mockManifestLoader = MockManifestLoader(manifests: [
154+
MockManifestLoader.Key(url: path.asString, version: nil): rootManifest,
155+
// FIXME: These versions are wrong, we aren't preserving versions currently.
156+
MockManifestLoader.Key(url: repos["A"]!.url, version: nil): aManifest,
157+
MockManifestLoader.Key(url: repos["AA"]!.url, version: nil): aaManifest
158+
])
159+
160+
// Create the workspace.
161+
let workspace = try Workspace(rootPackage: path, manifestLoader: mockManifestLoader)
162+
163+
// Ensure we have checkouts for A & AA.
164+
for name in ["A", "AA"] {
165+
let revision = try GitRepository(path: AbsolutePath(repos[name]!.url)).getCurrentRevision()
166+
_ = try workspace.clone(repository: repos[name]!, at: revision)
167+
}
168+
169+
// Load the "current" manifests.
170+
let manifests = try workspace.loadDependencyManifests()
171+
XCTAssertEqual(manifests.root.package, rootManifest.package)
172+
XCTAssertEqual(manifests.dependencies.map{ $0.package.name }.sorted(), ["A", "AA"])
173+
174+
// FIXME: These manifests do not have the right versions in them, and they should.
175+
}
176+
}
177+
86178
static var allTests = [
87179
("testBasics", testBasics),
180+
("testDependencyManifestLoading", testDependencyManifestLoading),
88181
]
89182
}

0 commit comments

Comments
 (0)