Skip to content

Commit

Permalink
Implement local dependencies proposal
Browse files Browse the repository at this point in the history
<rdar://problem/39418745> [SR-7433]: Implement SE-201 Package Manager Local Dependencies
  • Loading branch information
ankitspd committed May 24, 2018
1 parent 6983434 commit 876746b
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 45 deletions.
1 change: 1 addition & 0 deletions Sources/PackageDescription4/Package.swift
Expand Up @@ -26,6 +26,7 @@ public final class Package {
case rangeItem(Range<Version>)
case revisionItem(String)
case branchItem(String)
case localPackageItem
}

/// The url of the dependency.
Expand Down
15 changes: 15 additions & 0 deletions Sources/PackageDescription4/PackageDependency.swift
Expand Up @@ -24,6 +24,12 @@ extension Package.Dependency: Equatable {
url: String,
_ requirement: Package.Dependency.Requirement
) -> Package.Dependency {
// FIXME: This is suboptimal but its the only way to do this right now.
#if PACKAGE_DESCRIPTION_4
precondition(requirement != .localPackageItem, "Use `.package(path:)` API to declare a local package dependency")
#elseif PACKAGE_DESCRIPTION_4_2
precondition(requirement != .localPackageItem, "Use `.package(path:)` API to declare a local package dependency")
#endif
return .init(url: url, requirement: requirement)
}

Expand All @@ -49,6 +55,15 @@ extension Package.Dependency: Equatable {
return .package(url: url, range.lowerBound..<upperBound)
}

#if PACKAGE_DESCRIPTION_4_2
/// Add a dependency to a local package on the filesystem.
public static func package(
path: String
) -> Package.Dependency {
return .init(url: path, requirement: .localPackageItem)
}
#endif

public static func == (lhs: Package.Dependency, rhs: Package.Dependency) -> Bool {
return lhs.url == rhs.url && lhs.requirement == rhs.requirement
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/PackageDescription4/PackageRequirement.swift
Expand Up @@ -58,6 +58,10 @@ extension Package.Dependency.Requirement: Equatable {
return lhs == rhs
case (.exactItem, _):
return false
case (.localPackageItem, .localPackageItem):
return true
case (.localPackageItem, _):
return false
}
}

