Skip to content

[SE-0301] Updating a project and its manifest programmatically and from the command line #7467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 22, 2024
17 changes: 17 additions & 0 deletions BuildSupport/SwiftSyntax/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
SET(SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE ${CMAKE_SOURCE_DIR}/../swift-syntax)
message(STATUS "swift-syntax path: ${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}")

include(FetchContent)

if(NOT EXISTS "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}")
message(SEND_ERROR "swift-syntax is required to build SwiftPM. Please run update-checkout or specify SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE")
return()
endif()

# Build swift-syntax libraries with FetchContent.
# set(CMAKE_Swift_COMPILER_TARGET ${SWIFT_HOST_TRIPLE})
set(BUILD_SHARED_LIBS OFF)

file(TO_CMAKE_PATH "${SWIFTPM_PATH_TO_SWIFT_SYNTAX_SOURCE}" swift_syntax_path)
FetchContent_Declare(SwiftSyntax SOURCE_DIR "${swift_syntax_path}")
FetchContent_MakeAvailable(SwiftSyntax)
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@ find_package(SQLite3 REQUIRED)
# Enable `package` modifier for the whole package.
add_compile_options("$<$<COMPILE_LANGUAGE:Swift>:-package-name;SwiftPM>")

add_subdirectory(BuildSupport/SwiftSyntax)
add_subdirectory(Sources)
add_subdirectory(cmake/modules)
30 changes: 30 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ let swiftPMDataModelProduct = (
"PackageLoading",
"PackageMetadata",
"PackageModel",
"PackageModelSyntax",
"SourceControl",
"Workspace",
]
Expand Down Expand Up @@ -246,6 +247,23 @@ let package = Package(
swiftSettings: packageModelResourcesSettings
),

