diff --git a/Sources/PackageManagerDocs/Documentation.docc/Dependencies/PackageTraits.md b/Sources/PackageManagerDocs/Documentation.docc/Dependencies/PackageTraits.md index 0450fea8632..612c7967f99 100644 --- a/Sources/PackageManagerDocs/Documentation.docc/Dependencies/PackageTraits.md +++ b/Sources/PackageManagerDocs/Documentation.docc/Dependencies/PackageTraits.md @@ -1,30 +1,32 @@ -# Provide configurable packages using traits. +# Providing configurable packages using traits -Define one or more traits to offer default and configurable features for a package. +Define one or more package traits to offer default and configurable features for a package. + +## Overview Swift packages prior to Swift 6.1 offered a non-configurable API surface for each version. -With Swift 6.1, packages may offer traits, which express a configurable API surface for a package. +Starting with Swift 6.1, packages may offer traits, which express a configurable API surface for a package. Use traits to enable additional API beyond the core API of the package. -For example, a trait may enable an experimental API, optional extended functionality that requires additional dependencies, or functionality that isn't critical that a developer may want to enable only in specific circumstances. +For example, a trait may enable an experimental API, optional extended functionality that requires additional dependencies, or functionality that isn't critical that you may want to enable only in specific circumstances. + +Traits that you specify when building a package only activate within the package you're building. +If your package wants to use a trait in a dependent package, it needs to encode the traits it needs in its dependencies. > Note: Traits should never "remove" or disable public API when a trait is enabled. -Within the package, traits express conditional compilation, and may be used to declare additional dependencies that are enabled when that trait is active. +Within the package where a trait is defined, the trait expresses conditional compilation. +Swift Package Manager exposes enabled traits as conditional blocks (for example, `#if YourTrait`) that you can use to conditionally enable imports or different compilation paths in code. -Traits are identified by their names, which are name-spaced within the package that hosts them. +Trait names are namespaced within the package that hosts them - a trait name in one package has no impact on any other package. Trait names must be [valid swift identifiers](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/lexicalstructure#Identifiers) with the addition of the characters of `-` and `+`. -The trait names `default` and `defaults` (regardless of any capitalization) aren't allowed to avoid confusion with the default traits that a package defines. - -Enabled traits are exposed as conditional blocks (for example, `#if YourTrait`) that you can use to conditionally enable imports or different compilation paths in code. - -## Overview +Don't use the trait names `default` or `defaults` (regardless of capitalization) to avoid confusion with the default traits that a package defines. -### Declaring Traits +### Declare traits Create a trait to define a discrete amount of additional functionality, and define it in the [traits](https://docs.swift.org/swiftpm/documentation/packagedescription/package/traits) property of the package manifest. Use [`.default(enabledTraits:)`](https://docs.swift.org/swiftpm/documentation/packagedescription/trait/default(enabledtraits:)) to provide the set of traits that the package uses as a default. -If you don't define a default set of traits to enable, no traits are enabled by default. +If you don't define a default set of traits to enable, Swift Package Manager enables no traits by default. The following example illustrates a single trait, `FeatureA`, that is enabled by default: @@ -37,7 +39,7 @@ traits: [ // ... ``` -Traits may also be used to represent a set of other traits, which allows you to group features together. +Traits can also represent a set of other traits, which allows you to group features together. The following example illustrates defining three traits, and an additional trait (`B-and-C`) that enables both traits `FeatureB` and `FeatureC`: ```swift @@ -46,18 +48,21 @@ traits: [ .trait(name: "FeatureA"), .trait(name: "FeatureB"), .trait(name: "FeatureC"), - .trait(name: "B-and-C", enabledTraits: ["FeatureB", "FeatureC"]). + .trait(name: "B-and-C", enabledTraits: ["FeatureB", "FeatureC"]), .default(enabledTraits: ["FeatureA"]), ], // ... ``` -The traits enabled by default for the example above is `FeatureA`. +For the example above, the default trait is `FeatureA`. > Note: Changing the default set of traits for your package is a major semantic version change if it removes API surface. > Adding additional traits is not a major version change. -#### Mutually Exclusive Traits +Swift Package Manager treats traits as purely additive, and enables traits across all packages within the build graph. +Design your traits such that they enable additional API (and their dependencies, if needed). + +#### Define mutually exclusive traits The package manifest format doesn't support declaring mutually exclusive traits. In the rare case that you need to offer mutually exclusive traits, protect that scenario in code: @@ -70,11 +75,34 @@ In the rare case that you need to offer mutually exclusive traits, protect that > Note: Providing mutually exclusive traits can result in compilation errors when a developer enables the mutually exclusive traits. -### Using traits in your code +### Depend on a package with a trait + +A package dependency that doesn't specify traits in its declaration uses the package with its default traits enabled. +Add one or more traits that the package provides within the `traits` parameter for the package dependency to use that package with those traits enabled. + +The following example shows depending on the `swift-configuration` with both the `defaults` and `YAML` traits enabled: + +```swift +dependencies: [ + .package( + url: "https://github.com/apple/swift-configuration.git", + from: "1.0.0", + traits: [ + .defaults, + "YAML" + ] + ), +] +``` + +> Tip: The traits you define for a dependency are a strict, additive list of the traits that the package provides. +> If you want to use defaults and additional traits, make sure to add `.defaults` to the list of traits you specify. + +### Use traits in your code Use the name of a trait for conditional compilation. Wrap the additional API surface for that trait within a conditional compilation block. -For example, if the trait `FeatureA` is defined and enabled, the compiler see and compile the function `additionalAPI()`: +For example, if the trait `FeatureA` is defined and enabled, the compiler sees and compiles the function `additionalAPI()`: ```swift #if FeatureA @@ -84,7 +112,7 @@ public func additionalAPI() { #endif // FeatureA ``` -### Using a trait to enable conditional dependencies +### Use a trait to enable conditional dependencies You can use a trait to optionally include a dependency, or a dependency with specific traits enabled, to support the functionality you expose with a trait. To do so, add the dependency you need to the manifest's `dependencies` declaration, @@ -137,5 +165,5 @@ With the above example, the following code illustrates wrapping the import with MyAPI.provideExtraFunctionality() // ... } -#endif // MyTrait +#endif // FeatureB ```