-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[SE-0085] Add a package
subcommand to handle package-oriented operations
#364
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
/* | ||
This source file is part of the Swift.org open source project | ||
|
||
Copyright 2015 - 2016 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 Swift project authors | ||
*/ | ||
|
||
import Basic | ||
import Build | ||
import Get | ||
import PackageLoading | ||
import PackageModel | ||
import Utility | ||
import Xcodeproj | ||
|
||
#if HasCustomVersionString | ||
import VersionInfo | ||
#endif | ||
|
||
import enum Build.Configuration | ||
import enum Utility.ColorWrap | ||
import protocol Build.Toolchain | ||
|
||
import func POSIX.chdir | ||
|
||
/// Additional conformance for our Options type. | ||
extension PackageToolOptions: XcodeprojOptions {} | ||
|
||
private enum Mode: Argument, Equatable, CustomStringConvertible { | ||
case Init(InitMode) | ||
case Doctor | ||
case ShowDependencies(ShowDependenciesMode) | ||
case Fetch | ||
case Update | ||
case Usage | ||
case Version | ||
case GenerateXcodeproj(String?) | ||
case DumpPackage(String?) | ||
|
||
init?(argument: String, pop: () -> String?) throws { | ||
switch argument { | ||
case "init", "initialize": | ||
self = try .Init(InitMode(pop())) | ||
case "doctor": | ||
self = .Doctor | ||
case "show-dependencies", "-D": | ||
self = try .ShowDependencies(ShowDependenciesMode(pop())) | ||
case "fetch": | ||
self = .Fetch | ||
case "update": | ||
self = .Update | ||
case "help", "usage", "-h": | ||
self = .Usage | ||
case "version": | ||
self = .Version | ||
case "generate-xcodeproj": | ||
self = .GenerateXcodeproj(pop()) | ||
case "dump-package": | ||
self = .DumpPackage(pop()) | ||
default: | ||
return nil | ||
} | ||
} | ||
|
||
var description: String { | ||
switch self { | ||
case .Init(let type): return "init=\(type)" | ||
case .Doctor: return "doctor" | ||
case .ShowDependencies: return "show-dependencies" | ||
case .GenerateXcodeproj: return "generate-xcodeproj" | ||
case .Fetch: return "fetch" | ||
case .Update: return "update" | ||
case .Usage: return "help" | ||
case .Version: return "version" | ||
case .DumpPackage: return "dump-package" | ||
} | ||
} | ||
} | ||
|
||
private enum PackageToolFlag: Argument { | ||
case chdir(String) | ||
case colorMode(ColorWrap.Mode) | ||
case Xcc(String) | ||
case Xld(String) | ||
case Xswiftc(String) | ||
case xcconfigOverrides(String) | ||
case ignoreDependencies | ||
case verbose(Int) | ||
|
||
init?(argument: String, pop: () -> String?) throws { | ||
|
||
func forcePop() throws -> String { | ||
guard let value = pop() else { throw OptionParserError.ExpectedAssociatedValue(argument) } | ||
return value | ||
} | ||
|
||
switch argument { | ||
case Flag.chdir, Flag.C: | ||
self = try .chdir(forcePop()) | ||
case "--verbose", "-v": | ||
self = .verbose(1) | ||
case "-vv": | ||
self = .verbose(2) | ||
case "--color": | ||
let rawValue = try forcePop() | ||
guard let mode = ColorWrap.Mode(rawValue) else { | ||
throw OptionParserError.InvalidUsage("invalid color mode: \(rawValue)") | ||
} | ||
self = .colorMode(mode) | ||
case "--ignore-dependencies": | ||
self = .ignoreDependencies | ||
default: | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
private class PackageToolOptions: Options { | ||
var verbosity: Int = 0 | ||
var colorMode: ColorWrap.Mode = .Auto | ||
var Xcc: [String] = [] | ||
var Xld: [String] = [] | ||
var Xswiftc: [String] = [] | ||
var xcconfigOverrides: String? = nil | ||
var ignoreDependencies: Bool = false | ||
} | ||
|
||
/// swift-build tool namespace | ||
public struct SwiftPackageTool { | ||
let args: [String] | ||
|
||
public init(args: [String]) { | ||
self.args = args | ||
} | ||
|
||
public func run() { | ||
do { | ||
let args = Array(Process.arguments.dropFirst()) | ||
let (mode, opts) = try parse(commandLineArguments: args) | ||
|
||
verbosity = Verbosity(rawValue: opts.verbosity) | ||
colorMode = opts.colorMode | ||
|
||
if let dir = opts.chdir { | ||
try chdir(dir) | ||
} | ||
|
||
func parseManifest(path: String, baseURL: String) throws -> Manifest { | ||
let swiftc = ToolDefaults.SWIFT_EXEC | ||
let libdir = ToolDefaults.libdir | ||
return try Manifest(path: path, baseURL: baseURL, swiftc: swiftc, libdir: libdir) | ||
} | ||
|
||
func fetch(_ root: String) throws -> (rootPackage: Package, externalPackages:[Package]) { | ||
let manifest = try parseManifest(path: root, baseURL: root) | ||
if opts.ignoreDependencies { | ||
return (Package(manifest: manifest, url: manifest.path.parentDirectory), []) | ||
} else { | ||
return try get(manifest, manifestParser: parseManifest) | ||
} | ||
} | ||
|
||
switch mode { | ||
case .Init(let initMode): | ||
let initPackage = try InitPackage(mode: initMode) | ||
try initPackage.writePackageStructure() | ||
|
||
case .Update: | ||
try Utility.removeFileTree(opts.path.Packages) | ||
fallthrough | ||
|
||
case .Fetch: | ||
_ = try fetch(opts.path.root) | ||
|
||
case .Usage: | ||
usage() | ||
|
||
case .Doctor: | ||
doctor() | ||
|
||
case .ShowDependencies(let mode): | ||
let (rootPackage, _) = try fetch(opts.path.root) | ||
dumpDependenciesOf(rootPackage: rootPackage, mode: mode) | ||
|
||
case .Version: | ||
#if HasCustomVersionString | ||
print(String(cString: VersionInfo.DisplayString())) | ||
#else | ||
print("Swift Package Manager – Swift 3.0") | ||
#endif | ||
|
||
case .GenerateXcodeproj(let outpath): | ||
let (rootPackage, externalPackages) = try fetch(opts.path.root) | ||
let (modules, externalModules, products) = try transmute(rootPackage, externalPackages: externalPackages) | ||
|
||
let xcodeModules = modules.flatMap { $0 as? XcodeModuleProtocol } | ||
let externalXcodeModules = externalModules.flatMap { $0 as? XcodeModuleProtocol } | ||
|
||
let projectName: String | ||
let dstdir: String | ||
let packageName = rootPackage.name | ||
|
||
switch outpath { | ||
case let outpath? where outpath.hasSuffix(".xcodeproj"): | ||
// if user specified path ending with .xcodeproj, use that | ||
projectName = String(outpath.basename.characters.dropLast(10)) | ||
dstdir = outpath.parentDirectory | ||
case let outpath?: | ||
dstdir = outpath | ||
projectName = packageName | ||
case _: | ||
dstdir = opts.path.root | ||
projectName = packageName | ||
} | ||
let outpath = try Xcodeproj.generate(dstdir: dstdir.abspath, projectName: projectName, srcroot: opts.path.root, modules: xcodeModules, externalModules: externalXcodeModules, products: products, options: opts) | ||
|
||
print("generated:", outpath.prettyPath) | ||
|
||
case .DumpPackage(let packagePath): | ||
|
||
let root = packagePath ?? opts.path.root | ||
let manifest = try parseManifest(path: root, baseURL: root) | ||
let package = manifest.package | ||
let json = try jsonString(package: package) | ||
print(json) | ||
} | ||
|
||
} catch { | ||
handle(error: error, usage: usage) | ||
} | ||
} | ||
|
||
private func usage(_ print: (String) -> Void = { print($0) }) { | ||
// .........10.........20.........30.........40.........50.........60.........70.. | ||
print("OVERVIEW: Perform operations on a swift package") | ||
print("") | ||
print("USAGE: swift package [command] [options]") | ||
print("") | ||
print("COMMANDS:") | ||
print(" init[=<type>] Initialize a new package (executable|library)") | ||
print(" fetch Fetch package dependencies") | ||
print(" update Update package dependencies") | ||
print(" generate-xcodeproj[=<path>] Generates an Xcode project") | ||
print(" show-dependencies[=<format>] Print dependency graph (text|dot|json)") | ||
print(" dump-package[=<path>] Print Package.swift as JSON") | ||
print("") | ||
print("OPTIONS:") | ||
print(" --chdir <path> Change working directory before any command [-C]") | ||
print(" --color <mode> Specify color mode (auto|always|never)") | ||
print(" --verbose Increase verbosity of informational output [-v]") | ||
print(" -Xcc <flag> Pass flag through to all C compiler instantiations") | ||
print(" -Xlinker <flag> Pass flag through to all linker instantiations") | ||
print(" -Xswiftc <flag> Pass flag through to all Swift compiler instantiations") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Random thought, I wonder if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered that, but it really didn't feel right from a usage perspective. We can open up a separate SE for that, I think. There is still some refactoring to do here, and I think we can find a way to not have to repeat this information, from a technical perspective (through a richer way of being able to define options, and which options apply to which subcommands). |
||
print("") | ||
} | ||
|
||
private func parse(commandLineArguments args: [String]) throws -> (Mode, PackageToolOptions) { | ||
let (mode, flags): (Mode?, [PackageToolFlag]) = try Basic.parseOptions(arguments: args) | ||
|
||
let opts = PackageToolOptions() | ||
for flag in flags { | ||
switch flag { | ||
case .chdir(let path): | ||
opts.chdir = path | ||
case .Xcc(let value): | ||
opts.Xcc.append(value) | ||
case .Xld(let value): | ||
opts.Xld.append(value) | ||
case .Xswiftc(let value): | ||
opts.Xswiftc.append(value) | ||
case .verbose(let amount): | ||
opts.verbosity += amount | ||
case .colorMode(let mode): | ||
opts.colorMode = mode | ||
case .xcconfigOverrides(let path): | ||
opts.xcconfigOverrides = path | ||
case .ignoreDependencies: | ||
opts.ignoreDependencies = true | ||
} | ||
} | ||
if let mode = mode { | ||
return (mode, opts) | ||
} | ||
else { | ||
throw OptionParserError.InvalidUsage("no command provided: \(args)") | ||
} | ||
} | ||
} | ||
|
||
private func ==(lhs: Mode, rhs: Mode) -> Bool { | ||
return lhs.description == rhs.description | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also
--help
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, I got right of that by mistake. I'll add it back and update the PR. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in d0e65e5