Expand All @@ -84,6 +88,10 @@ extension Package.Dependency.Requirement: Equatable {
"type": .string("revision"),
"identifier": .string(identifier),
])
case .localPackageItem:
return .dictionary([
"type": .string("localPackage"),
])
}
}
}
15 changes: 12 additions & 3 deletions Sources/PackageGraph/DependencyResolver.swift
Expand Up @@ -17,6 +17,7 @@ public enum DependencyResolverError: Error, Equatable, CustomStringConvertible {
case unsatisfiable

/// The resolver encountered a versioned container which has a revision dependency.
// FIXME: Rename this to incompatible constraints.
case revisionConstraints(
dependency: (AnyPackageContainerIdentifier, String),
revisions: [(AnyPackageContainerIdentifier, String)])
Expand All @@ -41,7 +42,7 @@ public enum DependencyResolverError: Error, Equatable, CustomStringConvertible {
return "unable to resolve dependencies"
case let .revisionConstraints(dependency, revisions):
let stream = BufferedOutputByteStream()
stream <<< "the package \(dependency.0.identifier) @ \(dependency.1) contains revisioned dependencies:\n"
stream <<< "the package \(dependency.0.identifier) @ \(dependency.1) contains incompatible dependencies:\n"
for (i, revision) in revisions.enumerated() {
stream <<< " "
stream <<< "\(revision.0.identifier)" <<< " @ " <<< revision.1
Expand Down Expand Up @@ -1072,10 +1073,18 @@ public class DependencyResolver<
// dependencies can have a revision constraints.
let revisionConstraints: [(AnyPackageContainerIdentifier, String)]
revisionConstraints = constraints.compactMap({
if case .revision(let revision) = $0.requirement {
switch $0.requirement {
case .versionSet:
return nil
case .revision(let revision):
return (AnyPackageContainerIdentifier($0.identifier), revision)
case .unversioned:
// FIXME: Maybe we should have metadata inside unversion to signify
// if its a local or edited dependency. We add edited constraints
// as inputs so it shouldn't really matter because an edited
// requirement can't be specified in the manifest file.
return (AnyPackageContainerIdentifier($0.identifier), "local")
}
return nil
})
// If we have any revision constraints, set the error and abort.
guard revisionConstraints.isEmpty else {
Expand Down
6 changes: 5 additions & 1 deletion Sources/PackageGraph/PackageGraphRoot.swift
Expand Up @@ -58,7 +58,8 @@ public struct PackageGraphRoot {
public func createPackageRef() -> PackageReference {
return PackageReference(
identity: PackageReference.computeIdentity(packageURL: url),
path: url
path: url,
isLocal: (requirement == .localPackageItem)
)
}

Expand Down Expand Up @@ -133,6 +134,9 @@ extension PackageDescription4.Package.Dependency.Requirement {

case .exactItem(let version):
return .versionSet(.exact(Version(pdVersion: version)))

case .localPackageItem:
return .unversioned
}
}
}
3 changes: 2 additions & 1 deletion Sources/PackageGraph/RawPackageConstraints.swift
Expand Up @@ -16,7 +16,8 @@ extension PackageDescription4.Package.Dependency {
public func createPackageRef() -> PackageReference {
return PackageReference(
identity: PackageReference.computeIdentity(packageURL: url),
path: url
path: url,
isLocal: (requirement == .localPackageItem)
)
}
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/PackageLoading/PackageDescription4Loader.swift
Expand Up @@ -142,7 +142,10 @@ extension PackageDescription4.Package.Dependency {
guard case .string(let identifier)? = requirementDict["identifier"] else { fatalError() }
requirement = .exactItem(Version(identifier)!)

default: fatalError()
case .string("localPackage")?:
requirement = .localPackageItem

default: fatalError("Unexpected requirement dict \(requirementDict)")
}

let isBaseURLRemote = baseURL.flatMap(URL.scheme) != nil
Expand Down
12 changes: 12 additions & 0 deletions Sources/Workspace/Diagnostics.swift
Expand Up @@ -144,6 +144,18 @@ public enum WorkspaceDiagnostics {
public let repositoryPath: AbsolutePath
}

public struct LocalDependencyEdited: DiagnosticData, Swift.Error {
public static var id = DiagnosticID(
type: LocalDependencyEdited.self,
name: "org.swift.diags.workspace.local-dependency-edited",
description: {
$0 <<< "local dependency" <<< { "'\($0.dependencyName)'" } <<< "can't be edited"
})

/// The name of the dependency being edited.
public let dependencyName: String
}

/// The diagnostic triggered when the edit operation fails because the dependency
/// is already in edit mode.
public struct DependencyAlreadyInEditMode: DiagnosticData, Swift.Error {
Expand Down
34 changes: 34 additions & 0 deletions Sources/Workspace/ManagedDependency.swift
Expand Up @@ -33,6 +33,9 @@ public final class ManagedDependency: JSONMappable, JSONSerializable {
/// for top of the tree style development.
case edited(AbsolutePath?)

// The dependency is a local package.
case local

/// Returns true if state is checkout.
var isCheckout: Bool {
if case .checkout = self { return true }
Expand Down Expand Up @@ -67,6 +70,31 @@ public final class ManagedDependency: JSONMappable, JSONSerializable {
self.subpath = subpath
}

/// Create a dependency present locally on the filesystem.
static func local(
packageRef: PackageReference
) -> ManagedDependency {
return ManagedDependency(
packageRef: packageRef,
state: .local,
// FIXME: This is just a fake entry, we should fix it.
subpath: RelativePath(packageRef.identity),
basedOn: nil
)
}

private init(
packageRef: PackageReference,
state: State,
subpath: RelativePath,
basedOn: ManagedDependency?
) {
self.packageRef = packageRef
self.subpath = subpath
self.basedOn = basedOn
self.state = state
}

private init(
basedOn dependency: ManagedDependency,
subpath: RelativePath,
Expand Down Expand Up @@ -119,6 +147,10 @@ extension ManagedDependency.State: JSONMappable, JSONSerializable {
"name": "edited",
"path": path.toJSON(),
])
case .local:
return .init([
"name": "local",
])
}
}

Expand All @@ -130,6 +162,8 @@ extension ManagedDependency.State: JSONMappable, JSONSerializable {
case "edited":
let path: String? = json.get("path")
self = .edited(path.map({AbsolutePath($0)}))
case "local":
self = .local
default:
throw JSON.MapError.custom(key: nil, message: "Invalid state \(name)")
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Workspace/PinsStore.swift
Expand Up @@ -115,7 +115,7 @@ public final class PinsStore {
switch dependency.state {
case .checkout(let state):
checkoutState = state
case .edited:
case .edited, .local:
return
}

Expand Down

0 comments on commit 876746b

Please sign in to comment.