Skip to content

Commit 84e7ac1

Browse files
committed
[SE-0085] Add a package subcommand to handle package-oriented operations.
This moves the package-management commands from the `build` subcommand to `package`, but leaves building (and, currently, cleaning) on the `build` subcommand. The `test` subcommand is likewise unmodified. I had to temporarily move down the enums for InitMode and ShowDependen- ciesMode, so that both package and build can share them, but this will be consolidated again when build and test are reimplemented in terms of package as well. The build and test functionality is not yet moved in order to minimize changes while still ending up with the desired func- tionality.
1 parent b81b2b9 commit 84e7ac1

File tree

6 files changed

+368
-52
lines changed

6 files changed

+368
-52
lines changed

Package.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ let package = Package(
8989
dependencies: ["Basic", "Build", "Get", "PackageGraph", "Xcodeproj"]),
9090
Target(
9191
/** The main executable provided by SwiftPM */
92+
name: "swift-package",
93+
dependencies: ["Commands"]),
94+
Target(
95+
/** Builds packages */
9296
name: "swift-build",
9397
dependencies: ["Commands"]),
9498
Target(

Sources/Commands/SwiftBuildTool.swift

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -401,58 +401,6 @@ enum CleanMode: CustomStringConvertible {
401401
}
402402
}
403403

