diff --git a/Package.swift b/Package.swift index 703b073be21..b08b3d6d86f 100644 --- a/Package.swift +++ b/Package.swift @@ -89,6 +89,10 @@ let package = Package( dependencies: ["Basic", "Build", "Get", "PackageGraph", "Xcodeproj"]), Target( /** The main executable provided by SwiftPM */ + name: "swift-package", + dependencies: ["Commands"]), + Target( + /** Builds packages */ name: "swift-build", dependencies: ["Commands"]), Target( diff --git a/Sources/Commands/SwiftBuildTool.swift b/Sources/Commands/SwiftBuildTool.swift index 5fce7805095..162615ced9c 100644 --- a/Sources/Commands/SwiftBuildTool.swift +++ b/Sources/Commands/SwiftBuildTool.swift @@ -401,58 +401,6 @@ enum CleanMode: CustomStringConvertible { } } -enum InitMode: CustomStringConvertible { - case Library, Executable - - private init(_ rawValue: String?) throws { - switch rawValue?.lowercased() { - case "library"?, "lib"?: - self = .Library - case nil, "executable"?, "exec"?, "exe"?: - self = .Executable - default: - throw OptionParserError.InvalidUsage("invalid initialization mode: \(rawValue)") - } - } - - var description: String { - switch self { - case .Library: return "library" - case .Executable: return "executable" - } - } -} - private func ==(lhs: Mode, rhs: Mode) -> Bool { return lhs.description == rhs.description } - -enum ShowDependenciesMode: CustomStringConvertible { - case Text, DOT, JSON - - private init(_ rawValue: String?) throws { - guard let rawValue = rawValue else { - self = .Text - return - } - - switch rawValue.lowercased() { - case "text": - self = .Text - case "dot": - self = .DOT - case "json": - self = .JSON - default: - throw OptionParserError.InvalidUsage("invalid show dependencies mode: \(rawValue)") - } - } - - var description: String { - switch self { - case .Text: return "text" - case .DOT: return "dot" - case .JSON: return "json" - } - } -} diff --git a/Sources/Commands/SwiftPackageTool.swift b/Sources/Commands/SwiftPackageTool.swift new file mode 100644 index 00000000000..655008e5ce9 --- /dev/null +++ b/Sources/Commands/SwiftPackageTool.swift @@ -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", "--help", "-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[=] Initialize a new package (executable|library)") + print(" fetch Fetch package dependencies") + print(" update Update package dependencies") + print(" generate-xcodeproj[=] Generates an Xcode project") + print(" show-dependencies[=] Print dependency graph (text|dot|json)") + print(" dump-package[=] Print Package.swift as JSON") + print("") + print("OPTIONS:") + print(" --chdir Change working directory before any command [-C]") + print(" --color Specify color mode (auto|always|never)") + print(" --verbose Increase verbosity of informational output [-v]") + print(" -Xcc Pass flag through to all C compiler instantiations") + print(" -Xlinker Pass flag through to all linker instantiations") + print(" -Xswiftc Pass flag through to all Swift compiler instantiations") + 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 +} diff --git a/Sources/Commands/init.swift b/Sources/Commands/init.swift index 6a87fd94ad4..65b6b88ce04 100644 --- a/Sources/Commands/init.swift +++ b/Sources/Commands/init.swift @@ -8,6 +8,7 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ +import Basic import PackageModel import POSIX @@ -162,3 +163,26 @@ final class InitPackage { try fputs("}\n", testsFileFP) } } + +/// Represents a package type for the purposes of initialization. +enum InitMode: CustomStringConvertible { + case Library, Executable + + init(_ rawValue: String?) throws { + switch rawValue?.lowercased() { + case "library"?, "lib"?: + self = .Library + case nil, "executable"?, "exec"?, "exe"?: + self = .Executable + default: + throw OptionParserError.InvalidUsage("invalid initialization type: \(rawValue)") + } + } + + var description: String { + switch self { + case .Library: return "library" + case .Executable: return "executable" + } + } +} diff --git a/Sources/Commands/show-dependencies.swift b/Sources/Commands/show-dependencies.swift index 9a096c99f1e..30c5cb93d11 100644 --- a/Sources/Commands/show-dependencies.swift +++ b/Sources/Commands/show-dependencies.swift @@ -8,6 +8,7 @@ See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ +import Basic import PackageModel import struct PackageDescription.Version @@ -115,3 +116,33 @@ private final class JsonDumper: DependenciesDumper { recursiveWalk(rootpkg: rootpkg) } } + +enum ShowDependenciesMode: CustomStringConvertible { + case Text, DOT, JSON + + init(_ rawValue: String?) throws { + guard let rawValue = rawValue else { + self = .Text + return + } + + switch rawValue.lowercased() { + case "text": + self = .Text + case "dot": + self = .DOT + case "json": + self = .JSON + default: + throw OptionParserError.InvalidUsage("invalid show dependencies mode: \(rawValue)") + } + } + + var description: String { + switch self { + case .Text: return "text" + case .DOT: return "dot" + case .JSON: return "json" + } + } +} diff --git a/Sources/swift-package/main.swift b/Sources/swift-package/main.swift new file mode 100644 index 00000000000..a28afe40106 --- /dev/null +++ b/Sources/swift-package/main.swift @@ -0,0 +1,14 @@ +/* + 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 Commands + +let tool = SwiftPackageTool(args: Array(Process.arguments.dropFirst())) +tool.run()