<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><br></div><div><br>On 28 Feb 2017, at 01:50, Daniel Dunbar <<a href="mailto:daniel_dunbar@apple.com">daniel_dunbar@apple.com</a>> wrote:<br><br></div><blockquote type="cite"><div><meta http-equiv="Content-Type" content="text/html charset=us-ascii">Hi David,<div class=""><br class=""></div><div class="">We discussed the leading-dot & capitalization issue today again... this was already something we weren't really happy about, but had chosen to live with (using the "identity" rule to determine what was a type and what wasn't). However, as we talked it over more we:</div><div class="">1. Felt that for the product types, using .library vs .Library would be reasonable and consistent with a user model of thinking of these like enums (even though they won't actually be in practice, we will use factory functions on Product to make the dot work and keep the space extensible).</div><div class="">2. Realized that using .target would be a useful change to make now if we ever ended up needing to make the Targets array polymorphic (not something we plan to do now, but it never hurts to have it be extensible).</div><div class="">so we decided to go ahead and revise to a model where we use leading-dot + lowercase for everything (except Package), including reverting SystemPackageProvider to the `.brew(...)` style syntax.</div></div></blockquote><div><br></div><div>Sounds great! When you send it through proposal, can you include an example Package with all properties set so we can see the new API in use?</div><div><br></div><div>Thanks!</div><br><blockquote type="cite"><div><div class="">Thanks for the feedback!</div><div class=""> - Daniel</div><div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Feb 27, 2017, at 2:21 AM, Ankit Aggarwal via swift-build-dev <<a href="mailto:swift-build-dev@swift.org" class="">swift-build-dev@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">Hi David,<div class=""><br class=""></div><div class="">Thanks for the feedback! Comments inline:</div><div class=""><br class=""><div class="gmail_extra"><br class=""><div class="gmail_quote">On Sun, Feb 26, 2017 at 5:08 AM, David Hart via swift-build-dev<span class="Apple-converted-space"> </span><span dir="ltr" class=""><<a href="mailto:swift-build-dev@swift.org" target="_blank" class="">swift-build-dev@swift.org</a>></span><span class="Apple-converted-space"> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="auto" class=""><div class="">Was looking forward to this :) here are my comments:</div><div class=""><div class="gmail-h5"><div class=""><br class="">On 25 Feb 2017, at 01:35, Rick Ballard via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><span class="">Hi all,</span><br class=""><span class=""></span><br class=""><span class="">Ankit, Daniel, Anders, Boris and I have a draft proposal in progress for a Package.swift manifest API redesign for the Package Manager. We'll welcome comments or discussion at this time. My hope is that we can get this polished up and ready for evolution within the next week or so, but we'll see how the conversation goes!</span><br class=""><span class=""></span><br class=""><span class="">You can see the proposal in progress at<span class="Apple-converted-space"> </span><a href="https://github.com/aciidb0mb3r/swift-evolution/blob/manifest-api-redesign/proposals/xxxx-package-manager-manifest-api-redesign.md" target="_blank" class="">https://github.com/<wbr class="">aciidb0mb3r/swift-evolution/<wbr class="">blob/manifest-api-redesign/<wbr class="">proposals/xxxx-package-<wbr class="">manager-manifest-api-redesign.<wbr class="">md</a>. I'm also including the current version inline in this email.</span><br class=""><span class=""></span><br class=""><span class="">Thanks,</span><br class=""><span class=""></span><br class=""><span class=""> - Rick</span><br class=""><span class=""></span><br class=""><span class=""># Package Manager Manifest API Redesign</span><br class=""><span class=""></span><br class=""><span class="">* Proposal: [SE-XXXX](<a href="http://xxxx-package-manager-manifest-api-redesign.md/" target="_blank" class="">xxxx-package-<wbr class="">manager-manifest-api-redesign.<wbr class="">md</a>)</span><br class=""><span class="">* Author: [Ankit Aggarwal](<a href="https://github.com/aciidb0mb3r" target="_blank" class="">https://github.com/<wbr class="">aciidb0mb3r</a>)</span><br class=""><span class="">* Review Manager: TBD</span><br class=""><span class="">* Status: **Discussion**</span><br class=""><span class=""></span><br class=""><span class="">## Introduction</span><br class=""><span class=""></span><br class=""><span class="">This is a proposal for redesigning the `Package.swift` manifest APIs provided</span><br class=""><span class="">by Swift Package Manager. </span><br class=""><span class="">This proposal only redesigns the existing public APIs and does not add any</span><br class=""><span class="">new functionality; any API to be added for new functionality will happen in</span><br class=""><span class="">separate proposals.</span><br class=""><span class=""></span><br class=""><span class="">## Motivation</span><br class=""><span class=""></span><br class=""><span class="">The `Package.swift` manifest APIs were designed prior to the [API Design</span><br class=""><span class="">Guidelines] (<a href="https://swift.org/documentation/api-design-guidelines/" target="_blank" class="">https://swift.org/<wbr class="">documentation/api-design-<wbr class="">guidelines/</a>), and their</span><br class=""><span class="">design was not reviewed by the evolution process. Additionally, there are</span><br class=""><span class="">several small areas which can be cleaned up to make the overall API more</span><br class=""><span class="">"Swifty".</span><br class=""><span class=""></span><br class=""><span class="">We would like to redesign these APIs as necessary to provide clean,</span><br class=""><span class="">conventions-compliant APIs that we can rely on in the future. Because we</span><br class=""><span class="">anticipate that the user community for the Swift Package Manager will grow</span><br class=""><span class="">considerably in Swift 4, we would like to make these changes now, before</span><br class=""><span class="">more packages are created using the old API.</span><br class=""><span class=""></span><br class=""><span class="">## Proposed solution</span><br class=""><span class=""></span><br class=""><span class="">Note: Access modifier is omitted from the diffs and examples for brevity. The</span><br class=""><span class="">access modifier is `public` for all APIs unless specified.</span><br class=""><span class=""></span><br class=""><span class="">* Remove `successor()` and `predecessor()` from `Version`.</span><br class=""><span class=""></span><br class=""><span class=""> These methods neither have well defined semantics nor are used a lot</span><br class=""><span class=""> (internally or publicly). For e.g., the current implementation of</span><br class=""><span class=""> `successor()` always just increases the patch version.</span><br class=""><span class=""></span><br class=""><span class=""></span><br class=""><span class=""> <details></span><br class=""><span class=""> <summary>View diff</summary></span><br class=""><span class=""> <p></span><br class=""><span class=""> ```diff</span><br class=""><span class=""> struct Version {</span><br class=""><span class=""> - func successor() -> Version</span><br class=""><span class=""></span><br class=""><span class=""> - func predecessor() -> Version</span><br class=""><span class=""> }</span><br class=""><span class=""> ```</span><br class=""><span class=""> </p></details></span><br class=""><span class=""></span><br class=""><span class="">* Make all properties of `Package` and `Target` mutable.</span><br class=""><span class=""></span><br class=""><span class=""> Currently, `Package` has three immutable and four mutable properties, and</span><br class=""><span class=""> `Target` has one immutable and one mutable property. We propose to make all</span><br class=""><span class=""> properties mutable to allow complex customization on the package object</span><br class=""><span class=""> after initial declaration.</span><br class=""><span class=""></span><br class=""><span class=""> <details></span><br class=""><span class=""> <summary>View diff and example</summary></span><br class=""><span class=""> <p></span><br class=""><span class=""></span><br class=""><span class=""> Diff:</span><br class=""><span class=""> ```diff</span><br class=""><span class=""> final class Target {</span><br class=""><span class=""> - let name: String</span><br class=""><span class=""> + var name: String</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class=""> final class Package {</span><br class=""><span class=""> - let name: String</span><br class=""><span class=""> + var name: String</span><br class=""><span class=""></span><br class=""><span class=""> - let pkgConfig: String?</span><br class=""><span class=""> + var pkgConfig: String?</span><br class=""><span class=""></span><br class=""><span class=""> - let providers: [SystemPackageProvider]?</span><br class=""><span class=""> + var providers: [SystemPackageProvider]?</span><br class=""><span class=""> }</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> Example:</span><br class=""><span class=""> ```swift</span><br class=""><span class=""> let package = Package(</span><br class=""><span class=""> name: "FooPackage",</span><br class=""><span class=""> targets: [</span><br class=""><span class=""> Target(name: "Foo", dependencies: ["Bar"]),</span><br class=""><span class=""> ]</span><br class=""><span class=""> )</span><br class=""><span class=""></span><br class=""><span class=""> #if os(Linux)</span><br class=""><span class=""> package.targets[0].<wbr class="">dependencies = ["BarLinux"]</span><br class=""><span class=""> #endif</span><br class=""><span class=""> ```</span><br class=""><span class=""> </p></details></span><br class=""><span class=""></span><br class=""><span class="">* Change `Target.Dependency` enum cases to lowerCamelCase.</span><br class=""><span class=""></span><br class=""><span class=""> According to API design guidelines, everything other than types should be in lowerCamelCase.</span><br class=""><span class=""></span><br class=""><span class=""> <details></span><br class=""><span class=""> <summary>View diff and example</summary></span><br class=""><span class=""> <p></span><br class=""><span class=""></span><br class=""><span class=""> Diff:</span><br class=""><span class=""> ```diff</span><br class=""><span class=""> enum Dependency {</span><br class=""><span class=""> - case Target(name: String)</span><br class=""><span class=""> + case target(name: String)</span><br class=""><span class=""></span><br class=""><span class=""> - case Product(name: String, package: String?)</span><br class=""><span class=""> + case product(name: String, package: String?)</span><br class=""><span class=""></span><br class=""><span class=""> - case ByName(name: String)</span><br class=""><span class=""> + case byName(name: String)</span><br class=""><span class=""> }</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> Example:</span><br class=""><span class=""> ```diff</span><br class=""><span class=""> let package = Package(</span><br class=""><span class=""> name: "FooPackage",</span><br class=""><span class=""> targets: [</span><br class=""><span class=""> Target(</span><br class=""><span class=""> name: "Foo",<span class="Apple-converted-space"> </span></span><br class=""><span class=""> dependencies: [</span><br class=""><span class=""> - .Target(name: "Bar"),</span><br class=""><span class=""> + .target(name: "Bar"),</span><br class=""><span class=""></span><br class=""><span class=""> - .Product(name: "SwiftyJSON", package: "SwiftyJSON"),</span><br class=""><span class=""> + .product(name: "SwiftyJSON", package: "SwiftyJSON"),</span><br class=""><span class=""> ]</span><br class=""><span class=""> ),</span><br class=""><span class=""> ]</span><br class=""><span class=""> )</span><br class=""><span class=""> ```</span><br class=""><span class=""> </p></details></span><br class=""><span class=""></span><br class=""><span class="">* Add default parameter to the enum case `Target.Dependency.product`.</span><br class=""><span class=""></span><br class=""><span class=""> The associated value `package` in the (enum) case `product`, is an optional</span><br class=""><span class=""> `String`. It should have the default value `nil` so clients don't need to</span><br class=""><span class=""> write it if they prefer using explicit enum cases but don't want to specify</span><br class=""><span class=""> the package name i.e. it should be possible to write `.product(name:</span><br class=""><span class=""> "Foo")` instead of `.product(name: "Foo", package: nil)`.</span><br class=""><span class=""></span><br class=""><span class=""> If</span><br class=""><span class=""> [SE-0155](<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md" target="_blank" class="">https://github.<wbr class="">com/apple/swift-evolution/<wbr class="">blob/master/proposals/0155-<wbr class="">normalize-enum-case-<wbr class="">representation.md</a>)</span><br class=""><span class=""> is accepted, we can directly add a default value. Otherwise, we will use a</span><br class=""><span class=""> static factory method to provide default value for `package`.</span><br class=""><span class=""></span><br class=""><span class="">* Upgrade `SystemPackageProvider` enum to a struct.</span><br class=""><span class=""></span><br class=""><span class=""> This enum allows SwiftPM System Packages to emit hints in case of build</span><br class=""><span class=""> failures due to absence of a system package. Currently, only one system</span><br class=""><span class=""> package per system packager can be specified. We propose to allow</span><br class=""><span class=""> specifying multiple system packages by replacing the enum with this struct:</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> public struct SystemPackageProvider {</span><br class=""><span class=""> enum PackageManager {</span><br class=""><span class=""> case apt</span><br class=""><span class=""> case brew</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class=""> /// The system package manager.</span><br class=""><span class=""> let packageManager: PackageManager<span class="Apple-converted-space"> </span></span><br class=""><span class=""></span><br class=""><span class=""> /// The array of system packages.</span><br class=""><span class=""> let packages: [String]</span><br class=""><span class=""></span><br class=""><span class=""> init(_ packageManager: PackageManager, packages: [String])</span><br class=""><span class=""> }</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> <details></span><br class=""><span class=""> <summary>View diff and example</summary></span><br class=""><span class=""> <p></span><br class=""><span class=""></span><br class=""><span class=""> Diff:</span><br class=""><span class=""> ```diff</span><br class=""><span class=""> -enum SystemPackageProvider {</span><br class=""><span class=""> - case Brew(String)</span><br class=""><span class=""> - case Apt(String)</span><br class=""><span class=""> -}</span><br class=""><span class=""></span><br class=""><span class=""> +struct SystemPackageProvider {</span><br class=""><span class=""> + enum PackageManager {</span><br class=""><span class=""> + case apt</span><br class=""><span class=""> + case brew</span><br class=""><span class=""> + }</span><br class=""><span class=""> +</span><br class=""><span class=""> + /// The system package manager.</span><br class=""><span class=""> + let packageManager: PackageManager<span class="Apple-converted-space"> </span></span><br class=""><span class=""> +</span><br class=""><span class=""> + /// The array of system packages.</span><br class=""><span class=""> + let packages: [String]</span><br class=""><span class=""> +</span><br class=""><span class=""> + init(_ packageManager: PackageManager, packages: [String])</span><br class=""><span class=""> +}</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> Example:</span><br class=""><span class=""></span><br class=""><span class=""> ```diff</span><br class=""><span class=""> let package = Package(</span><br class=""><span class=""> name: "Copenssl",</span><br class=""><span class=""> pkgConfig: "openssl",</span><br class=""><span class=""> providers: [</span><br class=""><span class=""> - .Brew("openssl"),</span><br class=""><span class=""> + SystemPackageProvider(.<wbr class="">brew, packages: ["openssl"]),</span><br class=""><span class=""></span><br class=""><span class=""> - .Apt("openssl-dev"),</span><br class=""><span class=""> + SystemPackageProvider(.<wbr class="">apt, packages: ["openssl", "libssl-dev"]),</span><br class=""><span class=""> ]</span><br class=""><span class=""> )</span><br class=""><span class=""> ```</span><br class=""><span class=""> </p></details></span><br class=""></div></blockquote><div class=""><br class=""></div></div></div><div class="">Why not keep the enum and change the associated type to a String array?</div><span class="gmail-"><br class=""></span></div></blockquote><div class=""><br class=""></div><div class="">True, we could do that but we'd be repeating that information in every SystemPackager we add. Converting to a struct makes it easier to scale.</div><div class=""> </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="auto" class=""><span class="gmail-"><blockquote type="cite" class=""><div class=""><span class="">* Remove implicit target dependency rule for test targets.</span><br class=""><span class=""></span><br class=""><span class=""> There is an implicit test target dependency rule: a test target "FooTests"</span><br class=""><span class=""> implicity depends on a target "Foo", if "Foo" exists and "FooTests" doesn't</span><br class=""><span class=""> explicitly declare any dependency. We propose to remove this rule because:</span><br class=""><span class=""></span><br class=""><span class=""> 1. It is a non obvious "magic" rule that has to be learned.</span><br class=""><span class=""> 2. It is not possible for "FooTests" to remove dependency on "Foo" while</span><br class=""><span class=""> having no other (target) dependency.</span><br class=""><span class=""> 3. It makes real dependencies less discoverable.</span><br class=""><span class=""> 4. It may cause issues when we get support for mechanically editing target</span><br class=""><span class=""> dependencies.</span><br class=""><span class=""></span><br class=""><span class="">* Introduce an "identity rule" to determine if an API should use an initializer</span><br class=""><span class=""> or a factory method:</span><br class=""></div></blockquote><div class=""><br class=""></div></span><div class="">Could you explain this rule in more detail. What is an identity in this case? I'm confused.</div></div></blockquote><div class=""><br class=""></div><div class=""><br class=""></div><div class="">This is similar to the rule we use to decide if something should be a struct or a class. If you're forming a concrete object, that would be an identity. Consider these two examples:</div><div class=""><br class=""></div><div class="">1. Target and its dependencies:</div><div class=""><br class=""></div><div class=""> Target(name: "Foo", dependencies: [.target(name: "Bar")])</div><div class=""><br class=""></div><div class="">Here the target Foo is being constructed, so an initializer is used. The target Bar is being referred in Foo's dependencies so that uses a factory method.</div><div class=""><br class=""></div><div class="">2. Product and product dependencies in targets:</div><div class=""><br class=""></div><div class="">When constructing the product, the initializer should be used:</div><div class=""> .Library(name: "FooLib", targets: ["Foo", "Utility"])</div><div class=""><br class=""></div><div class="">And while referring to the product, like in target dependency, factory method should be used:</div><div class=""> Target(name: "Foo", dependencies: [.product(name: "FooLib")])<br class=""></div><div class=""> </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="auto" class=""><div class=""><div class="gmail-h5"><blockquote type="cite" class=""><div class=""><span class=""> Under this rule, an entity having an identity, will use a type initializer</span><br class=""><span class=""> and everything else will use factory methods. `Package`, `Target` and</span><br class=""><span class=""> `Product` are identities. However, a product referenced in a target</span><br class=""><span class=""> dependency is not an identity.</span><br class=""><span class=""></span><br class=""><span class=""> This means the `Product` enum should be converted into an identity. We</span><br class=""><span class=""> propose to introduce a `Product` class with two subclasses: `Executable`</span><br class=""><span class=""> and `Library`. These subclasses will be nested inside `Product` class</span><br class=""><span class=""> instead of being a top level declaration in the module. The major</span><br class=""><span class=""> advantage of nesting is that we get a namespace for products and it is easy</span><br class=""><span class=""> to find all the supported products when the product types grows to a large</span><br class=""><span class=""> number. A downside of nesting is that the product initializers will have to</span><br class=""><span class=""> used with the dot notation (e.g.: `.Executable(name: "tool", targets:</span><br class=""><span class=""> ["tool"])`) which is a little awkward because we expect factory methods to</span><br class=""><span class=""> use the dots.</span><br class=""><span class=""></span><br class=""><span class=""> They will be defined as follow:</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> /// Represents a product.</span><br class=""><span class=""> class Product {</span><br class=""><span class=""></span><br class=""><span class=""> /// The name of the product.</span><br class=""><span class=""> let name: String</span><br class=""><span class=""></span><br class=""><span class=""> /// The names of the targets in this product.</span><br class=""><span class=""> let targets: [String]</span><br class=""><span class=""></span><br class=""><span class=""> private init(name: String, targets: [String]) {</span><br class=""><span class=""> <a href="http://self.name/" target="_blank" class="">self.name</a><span class="Apple-converted-space"> </span>= name</span><br class=""><span class=""> self.targets = targets</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class=""> /// Represents an executable product.</span><br class=""><span class=""> final class Executable: Product {</span><br class=""><span class=""></span><br class=""><span class=""> /// Creates an executable product with given name and targets.</span><br class=""><span class=""> override init(name: String, targets: [String])</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class=""> /// Represents a library product.</span><br class=""><span class=""> final class Library: Product {</span><br class=""><span class=""> /// The type of library product.</span><br class=""><span class=""> enum LibraryType: String {</span><br class=""><span class=""> case `static`</span><br class=""><span class=""> case `dynamic`</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class=""> /// The type of the library.</span><br class=""><span class=""> ///</span><br class=""><span class=""> /// If the type is unspecified, package manager will automatically choose a type.</span><br class=""><span class=""> let type: LibraryType?</span><br class=""><span class=""></span><br class=""><span class=""> /// Creates a library product.</span><br class=""><span class=""> init(name: String, type: LibraryType? = nil, targets: [String])</span><br class=""><span class=""> }</span><br class=""><span class=""> }</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> <details></span><br class=""><span class=""> <summary>View example</summary></span><br class=""><span class=""> <p></span><br class=""><span class=""></span><br class=""><span class=""> Example:</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> let package = Package(</span><br class=""><span class=""> name: "Foo",</span><br class=""><span class=""> target: [</span><br class=""><span class=""> Target(name: "Foo", dependencies: ["Utility"]),</span><br class=""><span class=""> Target(name: "tool", dependencies: ["Foo"]),</span><br class=""><span class=""> ],</span><br class=""><span class=""> products: [</span><br class=""><span class=""> .Executable(name: "tool", targets: ["tool"]),<span class="Apple-converted-space"> </span></span><br class=""><span class=""> .Library(name: "Foo", targets: ["Foo"]),<span class="Apple-converted-space"> </span></span><br class=""><span class=""> .Library(name: "FooDy", type: .dynamic, targets: ["Foo"]),<span class="Apple-converted-space"> </span></span><br class=""><span class=""> ]</span><br class=""><span class=""> )</span><br class=""><span class=""> ```</span><br class=""><span class=""> </p></details></span><br class=""></div></blockquote><div class=""><br class=""></div></div></div><div class="">This API looks very weird: the leading dog is usually used for enum cases and static members. Using it with a type means that the capitalization looks very out of place.</div><div class=""><div class="gmail-h5"><br class=""></div></div></div></blockquote><div class=""><br class=""></div><div class="">Yes, as mentioned in the proposal we think the dot and capitalization following it looks out of place here but we really think that the products should be under a namespace because the types supported product might grow to a substantial number in future. Adding namespace using nesting solves this issue nicely. </div><div class=""><br class=""></div><div class="">Another advantage would be getting free autocomplete support for products in IDEs or text editors which supports SourceKit. Right now there is none which supports autocomplete for the manifest file but since SourceKit is cross platform, it should be possible to create a plugin in future.</div><div class=""> </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="auto" class=""><div class=""><div class="gmail-h5"><blockquote type="cite" class=""><div class=""><span class="">* Special syntax for version initializers.</span><br class=""><span class=""></span><br class=""><span class=""> A simplified summary of what is commonly supported in other package managers:</span><br class=""><span class=""></span><br class=""><span class=""> | Package Manager | x-ranges | tilde (`~` or `~>`) | caret (`^`) |</span><br class=""><span class=""> |-----------------|--------<wbr class="">-------|----------------------<wbr class="">---|---------------|</span><br class=""><span class=""> | npm | Supported | Allows patch-level changes if a minor version is specified on the comparator. Allows minor-level changes if not. | patch and minor updates |</span><br class=""><span class=""> | Cargo | Supported | Same as above | Same as above |</span><br class=""><span class=""> | CocoaPods | Not supported | Same as above | Not supported |</span><br class=""><span class=""> | Carthage | Not supported | patch and minor updates | Not supported |</span><br class=""><span class=""></span><br class=""><span class=""> Some general observations:</span><br class=""><span class=""></span><br class=""><span class=""> * Every package manager we looked at for this supports the tilde `~` operator in some form.</span><br class=""><span class=""> * The widely accepted suggestion on how to constrain your versions is "use</span><br class=""><span class=""> `~>`, it does the right thing".</span><br class=""><span class=""> * It's not clear to us why this has so much traction as "the right thing", as it can</span><br class=""><span class=""> prevent upgrades that should be compatible (one minor version to next minor version).</span><br class=""><span class=""> * Most users may not really understand `~`, and just use it per recommendations.</span><br class=""><span class=""> See e.g. how Google created a [6-minute instructional video](<a href="https://www.youtube.com/watch?v=x4ARXyovvPc" target="_blank" class="">https://www.youtube.<wbr class="">com/watch?v=x4ARXyovvPc</a>)</span><br class=""><span class=""> about this operator for CocoaPods.</span><br class=""><span class=""> * A lot of people even explicitly set a single exact version simply because</span><br class=""><span class=""> they don't know better. This leads to "dependency hell" (unresolvable dependencies</span><br class=""><span class=""> due to conflicting requirements for a package in the dependency graph).</span><br class=""><span class=""> * The Swift Package Manager will probably have many novice users, because it</span><br class=""><span class=""> comes built-in to Swift.</span><br class=""><span class=""> * We think caret `^` has the right behaviour most of the time. That is, you</span><br class=""><span class=""> should be able to specify a minimum version, and you should be willing to let</span><br class=""><span class=""> your package use anything after that up to the next major version. This policy</span><br class=""><span class=""> works if packages correctly follow semantic versioning, and it prevents "dependency</span><br class=""><span class=""> hell" by expressing permissive constraints.</span><br class=""><span class=""> * We also think caret `^` is syntactically non-obvious, and we'd prefer a syntax</span><br class=""><span class=""> that doesn't require reading a manual for novices to understand, even if that</span><br class=""><span class=""> means we break with the syntactic convention established by the other package managers which</span><br class=""><span class=""> support caret `^`.</span><br class=""><span class=""> * We'd like a convenient syntax for caret `^`, but to still support the use</span><br class=""><span class=""> case that tilde `~` is used for; but tilde `~` (or a single exact version) should</span><br class=""><span class=""> be less convenient than caret `^`, to encourge permissive dependency constraints.</span><br class=""><span class=""></span><br class=""><span class=""> What we propose:</span><br class=""><span class=""></span><br class=""><span class=""> * We will introduce a factory method which takes a lower bound version and</span><br class=""><span class=""> forms a range that goes upto the next major version (i.e. caret).</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> // 1.0.0 ..< 2.0.0</span><br class=""><span class=""> .package(url: "/SwiftyJSON", from: "1.0.0"),</span><br class=""><span class=""></span><br class=""><span class=""> // 1.2.0 ..< 2.0.0</span><br class=""><span class=""> .package(url: "/SwiftyJSON", from: "1.2.0"),</span><br class=""><span class=""></span><br class=""><span class=""> // 1.5.8 ..< 2.0.0</span><br class=""><span class=""> .package(url: "/SwiftyJSON", from: "1.5.8"),</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> * We will introduce a factory method which takes `VersionSetSpecifier`, to</span><br class=""><span class=""> conveniently specify common ranges.</span><br class=""><span class=""></span><br class=""><span class=""> `VersionSetSpecifier` is an enum defined as follows:</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> enum VersionSetSpecifier {</span><br class=""><span class=""> case exact(Version)</span><br class=""><span class=""> case range(Range<Version>)</span><br class=""><span class=""></span><br class=""><span class=""> /// Creates a specifier for an exact version.</span><br class=""><span class=""> static func only(_ version: Version) -> VersionSetSpecifier</span><br class=""><span class=""></span><br class=""><span class=""> /// Creates a specified for a range starting at the given lower bound</span><br class=""><span class=""> /// and going upto next major version.</span><br class=""><span class=""> static func uptoNextMajor(_ version: Version) -> VersionSetSpecifier</span><br class=""><span class=""></span><br class=""><span class=""> /// Creates a specified for a range starting at the given lower bound</span><br class=""><span class=""> /// and going upto next minor version.</span><br class=""><span class=""> static func uptoNextMinor(_ version: Version) -> VersionSetSpecifier</span><br class=""><span class=""> }</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> Examples:</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> // 1.5.8 ..< 2.0.0</span><br class=""><span class=""> .package(url: "/SwiftyJSON", .uptoNextMajor("1.5.8")),</span><br class=""><span class=""></span><br class=""><span class=""> // 1.5.8 ..< 1.6.0</span><br class=""><span class=""> .package(url: "/SwiftyJSON", .uptoNextMinor("1.5.8")),</span><br class=""><span class=""></span><br class=""><span class=""> // 1.5.8</span><br class=""><span class=""> .package(url: "/SwiftyJSON", .only("1.5.8")),</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> * This will also give us ability to add more complex features in future:</span><br class=""><span class=""></span><br class=""><span class=""> Examples:</span><br class=""><blockquote type="cite" class=""><span class="">Note that we're not actually proposing these as part of this proposal.</span><br class=""></blockquote><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> .package(url: "/SwiftyJSON", .uptoNextMajor("1.5.8").<wbr class="">excluding("1.6.4")),</span><br class=""><span class=""></span><br class=""><span class=""> .package(url: "/SwiftyJSON", .only("1.5.8", "1.6.3")),</span><br class=""><span class=""></span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> * We will introduce a factory method which takes `Range<Version>`, to specify</span><br class=""><span class=""> arbitrary open range.</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> // Constraint to an arbitrary open range.</span><br class=""><span class=""> .package(url: "/SwiftyJSON", "1.2.3"..<"1.2.6"),</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> * We will introduce a factory method which takes `ClosedRange<Version>`, to specify</span><br class=""><span class=""> arbitrary closed range.</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> // Constraint to an arbitrary closed range.</span><br class=""><span class=""> .package(url: "/SwiftyJSON", "1.2.3"..."1.2.8"),</span><br class=""><span class=""> ```</span><br class=""><span class=""></span><br class=""><span class=""> * We will remove all of the current factory methods:</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> // Constraint to a major version.</span><br class=""><span class=""> .Package(url: "/SwiftyJSON", majorVersion: 1),</span><br class=""><span class=""></span><br class=""><span class=""> // Constraint to a major and minor version.</span><br class=""><span class=""> .Package(url: "/SwiftyJSON", majorVersion: 1, minor: 2),</span><br class=""><span class=""></span><br class=""><span class=""> // Constraint to an exact version.</span><br class=""><span class=""> .Package(url: "/SwiftyJSON", "1.2.3"),</span><br class=""><span class=""></span><br class=""><span class=""> // Constraint to an arbitrary range.</span><br class=""><span class=""> .Package(url: "/SwiftyJSON", versions: "1.2.3"..<"1.2.6"),</span><br class=""><span class=""></span><br class=""><span class=""> // Constraint to an arbitrary closed range.</span><br class=""><span class=""> .Package(url: "/SwiftyJSON", versions: "1.2.3"..."1.2.8"),</span><br class=""><span class=""> ```</span><br class=""></div></blockquote><div class=""><br class=""></div></div></div><div class="">I'm ver happy with the versioning part of this proposal :-)</div><span class="gmail-"><br class=""></span></div></blockquote><div class=""><br class=""></div><div class="">Great! </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="auto" class=""><span class="gmail-"><blockquote type="cite" class=""><div class=""><span class="">* Adjust order of parameters on `Package` class:</span><br class=""><span class=""></span><br class=""><span class=""> We propose to reorder the parameters of `Package` class to: `name`,</span><br class=""><span class=""> `pkgConfig`, `products`, `dependencies`, `targets`, `compatibleSwiftVersions`.</span><br class=""><span class=""></span><br class=""><span class=""> The rationale behind this reorder is that the most interesting parts of a</span><br class=""><span class=""> package are its product and dependencies, so they should be at the top.</span><br class=""><span class=""> Targets are usually important during development of the package. Placing</span><br class=""><span class=""> them at the end keeps it easier for the developer to jump to end of the</span><br class=""><span class=""> file to access them. Note that the compatibleSwiftVersions property will likely</span><br class=""><span class=""> be removed once we support Build Settings, but that will be discussed in a separate proposal.</span><br class=""></div></blockquote><div class=""><br class=""></div></span><div class="">I would have liked this proposal to suggest modifying the API so the order is insignificant. While ordering feels important for me when calling a function or initializer with few arguments (like .package(url: "", from: "1.4.5")), the arguments to the Package function seem like a list of configuration options and shouldn't have a fixed order. My suggestion was to remove all arguments but the name and adds a configuration closure:</div><div class=""><br class=""></div><div class="">let package = Package(name: "paper") {</div><div class=""> <span class="Apple-converted-space"> </span>$0.products = [...]</div><div class=""> <span class="Apple-converted-space"> </span>$0.dependencies = [...]</div><div class="">}</div><br class=""></div></blockquote><div class=""><br class=""></div><div class="">It will be possible to avoid using the initializer because all the properties will be made mutable. However I think if majority of packages uses the initializer and thus have a consistent ordering, it will be easier for other developers to read manifests on Github (or similar).</div><div class=""><br class=""></div><div class="">PS: The closure syntax can also be added using extension in the manifest itself if someone really wants to use it.</div><div class=""> </div><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="auto" class=""><blockquote type="cite" class=""><div class=""><div class=""><div class="gmail-h5"><span class=""> <details></span><br class=""><span class=""> <summary>View example</summary></span><br class=""><span class=""> <p></span><br class=""><span class=""></span><br class=""><span class=""> Example:</span><br class=""><span class=""></span><br class=""><span class=""> ```swift</span><br class=""><span class=""> let package = Package(</span><br class=""><span class=""> name: "Paper",</span><br class=""><span class=""> products: [</span><br class=""><span class=""> .Executable(name: "tool", targets: ["tool"]),</span><br class=""><span class=""> .Libary(name: "Paper", type: .static, targets: ["Paper"]),</span><br class=""><span class=""> .Libary(name: "PaperDy", type: .dynamic, targets: ["Paper"]),</span><br class=""><span class=""> ],</span><br class=""><span class=""> dependencies: [</span><br class=""><span class=""> .package(url: "<a href="http://github.com/SwiftyJSON" target="_blank" class="">http://github.com/SwiftyJSON</a>"<wbr class="">, from: "1.2.3"),</span><br class=""><span class=""> .package(url: "../CHTTPParser", .uptoNextMinor("2.2.0")),</span><br class=""><span class=""> .package(url: "<a href="http://some/other/lib" target="_blank" class="">http://some/other/lib</a>", .only("1.2.3")),</span><br class=""><span class=""> ]</span><br class=""><span class=""> targets: [</span><br class=""><span class=""> Target(</span><br class=""><span class=""> name: "tool",</span><br class=""><span class=""> dependencies: [</span><br class=""><span class=""> "Paper",</span><br class=""><span class=""> "<wbr class="">SwiftyJSON"</span><br class=""><span class=""> ]),</span><br class=""><span class=""> Target(</span><br class=""><span class=""> name: "Paper",</span><br class=""><span class=""> dependencies: [</span><br class=""><span class=""> "Basic",</span><br class=""><span class=""> .target(<wbr class="">name: "Utility"),</span><br class=""><span class=""> .product(<wbr class="">name: "CHTTPParser"),</span><br class=""><span class=""> ])</span><br class=""><span class=""> ]</span><br class=""><span class=""> )</span><br class=""><span class=""> ```</span><br class=""><span class=""> </p></details></span><br class=""><span class=""></span><br class=""><span class="">* Eliminate exclude in future (via custom layouts feature).</span><br class=""><span class=""></span><br class=""><span class=""> We expect to remove the `exclude` property after we get support for custom</span><br class=""><span class=""> layouts. The exact details will be in the proposal of that feature.</span><br class=""><span class=""></span><br class=""><span class="">## Impact on existing code</span><br class=""><span class=""></span><br class=""><span class="">The above changes will be implemented only in the new Package Description v4</span><br class=""><span class="">library. The v4 runtime library will release with Swift 4 and packages will be</span><br class=""><span class="">able to opt-in into it as described by</span><br class=""><span class="">[SE-0152](<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0152-package-manager-tools-version.md" target="_blank" class="">https://github.com/<wbr class="">apple/swift-evolution/blob/<wbr class="">master/proposals/0152-package-<wbr class="">manager-tools-version.md</a>).</span><br class=""><span class=""></span><br class=""><span class="">There will be no automatic migration feature for updating the manifests from v3</span><br class=""><span class="">to v4. To indicate the replacements of old APIs, we will annotate them using</span><br class=""><span class="">the `@unavailable` attribute where possible. Unfortunately, this will not cover</span><br class=""><span class="">all the changes for e.g. rename of the target dependency enum cases.</span><br class=""><span class=""></span><br class=""><span class="">All new packages created with `swift package init` command in Swift 4 tools</span><br class=""><span class="">will by default to use the v4 manifest. It will be possible to switch to v3</span><br class=""><span class="">manifest version by changing the tools version using `swift package</span><br class=""><span class="">tools-version --set 3.1`. However, the manifest will needed to be adjusted to</span><br class=""><span class="">use the older APIs manually.</span><br class=""><span class=""></span><br class=""><span class="">Unless declared in the manifest, existing packages automatically default</span><br class=""><span class="">to the Swift 3 minimum tools version; since the Swift 4 tools will also include</span><br class=""><span class="">the v3 manifest API, they will build as expected.</span><br class=""><span class=""></span><br class=""><span class="">A package which needs to support both Swift 3 and Swift 4 tools will need to</span><br class=""><span class="">stay on the v3 manifest API and support the Swift 3 language version for its</span><br class=""><span class="">sources, using the API described in the proposal</span><br class=""><span class="">[SE-0151](<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0151-package-manager-swift-language-compatibility-version.md" target="_blank" class="">https://github.com/<wbr class="">apple/swift-evolution/blob/<wbr class="">master/proposals/0151-package-<wbr class="">manager-swift-language-<wbr class="">compatibility-version.md</a>).</span><br class=""><span class=""></span><br class=""><span class="">An existing package which wants to use the new v4 manifest APIs will need to bump its</span><br class=""><span class="">minimum tools version to 4.0 or later using the command `$ swift package tools-version</span><br class=""><span class="">--set-current`, and then modify the manifest file with the changes described in</span><br class=""><span class="">this proposal.</span><br class=""><span class=""></span><br class=""><span class="">## Alternatives considered</span><br class=""><span class=""></span><br class=""><span class="">* Add variadic overloads.</span><br class=""><span class=""></span><br class=""><span class=""> Adding variadic overload allows omitting parenthesis which leads to less</span><br class=""><span class=""> cognitive load on eyes, especially when there is only one value which needs</span><br class=""><span class=""> to be specified. For e.g.:</span><br class=""><span class=""></span><br class=""><span class=""> Target(name: "Foo", dependencies: "Bar")</span><br class=""><span class=""></span><br class=""><span class=""> might looked better than:</span><br class=""><span class=""></span><br class=""><span class=""> Target(name: "Foo", dependencies: ["Bar"])</span><br class=""><span class=""></span><br class=""><span class=""> However, plurals words like `dependencies` and `targets` imply a collection</span><br class=""><span class=""> which implies brackets. It also makes the grammar wrong. Therefore, we</span><br class=""><span class=""> reject this option.</span><br class=""><span class=""></span><br class=""><span class="">* Version exclusion.</span><br class=""><span class=""></span><br class=""><span class=""> It is not uncommon to have a specific package version break something, and</span><br class=""><span class=""> it is undesirable to "fix" this by adjusting the range to exclude it</span><br class=""><span class=""> because this overly constrains the graph and can prevent picking up the</span><br class=""><span class=""> version with the fix.</span><br class=""><span class=""></span><br class=""><span class=""> This is desirable but it should be proposed separately.</span><br class=""><span class=""></span><br class=""><span class="">* Inline package declaration.</span><br class=""><span class=""></span><br class=""><span class=""> We should probably support declaring a package dependency anywhere we</span><br class=""><span class=""> support spelling a package name. It is very common to only have one target</span><br class=""><span class=""> require a dependency, and annoying to have to specify the name twice.</span><br class=""><span class=""></span><br class=""><span class=""> This is desirable but it should be proposed separately.</span><br class=""><span class=""></span><br class=""><span class="">______________________________<wbr class="">_________________</span><br class=""></div></div><span class="">swift-evolution mailing list</span><br class=""><span class=""><a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a></span><br class=""><span class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank" class="">https://lists.swift.org/<wbr class="">mailman/listinfo/swift-<wbr class="">evolution</a></span><br class=""></div></blockquote><br class=""><div class="">One thing that still really bothers me about the API is the inconsistency in leading dots and capitalization. Should a novice (or an expert) have to remember the following different writings:</div><div class=""><span class="gmail-"><br class=""><div class=""><font class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">Target(name: "Foo", dependencies: ["Utility"])</span></font></div></span><span class="gmail-"><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">.package(url: "<a href="http://github.com/SwiftyJSON" target="_blank" class="">http://github.com/SwiftyJSON</a>"<wbr class="">, from: "1.2.3")</span></div></span></div><div class=""><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">.Library(name: "Paper", type: .static, targets: ["Paper"])</span></div></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">I understand the arguments brought forward in the proposal. But from a package author point of view, it's not obvious why Target is capitalized, why package is lowercase with a leading dot and why Library is capitalized with a leading dot. It looks confusing and inconsistent, which makes it less pleasant to read and harder to remember.</span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">Could we push for more consistency by having everything b lowercased with a leading dot? It does force us to introduce a Target factory method, to revert SystemPackageProvider back to an enum, to revert products back to an enum. But I think it's worth it.</span></div></div></blockquote></div><br class=""></div></div><div class="gmail_extra">It is true that it might not be obvious when to use what initially, but we think this is a good rule to create a distinction between constructing things and referring to things. A downside of lowercasing everything would be: it might seem like the references support all the parameters that constructor supports. e.g. .target(name: "Foo", dependencies: [ .target(name: "Bar", dependencies: ["Baz"]) ])</div><div class="gmail_extra"><br class=""></div><div class="gmail_extra"><br class=""></div><div class="gmail_extra"><br class=""></div><div class="gmail_extra"><br class=""></div><div class="gmail_extra"><br class=""></div><div class="gmail_extra"><br class=""></div></div><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">_______________________________________________</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">swift-build-dev mailing list</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><a href="mailto:swift-build-dev@swift.org" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">swift-build-dev@swift.org</a><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><a href="https://lists.swift.org/mailman/listinfo/swift-build-dev" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">https://lists.swift.org/mailman/listinfo/swift-build-dev</a></div></blockquote></div><br class=""></div></div></blockquote></body></html>