404-
enum InitMode: CustomStringConvertible {
405-
case Library, Executable
406-
407-
private init(_ rawValue: String?) throws {
408-
switch rawValue?.lowercased() {
409-
case "library"?, "lib"?:
410-
self = .Library
411-
case nil, "executable"?, "exec"?, "exe"?:
412-
self = .Executable
413-
default:
414-
throw OptionParserError.InvalidUsage("invalid initialization mode: \(rawValue)")
415-
}
416-
}
417-
418-
var description: String {
419-
switch self {
420-
case .Library: return "library"
421-
case .Executable: return "executable"
422-
}
423-
}
424-
}
425-
426404
private func ==(lhs: Mode, rhs: Mode) -> Bool {
427405
return lhs.description == rhs.description
428406
}
429-
430-
enum ShowDependenciesMode: CustomStringConvertible {
431-
case Text, DOT, JSON
432-
433-
private init(_ rawValue: String?) throws {
434-
guard let rawValue = rawValue else {
435-
self = .Text
436-
return
437-
}
438-
439-
switch rawValue.lowercased() {
440-
case "text":
441-
self = .Text
442-
case "dot":
443-
self = .DOT
444-
case "json":
445-
self = .JSON
446-
default:
447-
throw OptionParserError.InvalidUsage("invalid show dependencies mode: \(rawValue)")
448-
}
449-
}
450-
451-
var description: String {
452-
switch self {
453-
case .Text: return "text"
454-
case .DOT: return "dot"
455-
case .JSON: return "json"
456-
}
457-
}
458-
}
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright 2015 - 2016 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Basic
12+
import Build
13+
import Get
14+
import PackageLoading
15+
import PackageModel
16+
import Utility
17+
import Xcodeproj
18+
19+
#if HasCustomVersionString
20+
import VersionInfo
21+
#endif
22+
23+
import enum Build.Configuration
24+
import enum Utility.ColorWrap
25+
import protocol Build.Toolchain
26+
27+
import func POSIX.chdir
28+
29+
/// Additional conformance for our Options type.
30+
extension PackageToolOptions: XcodeprojOptions {}
31+
32+
private enum Mode: Argument, Equatable, CustomStringConvertible {
33+
case Init(InitMode)
34+
case Doctor
35+
case ShowDependencies(ShowDependenciesMode)
36+
case Fetch
37+
case Update
38+
case Usage
39+
case Version
40+
case GenerateXcodeproj(String?)
41+
case DumpPackage(String?)
42+
43+
init?(argument: String, pop: () -> String?) throws {
44+
switch argument {
45+
case "init", "initialize":
46+
self = try .Init(InitMode(pop()))
47+
case "doctor":
48+
self = .Doctor
49+
case "show-dependencies", "-D":
50+
self = try .ShowDependencies(ShowDependenciesMode(pop()))
51+
case "fetch":
52+
self = .Fetch
53+
case "update":
54+
self = .Update
55+
case "help", "usage", "-h":
56+
self = .Usage
57+
case "version":
58+
self = .Version
59+
case "generate-xcodeproj":
60+
self = .GenerateXcodeproj(pop())
61+
case "dump-package":
62+
self = .DumpPackage(pop())
63+
default:
64+
return nil
65+
}
66+
}
67+
68+
var description: String {
69+
switch self {
70+
case .Init(let type): return "init=\(type)"
71+
case .Doctor: return "doctor"
72+
case .ShowDependencies: return "show-dependencies"
73+
case .GenerateXcodeproj: return "generate-xcodeproj"
74+
case .Fetch: return "fetch"
75+
case .Update: return "update"
76+
case .Usage: return "help"
77+
case .Version: return "version"
78+
case .DumpPackage: return "dump-package"
79+
}
80+
}
81+
}
82+
83+
private enum PackageToolFlag: Argument {
84+
case chdir(String)
85+
case colorMode(ColorWrap.Mode)
86+
case Xcc(String)
87+
case Xld(String)
88+
case Xswiftc(String)
89+
case xcconfigOverrides(String)
90+
case ignoreDependencies
91+
case verbose(Int)
92+
93+
init?(argument: String, pop: () -> String?) throws {
94+
95+
func forcePop() throws -> String {
96+
guard let value = pop() else { throw OptionParserError.ExpectedAssociatedValue(argument) }
97+
return value
98+
}
99+
100+
switch argument {
101+
case Flag.chdir, Flag.C:
102+
self = try .chdir(forcePop())
103+
case "--verbose", "-v":
104+
self = .verbose(1)
105+
case "-vv":
106+
self = .verbose(2)
107+
case "--color":
108+
let rawValue = try forcePop()
109+
guard let mode = ColorWrap.Mode(rawValue) else {
110+
throw OptionParserError.InvalidUsage("invalid color mode: \(rawValue)")
111+
}
112+
self = .colorMode(mode)
113+
case "--ignore-dependencies":
114+
self = .ignoreDependencies
115+
default:
116+
return nil
117+
}
118+
}
119+
}
120+
121+
private class PackageToolOptions: Options {
122+
var verbosity: Int = 0
123+
var colorMode: ColorWrap.Mode = .Auto
124+
var Xcc: [String] = []
125+
var Xld: [String] = []
126+
var Xswiftc: [String] = []
127+
var xcconfigOverrides: String? = nil
128+
var ignoreDependencies: Bool = false
129+
}
130+
131+
/// swift-build tool namespace
132+
public struct SwiftPackageTool {
133+
let args: [String]
134+
135+
public init(args: [String]) {
136+
self.args = args
137+
}
138+
139+
public func run() {
140+
do {
141+
let args = Array(Process.arguments.dropFirst())
142+
let (mode, opts) = try parse(commandLineArguments: args)
143+
144+
verbosity = Verbosity(rawValue: opts.verbosity)
145+
colorMode = opts.colorMode
146+
147+
if let dir = opts.chdir {
148+
try chdir(dir)
149+
}
150+
151+
func parseManifest(path: String, baseURL: String) throws -> Manifest {
152+
let swiftc = ToolDefaults.SWIFT_EXEC
153+
let libdir = ToolDefaults.libdir
154+
return try Manifest(path: path, baseURL: baseURL, swiftc: swiftc, libdir: libdir)
155+
}
156+
157+
func fetch(_ root: String) throws -> (rootPackage: Package, externalPackages:[Package]) {
158+
let manifest = try parseManifest(path: root, baseURL: root)
159+
if opts.ignoreDependencies {
160+
return (Package(manifest: manifest, url: manifest.path.parentDirectory), [])
161+
} else {
162+
return try get(manifest, manifestParser: parseManifest)
163+
}
164+
}
165+
166+
switch mode {
167+
case .Init(let initMode):
168+
let initPackage = try InitPackage(mode: initMode)
169+
try initPackage.writePackageStructure()
170+
171+
case .Update:
172+
try Utility.removeFileTree(opts.path.Packages)
173+
fallthrough
174+
175+
case .Fetch:
176+
_ = try fetch(opts.path.root)
177+
178+
case .Usage:
179+
usage()
180+
181+
case .Doctor:
182+
doctor()
183+
184+
case .ShowDependencies(let mode):
185+
let (rootPackage, _) = try fetch(opts.path.root)
186+
dumpDependenciesOf(rootPackage: rootPackage, mode: mode)
187+
188+
case .Version:
189+
#if HasCustomVersionString
190+
print(String(cString: VersionInfo.DisplayString()))
191+
#else
192+
print("Swift Package Manager – Swift 3.0")
193+
#endif
194+
195+
case .GenerateXcodeproj(let outpath):
196+
let (rootPackage, externalPackages) = try fetch(opts.path.root)
197+
let (modules, externalModules, products) = try transmute(rootPackage, externalPackages: externalPackages)
198+
199+
let xcodeModules = modules.flatMap { $0 as? XcodeModuleProtocol }
200+
let externalXcodeModules = externalModules.flatMap { $0 as? XcodeModuleProtocol }
201+
202+
let projectName: String
203+
let dstdir: String
204+
let packageName = rootPackage.name
205+
206+
switch outpath {
207+
case let outpath? where outpath.hasSuffix(".xcodeproj"):
208+
// if user specified path ending with .xcodeproj, use that
209+
projectName = String(outpath.basename.characters.dropLast(10))
210+
dstdir = outpath.parentDirectory
211+
case let outpath?:
212+
dstdir = outpath
213+
projectName = packageName
214+
case _:
215+
dstdir = opts.path.root
216+
projectName = packageName
217+
}
218+
let outpath = try Xcodeproj.generate(dstdir: dstdir.abspath, projectName: projectName, srcroot: opts.path.root, modules: xcodeModules, externalModules: externalXcodeModules, products: products, options: opts)
219+
220+
print("generated:", outpath.prettyPath)
221+
222+
case .DumpPackage(let packagePath):
223+
224+
let root = packagePath ?? opts.path.root
225+
let manifest = try parseManifest(path: root, baseURL: root)
226+
let package = manifest.package
227+
let json = try jsonString(package: package)
228+
print(json)
229+
}
230+
231+
} catch {
232+
handle(error: error, usage: usage)
233+
}
234+
}
235+
236+
private func usage(_ print: (String) -> Void = { print($0) }) {
237+
// .........10.........20.........30.........40.........50.........60.........70..
238+
print("OVERVIEW: Perform operations on a swift package")
239+
print("")
240+
print("USAGE: swift package [command] [options]")
241+
print("")
242+
print("COMMANDS:")
243+
print(" init[=<type>] Initialize a new package (executable|library)")
244+
print(" fetch Fetch package dependencies")
245+
print(" update Update package dependencies")
246+
print(" generate-xcodeproj[=<path>] Generates an Xcode project")
247+
print(" show-dependencies[=<format>] Print dependency graph (text|dot|json)")
248+
print(" dump-package[=<path>] Print Package.swift as JSON")
249+
print("")
250+
print("OPTIONS:")
251+
print(" --chdir <path> Change working directory before any command [-C]")
252+
print(" --color <mode> Specify color mode (auto|always|never)")
253+
print(" --verbose Increase verbosity of informational output [-v]")
254+
print(" -Xcc <flag> Pass flag through to all C compiler instantiations")
255+
print(" -Xlinker <flag> Pass flag through to all linker instantiations")
256+
print(" -Xswiftc <flag> Pass flag through to all Swift compiler instantiations")
257+
print("")
258+
}
259+
260+
private func parse(commandLineArguments args: [String]) throws -> (Mode, PackageToolOptions) {
261+
let (mode, flags): (Mode?, [PackageToolFlag]) = try Basic.parseOptions(arguments: args)
262+
263+
let opts = PackageToolOptions()
264+
for flag in flags {
265+
switch flag {
266+
case .chdir(let path):
267+
opts.chdir = path
268+
case .Xcc(let value):
269+
opts.Xcc.append(value)
270+
case .Xld(let value):
271+
opts.Xld.append(value)
272+
case .Xswiftc(let value):
273+
opts.Xswiftc.append(value)
274+
case .verbose(let amount):
275+
opts.verbosity += amount
276+
case .colorMode(let mode):
277+
opts.colorMode = mode
278+
case .xcconfigOverrides(let path):
279+
opts.xcconfigOverrides = path
280+
case .ignoreDependencies:
281+
opts.ignoreDependencies = true
282+
}
283+
}
284+
if let mode = mode {
285+
return (mode, opts)
286+
}
287+
else {
288+
throw OptionParserError.InvalidUsage("no command provided: \(args)")
289+
}
290+
}
291+
}
292+
293+
private func ==(lhs: Mode, rhs: Mode) -> Bool {
294+
return lhs.description == rhs.description
295+
}

0 commit comments

Comments
 (0)