Skip to content

Commit bbac385

Browse files
authored
Merge pull request #1170 from aciidb0mb3r/unedit-resolve
[Workspace] Resolve post unediting
2 parents ad06527 + c54f8d9 commit bbac385

File tree

3 files changed

+194
-70
lines changed

3 files changed

+194
-70
lines changed

Sources/Commands/SwiftPackageTool.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,12 @@ public class SwiftPackageTool: SwiftTool<PackageToolOptions> {
123123
try resolve()
124124
let workspace = try getActiveWorkspace()
125125

126-
try workspace.unedit(packageName: packageName, forceRemove: options.editOptions.shouldForceRemove)
126+
try workspace.unedit(
127+
packageName: packageName,
128+
forceRemove: options.editOptions.shouldForceRemove,
129+
root: getWorkspaceRoot(),
130+
diagnostics: diagnostics
131+
)
127132

128133
case .showDependencies:
129134
let graph = try loadPackageGraph()

Sources/Workspace/Workspace.swift

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -323,17 +323,26 @@ extension Workspace {
323323
}
324324
}
325325

326-
/// Ends the edit mode of a dependency which is in edit mode.
326+
/// Ends the edit mode of an edited dependency.
327+
///
328+
/// This will re-resolve the dependencies after ending edit as the original
329+
/// checkout may be outdated.
327330
///
328331
/// - Parameters:
329332
/// - packageName: The name of the package to edit.
330-
/// - forceRemove: If true, the dependency will be unedited even if has
331-
/// unpushed and uncommited changes. Otherwise will throw respective errors.
332-
///
333-
/// - throws: WorkspaceError
334-
public func unedit(packageName: String, forceRemove: Bool) throws {
333+
/// - forceRemove: If true, the dependency will be unedited even if has unpushed
334+
/// or uncommited changes. Otherwise will throw respective errors.
335+
/// - root: The workspace root. This is used to resolve the dependencies post unediting.
336+
/// - diagnostics: The diagnostics engine that reports errors, warnings
337+
/// and notes.
338+
public func unedit(
339+
packageName: String,
340+
forceRemove: Bool,
341+
root: WorkspaceRoot,
342+
diagnostics: DiagnosticsEngine
343+
) throws {
335344
let dependency = try managedDependencies.dependency(forName: packageName)
336-
try unedit(dependency: dependency, forceRemove: forceRemove)
345+
try unedit(dependency: dependency, forceRemove: forceRemove, root: root, diagnostics: diagnostics)
337346
}
338347

339348
/// Resolve a package at the given state.
@@ -367,27 +376,22 @@ extension Workspace {
367376
return diagnostics.emit(error)
368377
}
369378

370-
// Compute the checkout state.
371-
//
372-
// We use a dummy revision in case of version and branch because we
373-
// might not know the needed revision at this point.
374-
let checkoutState: CheckoutState
379+
// Compute the custom or extra constraint we need to impose.
380+
let requirement: RepositoryPackageConstraint.Requirement
375381
if let version = version {
376-
checkoutState = CheckoutState(revision: Revision(identifier: ""), version: version)
382+
requirement = .versionSet(.exact(version))
377383
} else if let branch = branch {
378-
checkoutState = CheckoutState(revision: Revision(identifier: ""), branch: branch)
384+
requirement = .revision(branch)
379385
} else if let revision = revision {
380-
checkoutState = CheckoutState(revision: Revision(identifier: revision))
386+
requirement = .revision(revision)
381387
} else {
382-
checkoutState = currentState
388+
requirement = currentState.requirement()
383389
}
384-
385-
// Create a pin with above checkout state.
386-
let pin = PinsStore.Pin(
387-
package: dependency.name, repository: dependency.repository, state: checkoutState)
390+
let constraint = RepositoryPackageConstraint(
391+
container: dependency.repository, requirement: requirement)
388392

389393
// Run the resolution.
390-
_resolve(root: root, extraPins: [pin], diagnostics: diagnostics)
394+
_resolve(root: root, extraConstraints: [constraint], diagnostics: diagnostics)
391395
}
392396

393397
/// Cleans the build artefacts from workspace data.
@@ -666,7 +670,12 @@ extension Workspace {
666670
}
667671

