diff --git a/Sources/PackageManagerDocs/Documentation.docc/GettingStarted.md b/Sources/PackageManagerDocs/Documentation.docc/GettingStarted.md index d4d59cd87b4..f1f930724bc 100644 --- a/Sources/PackageManagerDocs/Documentation.docc/GettingStarted.md +++ b/Sources/PackageManagerDocs/Documentation.docc/GettingStarted.md @@ -4,190 +4,398 @@ Learn to create and use Swift packages. ## Overview -To provide a more complete look at what the Swift Package Manager can do, the following example consists of three interdependent packages: +To provide a more complete look at what the Swift Package Manager can do, the following example consists of three interdependent packages, which you can explore: - [PlayingCard](https://github.com/apple/example-package-playingcard) - Defines PlayingCard, Suit, and Rank types. - [DeckOfPlayingCards](https://github.com/apple/example-package-deckofplayingcards) - Defines a Deck type that shuffles and deals an array of PlayingCard values. - [Dealer](https://github.com/apple/example-package-dealer) - Defines an executable that creates a DeckOfPlayingCards, shuffles it, and deals the first 10 cards. -### Creating a Library Package +This guide shows you how to create a library that uses another library as a dependency, use the Package Manager to build and test your code, and show you how you might release your own package. -We'll start by creating a target representing a playing card in a standard 52-card deck. -The [PlayingCard](https://github.com/apple/example-package-playingcard) target defines the PlayingCard type, which consists of a Suit enumeration value (Clubs, Diamonds, Hearts, Spades) and a Rank enumeration value (Ace, Two, Three, …, Jack, Queen, King). +### Creating a library package -```swift -public enum Rank: Int { - case two = 2 - case three, four, five, six, seven, eight, nine, ten - case jack, queen, king, ace -} +This example starts with using an existing package that represents a playing card in a standard 52-card deck. +The package, [PlayingCard](https://github.com/apple/example-package-playingcard), is available through git and provides a library (`PlayingCard`) that this guide uses and expands upon. -public enum Suit: String { - case spades, hearts, diamonds, clubs -} +This example creates the library `DeckOfPlayingCards` that provides a type that represents a deck of cards and common interactions with the deck, including shuffling, counting, and dealing. +To start a new library, first make an empty directory, and within it run the `swift package init` command to initialize a new package: -public struct PlayingCard { - let rank: Rank - let suit: Suit -} +```bash +mkdir DeckOfPlayingCards +cd DeckOfPlayingCards +swift package init ``` -By convention, a target includes any source files located in the `Sources/` directory. +The default template that Package Manager creates is a library, which you can control with the `--type` parameter. +The name of the package defaults to the name of the directory you created, and can be overridden with the `--name` parameter to the `swift package init` command. +For the complete details on options for this command, see the [swift package init documentation](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/packageinit). -``` -example-package-playingcard +The template generates a structure of files in the directory that follow the defaults for a Swift package: + +```bash +DeckOfPlayingCards +├── .gitignore +├── Package.swift ├── Sources -│ └── PlayingCard -│ ├── PlayingCard.swift -│ ├── Rank.swift -│ └── Suit.swift -└── Package.swift +│   └── DeckOfPlayingCards +│   └── DeckOfPlayingCards.swift +└── Tests + └── DeckOfPlayingCardsTests + └── DeckOfPlayingCardsTests.swift ``` -Because the PlayingCard target does not produce an executable, it can be described as a library. -A library is a target that builds a module which can be imported by other packages. -By default, a library module exposes all of the public types and methods declared in source code located in the `Sources/` directory. - -When creating a library package intended for use as a dependency in other projects, the `Package.swift` manifest resides at the top level/root of the package directory structure. - -Run swift build to start the Swift build process. -If everything worked correctly, it compiles the Swift module for PlayingCard. - -> The complete code for the PlayingCard package can be found at [https://github.com/apple/example-package-playingcard](https://github.com/apple/example-package-playingcard). - -### Importing Dependencies - -The [DeckOfPlayingCards package](https://github.com/apple/example-package-playingcard.git) depends in the previous package: It defines a Deck type. - -To use the PlayingCards module, the DeckOfPlayingCards package declares the package as a dependency in its `Package.swift` manifest file. +The template provides a directory to host a single module that is exposed as a library at `Sources/DeckOfPlayingcards`, and a matching directory to host the tests. +The default package structure provides a library that consists of a single target, both of which are named the same as the package: `DeckOfPlayingCards`, and a test target where you can add tests as you develop your code. ```swift -// swift-tools-version:5.10 -import PackageDescription - let package = Package( name: "DeckOfPlayingCards", products: [ - .library(name: "DeckOfPlayingCards", - targets: ["DeckOfPlayingCards"]), - ], - dependencies: [ - .package( - url: "https://github.com/apple/example-package-playingcard.git", - from: "3.0.0"), + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "DeckOfPlayingCards", + targets: ["DeckOfPlayingCards"] + ), ], targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. .target( - name: "DeckOfPlayingCards", - dependencies: [ - .product(name: "PlayingCard", - package: "example-package-playingcard") - ]), + name: "DeckOfPlayingCards" + ), .testTarget( name: "DeckOfPlayingCardsTests", - dependencies: [ - .target(name: "DeckOfPlayingCards") - ]), + dependencies: ["DeckOfPlayingCards"] + ), ] ) ``` -Each dependency specifies a source URL and version requirements. -The source URL is a URL accessible to the current user that resolves to a Git repository. -The package manager uses the version requirements, which follow Semantic Versioning (SemVer) conventions, to determine which Git tag to check out and use to build the dependency. -The requirement for the PlayingCard dependency uses the most recent version with a major version equal to 3. +### Adding a dependency -When you run the swift build command, the Package Manager downloads all of the dependencies, compiles them, and links them to the package module. -This allows DeckOfPlayingCards to access the public members of its dependent modules with import statements. - -You can see the downloaded sources in the `.build/checkouts` directory at the root of your project, and intermediate build products in the `.build` directory at the root of your project. +To add the dependency on the library that provides a PlayingCard so that this example can use it, use the `add-dependency` command, providing the location where the package is hosted. -> The complete code for the DeckOfPlayingCards package can be found at [https://github.com/apple/example-package-deckofplayingcards](https://github.com/apple/example-package-deckofplayingcards). +```bash +swift package add-dependency https://github.com/apple/example-package-playingcard --from 3.0.0 +``` -### Resolving transitive dependencies +Each dependency within a package specifies a source URL and version requirements. +The source URL is a URL accessible to the current user that resolves to a Git repository. +The package manager uses the version requirements, which follow Semantic Versioning (SemVer) conventions, to determine which Git tag to check out and use to build the dependency. -With everything else in place, now you can build the Dealer executable. -The Dealer executable depends on the `DeckOfPlayingCards` package, which in turn depends on the `PlayingCard` package. -However, because the package manager automatically resolves transitive dependencies, you only need to declare the `DeckOfPlayingCards` package as a dependency. +The above example uses the parameter `--from 3.0.0`, to indicate the version requirements for the dependency. +The `from` constrains the dependency chosen to a minimum of `3.0.0` and extending up to the highest minor and patch release available from the git repository. +Package Manager uses tags, interpreting them as semantic versions, to determine the versions available. +The command updated the `Package.swift` manifest file, adding the `dependencies` section: ```swift -// swift-tools-version:5.10 - -import PackageDescription - let package = Package( - name: "dealer", - platforms: [ - .macOS(.v11) - ], + name: "DeckOfPlayingCards", products: [ - .executable(name: "dealer", - targets: ["dealer"]), + ... ], dependencies: [ - .package( - url: "https://github.com/apple/example-package-deckofplayingcards.git", - from: "3.0.0"), - .package( - url: "https://github.com/apple/swift-argument-parser.git", - from: "0.4.4"), + .package(url: "https://github.com/apple/example-package-playingcard", from: "3.0.0"), ], targets: [ - .executableTarget( - name: "dealer", - dependencies: [ - .product(name: "DeckOfPlayingCards", - package: "example-package-deckofplayingcards"), - .product(name: "ArgumentParser", - package: "swift-argument-parser") - ]), - .testTarget( - name: "DealerTests", - dependencies: [ - .byName(name: "dealer") - ]), + ... ] ) +``` + +Adding the dependency makes it available to the package, but doesn't include it by default into the targets within the package. +For example, if you are attempting to build the package using `swift build`, the build would succeed, but provide the warning: +```bash +warning: 'deckofplayingcards': dependency 'example-package-playingcard' is not used by any target ``` -Swift requires that a source file imports the modules for any types that are referenced in code. -In the Dealer module's `Deal.swift` file, the code imports `DeckOfPlayingCards` and `PlayingCard` to use types from each. +If you attempted to use the library in the source, for example `import PlayingCard`, the compiler reports `No such module 'PlayingCard'`. + +When you run the swift build command, the Package Manager downloads all of the dependencies, compiles them, and links them to the package module based on the Package.swift manifest. +You can see the downloaded sources in the `.build/checkouts` directory at the root of your project, and intermediate build products in the `.build` directory at the root of your project. + +You also need to include the dependency on the target where you want to use the library. +Use the command `add-target-dependency` to add the dependency to the target in this package. + +```bash +swift package add-target-dependency PlayingCard DeckOfPlayingCards --package example-package-playingcard +``` + +This allows DeckOfPlayingCards to access the public members of its dependent modules with import statements. +The above command updates the `Package.swift` manifest so that the DeckOfPlayingCards target now references the dependency: ```swift -import DeckOfPlayingCards +.target( + name: "DeckOfPlayingCards", + dependencies: [ + .target(name: "PlayingCard"), + ] +), +``` + +With this update in place, when you run `swift build` the package compiles without warnings. + +### Implement the library + +The template provides an empty file for the source for your package. Remove the content, add `import PlayingCard`, and your implementation. +The following code provides an example implementation: + +```swift +import PlayingCard -var deck = Deck.standard52CardDeck() -deck.shuffle() +/// A model for shuffling and dealing a deck of playing cards. +/// +/// The playing card deck consists of 52 individual cards in four suites: spades, hearts, diamonds, and clubs. +/// There are 13 ranks from two to ten, then jack, queen, king, and ace. +public struct Deck: Equatable { + fileprivate var cards: [PlayingCard] -for count in counts { + /// Returns a deck of playing cards. + public static func standard52CardDeck() -> Deck { var cards: [PlayingCard] = [] + for rank in Rank.allCases { + for suit in Suit.allCases { + cards.append(PlayingCard(rank: rank, suit: suit)) + } + } + + return Deck(cards) + } + + /// Creates a deck of playing cards. + public init(_ cards: [PlayingCard]) { + self.cards = cards + } + + /// Randomly shuffles a deck of playing cards. + public mutating func shuffle() { + cards.shuffle() + } + + /// Deals a card from the deck. + /// + /// The function returns the last card in the deck. + public mutating func deal() -> PlayingCard? { + guard !cards.isEmpty else { return nil } + + return cards.removeLast() + } + + /// The number of remaining cards in the deck. + public var count: Int { + cards.count + } +} + +// MARK: - ExpressibleByArrayLiteral - for _ in 0.. The complete code for the DeckOfPlayingCards example package can be found at [https://github.com/apple/example-package-deckofplayingcards](https://github.com/apple/example-package-deckofplayingcards). + +### Share your package + +You can use this package from other Swift packages locally, or share the package through a git hosting provider. +When you want to release your own package, create a git tag that matches the major, minor, and patch versions of a semantic version and push the tag to your git hosting provider. +For example, to tag the package with the semantic version `0.1`, which indicates that it's an initial minor release, use the tag `0.1.0`. + +For more information about sharing packages, see [Releasing and publishing a Swift package](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/releasingpublishingapackage). + +### Resolving transitive dependencies + +The Package Manager resolves dependencies for the package you're using and all of its transitive dependencies. +Another example package, `Dealer`, illustrates how this works by using the example of the package this guide created. +You can explore the example package online at [https://github.com/swiftlang/example-package-dealer/](https://github.com/swiftlang/example-package-dealer/), or download it locally to explore: + +```bash +git clone https://github.com/swiftlang/example-package-dealer.git +cd example-package-dealer ``` -You can build and run the complete example by downloading the source code of the Dealer project from GitHub and running the following commands: +The dealer package includes an additional dependency to [Swift Argument Parser](https://github.com/apple/swift-argument-parser), a package that helps parse arguments for command-line applications. + + +To see the package resolution and choices, run the command `swift package resolve`. ```bash -$ git clone https://github.com/apple/example-package-dealer.git -$ cd example-package-dealer -$ swift run dealer +Fetching https://github.com/swiftlang/example-package-deckofplayingcards.git +Fetching https://github.com/apple/swift-argument-parser.git from cache +Fetched https://github.com/swiftlang/example-package-deckofplayingcards.git from cache (0.41s) +Fetched https://github.com/apple/swift-argument-parser.git from cache (0.58s) +Computing version for https://github.com/swiftlang/example-package-deckofplayingcards.git +Computed https://github.com/swiftlang/example-package-deckofplayingcards.git at 4.0.0 (0.94s) +Fetching https://github.com/apple/example-package-playingcard.git from cache +Fetched https://github.com/apple/example-package-playingcard.git from cache (0.30s) +Computing version for https://github.com/apple/example-package-playingcard.git +Computed https://github.com/apple/example-package-playingcard.git at 4.0.0 (0.66s) +Computing version for https://github.com/apple/swift-argument-parser.git +Computed https://github.com/apple/swift-argument-parser.git at 1.6.1 (0.39s) +Creating working copy for https://github.com/apple/swift-argument-parser.git +Working copy of https://github.com/apple/swift-argument-parser.git resolved at 1.6.1 +Creating working copy for https://github.com/apple/example-package-playingcard.git +Working copy of https://github.com/apple/example-package-playingcard.git resolved at 4.0.0 +Creating working copy for https://github.com/swiftlang/example-package-deckofplayingcards.git +Working copy of https://github.com/swiftlang/example-package-deckofplayingcards.git resolved at 4.0.0 ``` +This process happens automatically when you run `swift build` or `swift test`, making the dependencies available for your project. +As with the previous package, you can build this package with `swift build`, and run and see the tests for the package using the command `swift test`. + +As the dealer package provides a command-line executable, you can also run the executable built by the package using `swift run`: + +```bash +[1/1] Planning build +Building for debugging... +[1/1] Write swift-version-2C315BDEC41BFF30.txt +Build of product 'dealer' complete! (0.13s) +Error: Missing expected argument ' ...' + +OVERVIEW: Shuffles a deck of playing cards and deals a number of cards. + +For each count argument, prints a line of tab-delimited cards to stdout, +or if there aren't enough cards remaining, +prints "Not enough cards" to stderr and exits with a nonzero status. + +USAGE: dealer ... + +ARGUMENTS: + The number of cards to deal at a time. + +OPTIONS: + -h, --help Show help information. +``` + +Specify the name of the executable along with any required arguments to try it out, for example `swift run dealer 5`: + +```bash +Building for debugging... +[1/1] Write swift-version-2C315BDEC41BFF30.txt +Build of product 'dealer' complete! (0.07s) +♢ J ♢ 3 ♢ 7 ♣︎ 5 ♡ 7 +``` + +The build product from the package is also available in the `.build` directory by default, where you can also execute the tool directly. +For example, the debug build (the default) for the dealer package is available at `.build/debug/dealer`. +You can invoke that from the terminal: `.build/debug/dealer 5` + +```bash +♠︎ 6 ♡ 4 ♣︎ 4 ♡ A ♡ K +```