.target(
/** Primary Package model objects relationship to SwiftSyntax */
name: "PackageModelSyntax",
dependencies: [
"Basics",
"PackageLoading",
"PackageModel",
.product(name: "SwiftBasicFormat", package: "swift-syntax"),
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
.product(name: "SwiftParser", package: "swift-syntax"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
],
exclude: ["CMakeLists.txt"]
),

.target(
/** Package model conventions and loading support */
name: "PackageLoading",
Expand Down Expand Up @@ -414,10 +432,12 @@ let package = Package(
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "OrderedCollections", package: "swift-collections"),
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
"Basics",
"Build",
"CoreCommands",
"PackageGraph",
"PackageModelSyntax",
"SourceControl",
"Workspace",
"XCBuildSupport",
Expand Down Expand Up @@ -635,6 +655,14 @@ let package = Package(
name: "PackageModelTests",
dependencies: ["PackageModel", "SPMTestSupport"]
),
.testTarget(
name: "PackageModelSyntaxTests",
dependencies: [
"PackageModelSyntax",
"SPMTestSupport",
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
]
),
.testTarget(
name: "PackageGraphTests",
dependencies: ["PackageGraph", "SPMTestSupport"]
Expand Down Expand Up @@ -785,6 +813,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.2.2")),
.package(url: "https://github.com/apple/swift-driver.git", branch: relatedDependenciesBranch),
.package(url: "https://github.com/apple/swift-crypto.git", .upToNextMinor(from: "3.0.0")),
.package(url: "https://github.com/apple/swift-syntax.git", branch: relatedDependenciesBranch),
.package(url: "https://github.com/apple/swift-system.git", .upToNextMinor(from: "1.1.1")),
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMinor(from: "1.0.1")),
.package(url: "https://github.com/apple/swift-certificates.git", .upToNextMinor(from: "1.0.1")),
Expand All @@ -795,6 +824,7 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
.package(path: "../swift-argument-parser"),
.package(path: "../swift-driver"),
.package(path: "../swift-crypto"),
.package(path: "../swift-syntax"),
.package(path: "../swift-system"),
.package(path: "../swift-collections"),
.package(path: "../swift-certificates"),
Expand Down
1 change: 1 addition & 0 deletions Sources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_subdirectory(PackageFingerprint)
add_subdirectory(PackageGraph)
add_subdirectory(PackageLoading)
add_subdirectory(PackageModel)
add_subdirectory(PackageModelSyntax)
add_subdirectory(PackagePlugin)
add_subdirectory(PackageRegistry)
add_subdirectory(PackageSigning)
Expand Down
3 changes: 3 additions & 0 deletions Sources/Commands/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors

add_library(Commands
PackageCommands/AddDependency.swift
PackageCommands/AddTarget.swift
PackageCommands/APIDiff.swift
PackageCommands/ArchiveSource.swift
PackageCommands/CompletionCommand.swift
Expand Down Expand Up @@ -56,6 +58,7 @@ target_link_libraries(Commands PUBLIC
CoreCommands
LLBuildManifest
PackageGraph
PackageModelSyntax
SourceControl
TSCBasic
TSCUtility
Expand Down
155 changes: 155 additions & 0 deletions Sources/Commands/PackageCommands/AddDependency.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2014-2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import ArgumentParser
import Basics
import CoreCommands
import PackageModel
import PackageModelSyntax
import SwiftParser
import SwiftSyntax
import TSCBasic
import TSCUtility
import Workspace

extension SwiftPackageCommand {
struct AddDependency: SwiftCommand {
package static let configuration = CommandConfiguration(
abstract: "Add a package dependency to the manifest")

@Argument(help: "The URL or directory of the package to add")
var dependency: String

@OptionGroup(visibility: .hidden)
var globalOptions: GlobalOptions

@Option(help: "The exact package version to depend on")
var exact: Version?

@Option(help: "The specific package revision to depend on")
var revision: String?

@Option(help: "The branch of the package to depend on")
var branch: String?

@Option(help: "The package version to depend on (up to the next major version)")
var from: Version?

@Option(help: "The package version to depend on (up to the next minor version)")
var upToNextMinorFrom: Version?

@Option(help: "Specify upper bound on the package version range (exclusive)")
var to: Version?

func run(_ swiftCommandState: SwiftCommandState) throws {
let workspace = try swiftCommandState.getActiveWorkspace()

guard let packagePath = try swiftCommandState.getWorkspaceRoot().packages.first else {
throw StringError("unknown package")
}

// Load the manifest file
let fileSystem = workspace.fileSystem
let manifestPath = packagePath.appending("Package.swift")
let manifestContents: ByteString
do {
manifestContents = try fileSystem.readFileContents(manifestPath)
} catch {
throw StringError("cannot find package manifest in \(manifestPath)")
}

// Parse the manifest.
let manifestSyntax = manifestContents.withData { data in
data.withUnsafeBytes { buffer in
buffer.withMemoryRebound(to: UInt8.self) { buffer in
Parser.parse(source: buffer)
}
}
}

let identity = PackageIdentity(url: .init(dependency))

// Collect all of the possible version requirements.
var requirements: [PackageDependency.SourceControl.Requirement] = []
if let exact {
requirements.append(.exact(exact))
}

if let branch {
requirements.append(.branch(branch))
}

if let revision {
requirements.append(.revision(revision))
}

if let from {
requirements.append(.range(.upToNextMajor(from: from)))
}

if let upToNextMinorFrom {
requirements.append(.range(.upToNextMinor(from: upToNextMinorFrom)))
}

if requirements.count > 1 {
throw StringError("must specify at most one of --exact, --branch, --revision, --from, or --up-to-next-minor-from")
}

guard let firstRequirement = requirements.first else {
throw StringError("must specify one of --exact, --branch, --revision, --from, or --up-to-next-minor-from")
}

let requirement: PackageDependency.SourceControl.Requirement
if case .range(let range) = firstRequirement {
if let to {
requirement = .range(range.lowerBound..<to)
} else {
requirement = .range(range)
}
} else {
requirement = firstRequirement

if to != nil {
throw StringError("--to can only be specified with --from or --up-to-next-minor-from")
}
}

// Figure out the location of the package.
let location: PackageDependency.SourceControl.Location
if let path = try? Basics.AbsolutePath(validating: dependency) {
location = .local(path)
} else {
location = .remote(.init(dependency))
}

let packageDependency: PackageDependency = .sourceControl(
identity: identity,
nameForTargetDependencyResolutionOnly: nil,
location: location,
requirement: requirement,
productFilter: .everything
)

let editResult = try AddPackageDependency.addPackageDependency(
packageDependency,
to: manifestSyntax
)

try editResult.applyEdits(
to: fileSystem,
manifest: manifestSyntax,
manifestPath: manifestPath,
verbose: !globalOptions.logging.quiet
)
}
}
}
Loading