668672
/// Unedit a managed dependency. See public API unedit(packageName:forceRemove:).
669-
fileprivate func unedit(dependency: ManagedDependency, forceRemove: Bool) throws {
673+
fileprivate func unedit(
674+
dependency: ManagedDependency,
675+
forceRemove: Bool,
676+
root: WorkspaceRoot? = nil,
677+
diagnostics: DiagnosticsEngine
678+
) throws {
670679

671680
// Compute if we need to force remove.
672681
var forceRemove = forceRemove
@@ -709,6 +718,12 @@ extension Workspace {
709718
managedDependencies[dependency.repository] = dependency.basedOn
710719
// Save the state.
711720
try managedDependencies.saveState()
721+
722+
// Resolve the dependencies if workspace root is provided. We do this to
723+
// ensure the unedited version of this dependency is resolved properly.
724+
if let root = root {
725+
resolve(root: root, diagnostics: diagnostics)
726+
}
712727
}
713728

714729
}
@@ -866,14 +881,14 @@ extension Workspace {
866881

867882
/// Implementation of resolve(root:diagnostics:).
868883
///
869-
/// The extra pins will override the pin of the same package in the
870-
/// pinsStore. It is useful in situations where a requirement is being
884+
/// The extra constraints will be added to the main requirements.
885+
/// It is useful in situations where a requirement is being
871886
/// imposed outside of manifest and pins file. E.g., when using a command
872887
/// like `$ swift package resolve foo --version 1.0.0`.
873888
@discardableResult
874889
fileprivate func _resolve(
875890
root: WorkspaceRoot,
876-
extraPins: [PinsStore.Pin] = [],
891+
extraConstraints: [RepositoryPackageConstraint] = [],
877892
diagnostics: DiagnosticsEngine
878893
) -> DependencyManifests {
879894

@@ -898,17 +913,18 @@ extension Workspace {
898913
// The pins to use in case we need to run the resolution.
899914
var validPins = pinsStore.createConstraints()
900915

901-
// Compute if we need to run the resolver.
916+
// Compute if we need to run the resolver. We always run the resolver if
917+
// there are extra constraints.
902918
if missingURLs.isEmpty {
903919
// Use root constraints, dependency manifest constraints and extra
904-
// pins to compute if a new resolution is required.
905-
let dependencies = graphRoot.constraints + currentManifests.dependencyConstraints()
906-
extraPins.forEach(pinsStore.add)
920+
// constraints to compute if a new resolution is required.
921+
let dependencies = graphRoot.constraints + currentManifests.dependencyConstraints() + extraConstraints
907922

908923
let result = isResolutionRequired(dependencies: dependencies, pinsStore: pinsStore)
909924

910-
// If we don't need resolution, just validate pinsStore and return.
911-
if !result.resolve {
925+
// If we don't need resolution and there are no extra constraints,
926+
// just validate pinsStore and return.
927+
if !result.resolve && extraConstraints.isEmpty {
912928
validatePinsStore(with: diagnostics)
913929
return currentManifests
914930
}
@@ -918,13 +934,23 @@ extension Workspace {
918934

919935
// Create the constraints.
920936
var constraints = [RepositoryPackageConstraint]()
921-
constraints += graphRoot.constraints
937+
constraints += graphRoot.constraints + extraConstraints
922938
constraints += currentManifests.editedPackagesConstraints()
923939

924940
// Perform dependency resolution.
925-
let result = resolveDependencies(dependencies: constraints, pins: validPins, diagnostics: diagnostics)
926-
guard !diagnostics.hasErrors else {
927-
return currentManifests
941+
let resolverDiagnostics = DiagnosticsEngine()
942+
var result = resolveDependencies(dependencies: constraints, pins: validPins, diagnostics: resolverDiagnostics)
943+
944+
// If we fail, we just try again without any pins because the pins might
945+
// be completely incompatible.
946+
//
947+
// FIXME: We should only do this if resolver emits "unresolvable" error.
948+
// FIXME: We should merge the engine in case we get no errors but warnings or notes.
949+
if resolverDiagnostics.hasErrors {
950+
result = resolveDependencies(dependencies: constraints, pins: [], diagnostics: diagnostics)
951+
guard !diagnostics.hasErrors else {
952+
return currentManifests
953+
}
928954
}
929955

930956
// Update the checkouts with dependency resolution result.
@@ -1171,18 +1197,26 @@ extension Workspace {
11711197
fileprivate func fixManagedDependencies(with diagnostics: DiagnosticsEngine) {
11721198
for dependency in managedDependencies.values {
11731199
diagnostics.wrap {
1200+
1201+
// If the dependency is present, we're done.
11741202
let dependencyPath = path(for: dependency)
1175-
if !fileSystem.isDirectory(dependencyPath) {
1176-
switch dependency.state {
1177-
case .checkout(let checkoutState):
1178-
// If some checkout dependency has been removed, clone it again.
1179-
_ = try clone(repository: dependency.repository, at: checkoutState)
1180-
diagnostics.emit(WorkspaceDiagnostics.CheckedOutDependencyMissing(packageName: dependency.name))
1181-
case .edited:
1182-
// If some edited dependency has been removed, mark it as unedited.
1183-
try unedit(dependency: dependency, forceRemove: true)
1184-
diagnostics.emit(WorkspaceDiagnostics.EditedDependencyMissing(packageName: dependency.name))
1185-
}
1203+
guard !fileSystem.isDirectory(dependencyPath) else { return }
1204+
1205+
switch dependency.state {
1206+
case .checkout(let checkoutState):
1207+
// If some checkout dependency has been removed, clone it again.
1208+
_ = try clone(repository: dependency.repository, at: checkoutState)
1209+
diagnostics.emit(WorkspaceDiagnostics.CheckedOutDependencyMissing(packageName: dependency.name))
1210+
1211+
case .edited:
1212+
// If some edited dependency has been removed, mark it as unedited.
1213+
//
1214+
// Note: We don't resolve the dependencies when unediting
1215+
// here because we expect this method to be called as part
1216+
// of some other resolve operation (i.e. resolve, update, etc).
1217+
try unedit(dependency: dependency, forceRemove: true, diagnostics: diagnostics)
1218+
1219+
diagnostics.emit(WorkspaceDiagnostics.EditedDependencyMissing(packageName: dependency.name))
11861220
}
11871221
}
11881222
}

0 commit comments

Comments
 (0)