@@ -339,22 +339,21 @@ extension Workspace {
339
339
try unedit ( dependency: dependency, forceRemove: forceRemove)
340
340
}
341
341
342
- /// Pins a package at a given state.
342
+ /// Resolve a package at the given state.
343
343
///
344
344
/// Only one of version, branch and revision will be used and in the same
345
345
/// order. If none of these is provided, the dependency will be pinned at
346
346
/// the current checkout state.
347
347
///
348
348
/// - Parameters:
349
- /// - dependency: The dependency to pin.
350
- /// - packageName: The name of the package which is being pinned.
349
+ /// - packageName: The name of the package which is being resolved.
351
350
/// - root: The workspace's root input.
352
351
/// - version: The version to pin at.
353
352
/// - branch: The branch to pin at.
354
353
/// - revision: The revision to pin at.
355
354
/// - diagnostics: The diagnostics engine that reports errors, warnings
356
355
/// and notes.
357
- public func pin (
356
+ public func resolve (
358
357
packageName: String ,
359
358
root: WorkspaceRoot ,
360
359
version: Version ? = nil ,
@@ -371,72 +370,27 @@ extension Workspace {
371
370
return diagnostics. emit ( error)
372
371
}
373
372
374
- // Compute the requirement.
375
- let requirement : RepositoryPackageConstraint . Requirement
373
+ // Compute the checkout state.
374
+ //
375
+ // We use a dummy revision in case of version and branch because we
376
+ // might not know the needed revision at this point.
377
+ let checkoutState : CheckoutState
376
378
if let version = version {
377
- requirement = . versionSet ( . exact ( version ) )
379
+ checkoutState = CheckoutState ( revision : Revision ( identifier : " " ) , version : version )
378
380
} else if let branch = branch {
379
- requirement = . revision( branch)
381
+ checkoutState = CheckoutState ( revision: Revision ( identifier : " " ) , branch : branch)
380
382
} else if let revision = revision {
381
- requirement = . revision( revision)
383
+ checkoutState = CheckoutState ( revision: Revision ( identifier : revision) )
382
384
} else {
383
- requirement = currentState. requirement ( )
384
- }
385
-
386
- // Load the root manifests and currently checked out manifests.
387
- let rootManifests = loadRootManifests ( packages: root. packages, diagnostics: diagnostics)
388
-
389
- // Load the current manifests.
390
- let graphRoot = PackageGraphRoot ( manifests: rootManifests, dependencies: root. dependencies)
391
- let currentManifests = loadDependencyManifests ( root: graphRoot, diagnostics: diagnostics)
392
-
393
- // Abort if we're unable to load the pinsStore or have any diagnostics.
394
- guard let pinsStore = diagnostics. wrap ( { try self . pinsStore. load ( ) } ) else {
395
- return
385
+ checkoutState = currentState
396
386
}
397
387
398
- // Ensure we don't have any error at this point.
399
- guard !diagnostics. hasErrors else { return }
388
+ // Create a pin with above checkout state.
389
+ let pin = PinsStore . Pin (
390
+ package : dependency. name, repository: dependency. repository, state: checkoutState)
400
391
401
- // Compute constraints with the new pin and try to resolve
402
- // dependencies. We only commit the pin if the dependencies can be
403
- // resolved with new constraints.
404
- //
405
- // The constraints consist of three things:
406
- // * Unversioned constraints for edited packages.
407
- // * Root manifest contraints.
408
- // * Exisiting pins except the dependency we're currently pinning.
409
- // * The constraint for the new pin we're trying to add.
410
- var constraints = currentManifests. editedPackagesConstraints ( )
411
- constraints += graphRoot. constraints
412
-
413
- var pins = pinsStore. createConstraints ( ) . filter ( { $0. identifier != dependency. repository } )
414
- pins. append (
415
- RepositoryPackageConstraint (
416
- container: dependency. repository, requirement: requirement) )
417
-
418
- // Resolve the dependencies.
419
- let results = resolveDependencies ( dependencies: constraints, pins: pins, diagnostics: diagnostics)
420
- guard !diagnostics. hasErrors else { return }
421
-
422
- // Update the checkouts based on new dependency resolution.
423
- updateCheckouts ( with: results, diagnostics: diagnostics)
424
- guard !diagnostics. hasErrors else { return }
425
-
426
- // Get the updated dependency.
427
- let newDependency = managedDependencies [ dependency. repository] !
428
-
429
- // Assert that the dependency is at the pinned checkout state now.
430
- if case . checkout( let checkoutState) = newDependency. state {
431
- assert ( checkoutState. requirement ( ) == requirement)
432
- } else {
433
- assertionFailure ( )
434
- }
435
-
436
- // Update the pins store.
437
- self . pinAll (
438
- pinsStore: pinsStore,
439
- diagnostics: diagnostics)
392
+ // Run the resolution.
393
+ _resolve ( root: root, extraPins: [ pin] , diagnostics: diagnostics)
440
394
}
441
395
442
396
/// Cleans the build artefacts from workspace data.
@@ -574,7 +528,7 @@ extension Workspace {
574
528
root: WorkspaceRoot ,
575
529
diagnostics: DiagnosticsEngine
576
530
) {
577
- _ = _resolve ( root: root, diagnostics: diagnostics)
531
+ _resolve ( root: root, diagnostics: diagnostics)
578
532
}
579
533
580
534
/// Load the package graph data.
@@ -910,10 +864,18 @@ extension Workspace {
910
864
extension Workspace {
911
865
912
866
/// Implementation of resolve(root:diagnostics:).
867
+ ///
868
+ /// The extra pins will override the pin of the same package in the
869
+ /// pinsStore. It is useful in situations where a requirement is being
870
+ /// imposed outside of manifest and pins file. E.g., when using a command
871
+ /// like `$ swift package resolve foo --version 1.0.0`.
872
+ @discardableResult
913
873
fileprivate func _resolve(
914
874
root: WorkspaceRoot ,
875
+ extraPins: [ PinsStore . Pin ] = [ ] ,
915
876
diagnostics: DiagnosticsEngine
916
877
) -> DependencyManifests {
878
+
917
879
// Ensure the cache path exists and validate that edited dependencies.
918
880
createCacheDirectories ( with: diagnostics)
919
881
@@ -937,11 +899,16 @@ extension Workspace {
937
899
938
900
// Compute if we need to run the resolver.
939
901
if missingURLs. isEmpty {
902
+ // Use root constraints, dependency manifest constraints and extra
903
+ // pins to compute if a new resolution is required.
940
904
let dependencies = graphRoot. constraints + currentManifests. dependencyConstraints ( )
905
+ extraPins. forEach ( pinsStore. add)
906
+
941
907
let result = isResolutionRequired ( dependencies: dependencies, pinsStore: pinsStore)
942
908
943
- // We're done if we don't need resolution.
944
- guard result. resolve else {
909
+ // If we don't need resolution, just validate pinsStore and return.
910
+ if !result. resolve {
911
+ validatePinsStore ( with: diagnostics)
945
912
return currentManifests
946
913
}
947
914
@@ -1045,6 +1012,23 @@ extension Workspace {
1045
1012
return ( false , [ ] )
1046
1013
}
1047
1014
1015
+ /// Validates that each checked out managed dependency has an entry in pinsStore.
1016
+ private func validatePinsStore( with diagnostics: DiagnosticsEngine ) {
1017
+ guard let pinsStore = diagnostics. wrap ( { try pinsStore. load ( ) } ) else {
1018
+ return
1019
+ }
1020
+
1021
+ for dependency in managedDependencies. values {
1022
+ switch dependency. state {
1023
+ case . checkout: break
1024
+ case . edited: continue
1025
+ }
1026
+ // If we find any checkout that is not in pins store, invoke pin all and return.
1027
+ if pinsStore. pinsMap [ dependency. name] == nil {
1028
+ return self . pinAll ( pinsStore: pinsStore, diagnostics: diagnostics)
1029
+ }
1030
+ }
1031
+ }
1048
1032
1049
1033
/// This enum represents state of an external package.
1050
1034
fileprivate enum PackageStateChange {
0 commit comments