# \[Pre-pitch\] SwiftPM Manifest based on Result Builders

**URL:** <https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457>\
**Category:** Package Manager\
**Created:** [November 12, 2021, 10:39pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457 "2021-11-12T22:39:53Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tomerd/32/3402_2.png) [@tomerd](https://forums.swift.org/u/tomerd)\
**Post date:** [November 12, 2021, 10:39pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/1 "2021-11-12T22:39:53Z")

</div>

## Background and motivation

Swift Package Manager (here after SwiftPM) was released when Swift was made open source in 2016. SwiftPM uses a file named `Package.swift` with which users describe the package's source structure, the build artifacts such as any executables or libraries the build produces, and any dependencies on other packages.

SwiftPM’s manifest is a Swift program (a script of sorts) which SwiftPM builds and executes as a separate process in a security sandbox to generate a static data model representing the desired package configuration. Currently, the static representation is based on JSON and the exact format of that JSON is an internal implementation detail. The JSON model is later deserialized and loaded into the parent process memory space, driving SwiftPM’s workflows such as the dependency resolution, build, test , etc.

SwiftPM’s manifest is a simple Swift script, with several requirements and constraints::

1. The manifest must import the `PackageDescription` module to gain access to the relevant APIs required to construct the package model.
2. The manifest must construct a `package` instance of type `Package` which is defined by the `PackageDescription` module.
3. The manifest has access to Swift’s standard library, Dispatch and Foundation but cannot use any other packages or libraries.
4. The security sandbox limits access to network and file system.

Consider the following example of a manifest file:

```swift
import PackageDescription

let package = Package(
    name: "MyPackage",
    products: [
        .executable(
            name: "MyExecutable", 
            target: ["MyExecutable"]
        ),
       .library(
            name: "MyLibrary", 
            targets: ["MyLibrary", "MyDataModel"]
        ),       
    ],
    dependencies: [
        .package(url: "https://git-service.com/foo/some-package", from: "1.0.0"),
        .package(url: "https://git-service.com/foo/some-other-package", from: "1.0.0"),
    ],
    targets: [
        .executableTarget(
            name: "MyExecutable",
            dependencies: [
                "MyDataModel",
                "MyUtilities",
                .product(name: "SomeModule", package: "some-package")                
            ]
        ),
        .target(
            name: "MyLibrary",
            dependencies: [
                "MyDataModel",                
                .product(name: "SomeOtherModule", package: "some-other-package")
            ]
        ),
        .target(
            name: "MyDataModel",
            dependencies: []
        ),
        .target(
            name: "MyUtilities",
            dependencies: []
        ),        
        .target(
            name: "MyTestUtilities",
            dependencies: []
        ),
        testTarget(
            name: "MyExecutableTests",
            dependencies: ["MyExecutable", "MyTestUtilities"]
        ),        
        testTarget(
            name: "MyLibraryTests",
            dependencies: ["MyLibrary"]
        )
    ]
)

```

Breaking down the manifest example above shed lights on several key issues:

1. The manifest defines 2 publicly available build artifacts, an executable named `MyExecutable` and a library named `MyLibrary`. To define these artifacts, the manifest requires definition of two abstractions: a `Target` and a `Product`, having the `Target` representing a source module and the `Product` representing a public build artifact. Worth noting that in many cases, each `Product` points to exactly one `Target`.
2. The manifest also defines 3 internal targets (modules) that are not exposed outside the package: `MyDataModel,` `MyUtilities` and `MyTestUtilities`. The `MyLibrary` product (public build artifact) exports both the `MyLibrary` and `MyDataModel` targets (modules).
3. Finally the manifest defines 2 test targets that are not exposed outside the package: `MyExecutableTests` and `MyLibraryTests`, designed to test the `MyExecutable` and `MyLibrary` targets respectively. The `MyExecutableTests` target also depends on the `MyTestUtilities` target (an internal module).
4. The manifest defines dependencies on two external packages: `https://git-service.com/foo/some-package` and `https://git-service.com/foo/some-other-package`. `some-package` is used by the `MyExecutable` target, while `some-other-package` is used by the `MyLibrary` target.
5. The manifest is a “giant initializer expression" for constructing a `Package` instance. The impact of this design is that it is very hard to get the manifest right when typing it from scratch as mistakes are syntax errors that can be hard to decipher. For the same reason, auto-complete tools struggle with such expression and IDEs offer little help to that end. In practice, the most effective way to write a manifest is to copy/paste a working one and then change it, which is indicative of the usability problem.
6. Worth mentioning that the `Package` instance can be constructed empty, and then built up line by line imperatively which helps mitigate the usability problem mentioned above. That said, the “giant initializer” is the more common form of the manifest, arguably because it looks to be declarative and as such easier to read at a glance.
7. The “giant initializer” design also prevents the use of conditionals such as `#if` which can be useful in some cases, for example multi-platform support (some caveats apply about this usefulness).
8. The “giant initializer” API makes the manifest fairly verbose.

## Proposed solution

At the time the manifest was designed Swift’s [Result Builders](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md) were not part of the language, leading to the “giant initializer” design mentioned above. With Result Builders in place, we can potentially use this technique to solve some of the problems mentioned above.

Consider the following example of a manifest file, replicating the example above with a Result Builders based API:

Alternative 1, script based:

```swift
import PackageManifest

let package = Package()
  .modules {
    Executable("MyExecutable", public: true)
      .include {
        Internal("MyDataModel")
        Internal("MyUtilities")
        External("SomeModule", from: "some-package") 
      }
    Library("MyLibrary", public: true)
      .include {
        Internal("MyDataModel", public: true)
        External("SomeOtherModule", from: "some-other-package") 
      }
    Library("MyDataModel")
    Library("MyUtilities")
    Library("MyTestUtilities")
    Test("MyExecutableTests", for: "MyExecutable")
      .include("MyTestUtilities")
    Test("MyLibraryTests", for: "MyLibrary")
  }
  .dependencies {
    SourceControl(at: "https://git-service.com/foo/some-package", upToNextMajor: "1.0.0")
    SourceControl(at: "https://git-service.com/foo/some-other-package", upToNextMajor: "1.0.0")    
  }

```

Breaking down the manifest example above shed lights on several changes:

1. The Package instance is constructed gradually using Result Builders, making it significantly easier to type a manifest from scratch and allowing IDEs to produce useful auto-complete hints.
2. Target and module are collapsed into a single “module” entity. Modules that need to be exposed publicly (as build artifacts) use a “public” attribute. Dependencies between modules (internal or external) is expressed as “include”.
3. Test module relationship with the module it is testing is expressed as “for”, and any additional dependencies is expressed as “include”.
4. Use of `#if` conditional is possible.
5. The manifest is more concise.

### Using @main

A slight variation on this could be to define a `Package` protocol similar to SwiftUI’s `App` protocol and use a `@main` entry-point to define the package:

```swift
import PackageManifest

@main
struct MyPackage: Package {
  func init(context: Context) {
    self.modules = some Modules {
      ... (same as above)  
    }
    self.dependencies = some Dependencies {
      ... (same as above)    
    }
  }
}

```

```swift
public protocol Package {
  @ModulesBuilder var modules: Modules { get }
  @DependenciesBuilder var dependencies: Dependencies { get }

  init(context: Context)
}

```

### Additional modifiers

The above example focused on key constructs of package: dependencies and modules. Real-world modules tend to be more complex than the example above and include large amount of settings that are applicable to specific module types. Using Result Builders based API allows us to define module specific type modifiers, for example:

```swift
let package = Package()
    .modules {
        Executable("MyProgram", public: true)
            .customPath("custom/path")
            .swiftSettings("swiftSettings")
            .include {
                Internal("CSQLite3")
            }
        System("CSQLite3")
            .providers {
                Apt("libsqlite3-dev")
                Yum("sqlite-devel")
                Brew("sqlite3")
            }
    }

```

This example demonstrates the use of Result Builders modifiers to apply settings. The modifiers are module type specific, for example an `Executable` module has different set of modifiers (e.g. `customPath`, `swiftSettings`) than a `System` module (e.g. `providers`). The full list of modifiers matches the existing capabilities in SwiftPM’s current manifest configuration options.

## Impact on existing packages

The new format would be supported side-by-side to the existing one for a long transition period. Users would be encouraged (with warnings) to use the new format when updating a manifest to a tools-version greater than the one in which the new format was introduced. New features will be added exclusively to the new format.

## **Open questions**

1. Should we use the `@main` or the script-like variant? `@main` is more structures and allows for passing context but more verbose / complicated?
2. If choosing the script variant over the `@main` one, could we get rid of the need to `let package = ...` in fully declarative use cases? It is needed for the imperative use case but feels unnecessary in the fully declarative version.
3. Is “modules” a good term to replace targets and products? Alternatively we can group them by type, e.g. “libraries”, "executables", "tests", "plugins", etc
4. Is “include” a good term for expressing module \<\> module relationship?
5. Is test module relationship with the module it is testing expressed as “for” and additional dependencies as “include” good terminology? should they be collapsed into one "include" section instead (more similar to the current manliest)?
6. Is “public: true” on Internal dependency the right way to express the dependency is to be exposed publicly? (this replaces product → target relationship for multi-target products)
7. Should we continue to use a `tools-version` comment at the top of the file? Is there a better technique or name we should consider to express the manifest's schema/api version? For example, would `schemaVersion` or `schemaAPIVersion` be a more accurate name?

## Reference implementation

> **[GitHub - tomerd/swift-package-manager at feature/manifest2-poc](https://github.com/tomerd/swift-package-manager/tree/feature/manifest2-poc)**
>
> feature/manifest2-poc

Diff view: [Comparing swiftlang:main...tomerd:feature/manifest2-poc · swiftlang/swift-package-manager · GitHub](https://github.com/apple/swift-package-manager/compare/main...tomerd:feature/manifest2-poc)

## Future directions

Orthogonal to this proposal, an interesting usability improvement could be to remove the need to explicitly declare dependencies on targets. Target dependencies are used to compute the minimal dependency closure and avoid pulling non-required dependencies (see [SE-0226](https://forums.swift.org/t/se-0226-package-manager-target-based-dependency-resolution/)). Declaring them explicitly as done today makes the computation easier, but puts the burden on the user, and leads to a verbose and non-esthetic manifest syntax. Since SwiftPM has access to the package and dependencies sources it could potentially deduce this information from the code itself by parsing the import statements.

## Alternative considered

The most obvious alternative is to use a static declarative format such as JSON, YAML or TOML to describe packages. The main advantage of using static declarative format is that it is faster and safer to load - Swift based manifest must be compiled and run to extract the underlying information, while a static file can be parsed which is faster and safer. On the flip side, a sufficiently advanced package needs to go beyond what is possible in a static deceleration and often tap into the programatic capabilities offered by the manifest API. This is true not only in the Swift ecosystem but also in other ecosystems, where some ecosystems use a fully static declarative format with escape hatches for things like running scripts, while others use a programatic API / DSL similar to SwiftPM’s manifest API, for example:

- CocoaPods uses a programatic script based on ruby ([CocoaPods Guides - Podspec Syntax Reference \<span\>v1.16.1\</span\>](https://guides.cocoapods.org/syntax/podspec.html))
- Ruby’s bundle uses a programatic script based on ruby ([https://bundler.io/gemfile.html](https://bundler.io/gemfile.html))
- Node.js npm uses a static declarative format based on JSON with programmatic escape hatches in the form of arbitrary scripts ([scripts | npm Docs](https://docs.npmjs.com/cli/v7/using-npm/scripts))
- Rust’s cargo uses a static declarative format based on TOML with programmatic escape hatches in the form of arbitrary build script ([Build Scripts - The Cargo Book](https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/cargo/reference/build-scripts.html#build-scripts))
- Java’s Maven uses a static declarative format based on XML composing a collection of programatic plugins ([Available Plugins – Maven](https://maven.apache.org/plugins/index.html))
- Java’s Gradle uses a DSL based on Groovy or Kotlin ([Gradle DSL Version 9.2.1](https://docs.gradle.org/current/dsl/index.html))

From a broad security and performance perspective, escape hatches such as custom scripts and plugins are equivalent to a manifest based on a programatic API or DSL, since the system need to compile and run code in either case. As such, we do not feel that at this time transitioning to a static declarative format with escape hatches would give a significant benefit.

One could argue "why not both?": Static declarative format for simple packages and programatic API for advanced ones. The benefit of such approach is that at least _some_ packages can benefit from better performance and simplified security profile, and the implications are that users would need to learn 2 manifest formats (and languages) and a maintenance burden that can lead to asymmetry in features and quality.

---

<div class="post-metadata">

**Author:** ![Xi\_Ge](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/xi_ge/32/1594_2.png) [@Xi\_Ge](https://forums.swift.org/u/Xi_Ge)\
**Post date:** [November 12, 2021, 11:01pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/2 "2021-11-12T23:01:26Z")

</div>

Love the idea of more declarative and builder-oriented APIs! Because the proposed APIs resemble static declarative format such as JSON, probably we could extract the data model from the Swift source code without running the code to be more secure? at least for simple package structures?

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [November 12, 2021, 11:12pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/3 "2021-11-12T23:12:38Z")

</div>

This is great, very excited about -- the manifests are really due for a refresh 🙂 I'll dig deeper into it soon.

* * *

Quick question (or nitpick?) while we're early here...

I think it is a bit weird that the `let = Package()` is not a builder, i.e. we can make it also the same style:

```swift
let package = Package {
  Modules {  
    Library(...)
  }
}

```

rather than

```swift

let package = Package()
  .modules {

```

That's doable AFAIR as I was doing some result builders of my own. It keeps the same style which is nice IMHO.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [November 12, 2021, 11:14pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/4 "2021-11-12T23:14:45Z")

</div>

> [@tomerd](#):
>
> - Is “public: true” on Internal dependency the right way to express the dependency is to be exposed publicly? (this replaces product → target relationship for multi-target products)

Maybe there's `InternalModules { ... }` and `PublicModules { ... }` (or exported modules?) to address that by declaring them in a diff block.

In practice this could just do:

```swift
var modules = <get modules from the result builder>
// inside PublicModules we'd do:
modules = modules.map { $0.public = true }

```

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [November 12, 2021, 11:28pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/5 "2021-11-12T23:28:26Z")

</div>

Interesting. I agree that `Package` should also be a builder, rather than using SwiftUI-style modifiers for each structural group.

Also, since you can only declare one package per manifest, would we be able to have a hidden global (within the `PackageManifest` module) which is assigned to in `buildFinalResult`?

```swift
// Within PackageManifest module

public var package: Package?

@resultBuilder
struct Package {

  @discardableResult
  static func buildFinalResult(_ p: Package) -> Package {
    package = p
  }
}

```

That would eliminate the need the declare a variable named "package", and allow a more declarative format:

```swift
import PackageManifest

Package {
  ...
}

```

It's a small thing but I think it's more visually pleasing.

---

<div class="post-metadata">

**Author:** ![Mordil](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mordil/32/5840_2.png) [@Mordil](https://forums.swift.org/u/Mordil)\
**Post date:** [November 12, 2021, 11:46pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/6 "2021-11-12T23:46:37Z")

</div>

I am in favor of dropping the tools-version comment for an explicitly set variable, much like we were with `Package`

* * *

In terms of which route to go, `@main` vs. just Result Builders, the question I have is which leaves the door open to _potentially_ importing data from dependency package manifests, such as custom configurations?

Example of what I'm invisioning

```swift
// Dependency/Package.swift
import PackageDescription

let schemaVersion = 5.6

enum Configuration: PackageConfiguration { // protocol PackageConfiguration: CaseIterable
  case super
  // details left to another proposal
}

Package(availableConfigurations: Configuration.allCases) {
  // ...
}

// Root Package.swift
import PackageDescription
import DependencyDescription // bikeshed

let schemaVersion = // ...

Package {
  Dependencies {
    External(...)
      .configuration(DependencyDescription.Configuration.super, when: .platform(.linux))
  }
}

```

---

<div class="post-metadata">

**Author:** ![odmir](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/odmir/32/4784_2.png) [@odmir](https://forums.swift.org/u/odmir)\
**Post date:** [November 12, 2021, 11:49pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/7 "2021-11-12T23:49:09Z")

</div>

> [@tomerd](#):
>
> Currently, the static representation is based on JSON and the exact format of that JSON is an internal implementation detail. The JSON model is later deserialized and loaded into the parent process memory space, driving SwiftPM’s workflows such as the dependency resolution, build, test , etc.

While I appreciate the “behind the scenes” explanation, I don’t fully understand its relevance to the pitch, does this pitch do something about this static representation that I missed?

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [November 13, 2021, 12:57am UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/8 "2021-11-13T00:57:46Z")

</div>

Personally, I don't find the proposed API compelling, it's just the same thing spelled slightly different. Perhaps its only advantage is the removal of the ordered requirement we currently see with the parameterized version. Better tooling could help with that pain point, but the rest of the issues with the current manifest remain. Perhaps my biggest complaint is that building every type of thing in the manifest necessarily requires interacting with `String` values which cannot be validated, autocompleted, or otherwise tied together. If I'm going to write Swift for the manifest there should be some advantage to doing so and there isn't right now.

---

<div class="post-metadata">

**Author:** ![SDGGiesbrecht](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sdggiesbrecht/32/4768_2.png) [@SDGGiesbrecht](https://forums.swift.org/u/SDGGiesbrecht)\
**Post date:** [November 13, 2021, 1:23am UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/9 "2021-11-13T01:23:11Z")

</div>

I really like the `@main` idea, because it loosens the ordering constraints on the global scope.

Right now if we want to reuse something at several points, or to write dynamic logic, we have two options:

1. Put it before the main `init`, cluttering the top of the file with functions and variables, and pushing the most relevant information down so you need to scroll to find it. (For an example, see [SwiftPM’s manifest](https://github.com/apple/swift-package-manager/blob/4b6798f997058aae167c27cb4e42e22c7522afd2/Package.swift#L19-L24).)
2. Mutate the package after initializing it, which often requires the extra effort of writing a search algorithm to locate the particular target that needs adjusting. (For an example, see [SwiftSyntax’s manifest](https://github.com/apple/swift-syntax/blob/4c46b31770a775b08124ed16419bf926d963f200/Package.swift#L21-L48).)

But if you could define a function or variable at the bottom of the file, and then call it in the main portion at the top, then you could avoid the downsides of both current options.

---

<div class="post-metadata">

**Author:** ![finestructure](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/finestructure/32/34879_2.png) [@finestructure](https://forums.swift.org/u/finestructure)\
**Post date:** [November 13, 2021, 11:28am UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/10 "2021-11-13T11:28:36Z")

</div>

I really like this. I can never get the syntax right in the existing format and heavily rely on copy-paste to make any significant changes. This would help a lot and I suspect also make it easier for people new to the language.

If the target dependencies could be computed automatically without too much or a performance hit that would be great. The only time I actually remember to also use a new dependency in a target is when I've just failed to do it for another one 😅

As to the open questions:

> 1. Should we use the @main or the script-like variant? @main is more structures and allows for passing context but more verbose / complicated?

I personally find the script-like variant more readable and I suspect it's going to be more approachable. Are there any benefits to using the `@main` approach over the script-like one?

One thing that I find quite weird about the `@main`-variant is that you need to come up with some name for the struct, `MyPackage` in the example. I always struggle naming things when I don't know how it's going to impact things down the line. It seems like there's not as good a default available here as in the script-like variant, where you can write `let package = Package`. Using `struct Package: PackgeProtocol` or something similar would be awkward.

> 1. If choosing the script variant over the @main one, could we get rid of the need to let package = ... in fully declarative use cases? It is needed for the imperative use case but feels unnecessary in the fully declarative version.

That would be great and another argument in favour of the script-like variant.

> 1. Is “modules” a good term to replace targets and products? Alternatively we can group them by type, e.g. “libraries”, "executables", "tests", "plugins", etc

I've come to think of "modules" as the products really so I would welcome this change. "target" is quite a "build-system-y" kind of term and "product" works best together with "build product", I feel. So "module" would be a good new term if it fits here (which it seems it does).

> 1. Is “include” a good term for expressing module \<\> module relationship?

This one feels a bit weird. It sounds like it's including it instead of using or referencing it. Perhaps `.dependsOn`? Or `.uses`?

```swift
    Executable("MyExecutable", public: true)
      .dependsOn {
        Internal("MyDataModel")
        Internal("MyUtilities")
        External("SomeModule", from: "some-package") 
      }

```

> 1. Is test module relationship with the module it is testing expressed as “for” and additional dependencies as “include” good terminology? should they be collapsed into one "include" section instead (more similar to the current manliest)?

Seems fine but I wonder if this could be attached to the module instead? I.e.

```swift
    Executable("MyExecutable", public: true)
      .include {
        Internal("MyDataModel")
        Internal("MyUtilities")
        External("SomeModule", from: "some-package") 
      }
      .tests {
        Test("MyExecutableTests")
      }

```

> 1. Is “public: true” on Internal dependency the right way to express the dependency is to be exposed publicly? (this replaces product → target relationship for multi-target products)

I feel it would be better if it's more obvious this is an externally consumable module, perhaps via the module type, i.e.

```swift
    PublicExecutable(...)
      .include { ... }

```

although I don't love that it'd be a different type. It's not going to be possible, but `public Library(...)` would be a great way to declare that.

One thing that puzzles me about the example is the `public: true` on the nested `Internal` module:

```swift
    Library("MyLibrary", public: true)
      .include {
        Internal("MyDataModel", public: true)
        // ^^^^^^^^^^^^
        External("SomeOtherModule", from: "some-other-package") 
      }

```

Is that a typo or would this allow declaring a public module "MyDataModel" from within the `.include` clause? I think using `PublicLibrary` and disallowing it inside `.include` would be better.

> 1. Should we continue to use a tools-version comment at the top of the file? Is there a better technique or name we should consider to express the manifest's schema/api version? For example, would schemaVersion or schemaAPIVersion be a more accurate name?

It's nice that the tools-version tracks the swift version and you don't need to keep track of which version of Swift brought which tools-version but the naming always felt a bit weird. Renaming that to schemaVersion or schemaAPIVersion would be clearer.

Excited to see where this is going! 🎉

---

<div class="post-metadata">

**Author:** ![krzyzanowskim](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/krzyzanowskim/32/1282_2.png) [@krzyzanowskim](https://forums.swift.org/u/krzyzanowskim)\
**Post date:** [November 13, 2021, 1:07pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/11 "2021-11-13T13:07:11Z")

</div>

> [@tomerd](#):
>
> auto-complete tools struggle with such expression and IDEs offer little help to that end. In practice, the most effective way to write a manifest is to copy/paste a working one and then change it, which is indicative of the usability problem.

tbh I don't think the new syntax, especially with @ main struct etc will be any easier to write by hand from scratch. Especially that IDE (and we're talking about Xcode) is not much better today with autocompletion (as we know from SwiftUI).

Given the manifest is still compiled to JSON, can we consider `Package.swift.json` as a supported format as well (akin to CocoaPods)? That would simplify even further generating manifests from third-party tools.

---

<div class="post-metadata">

**Author:** ![George](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/george/32/37757_2.png) [@George](https://forums.swift.org/u/George)\
**Post date:** [November 13, 2021, 2:55pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/12 "2021-11-13T14:55:50Z")

</div>

So it looks like there are a few distinct changes here:

1. Unify products and targets using the "module" abstraction
2. Use result-builders in place of arrays
3. Use "builder style" APIs

I'll address each one separately

#### Unify products and targets using the "module" abstraction

This seems like a good simplification. I've already written code that mapped a single array of strings into the products/targets I wanted.

#### Use result builders in place of arrays:

As I see it, this is a trade-off. The main benefit is not needed to add commas and never having the compiler think you are writing `.target(…).target(…)`. The downside is that there isn't an easy way to say "show me what values I can use here", which we can currently do by typing `.` and seeing what shows up in autocomplete. This is particularly noticeable in the `providers` example where there is no way (aside from consulting documentation) to know what other options might exist other than `Apt`, `Yum` and `Brew`.  
Personally, I don't see this as compelling enough to make the switch. Result builders for tree-like DSLs like SwiftUI have a clearer value proposition since they get you out of bracket-hell, but for SPM I just don't see enough benefit to make the change.

### Builder-style APIs

I'm not really sure I see a clear win here over just having initializers, perhaps the ability to specify initialization arguments in any order you want or split them into meaningful groupings like the following example?

```swift
.modules {
  /// Modules related to Foo
}
.dependencies {
  /// Dependencies related to Foo
}
.modules {
  /// Module related to Bar
}
.dependencies {
  /// Dependencies related to Bar
}

```

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [November 13, 2021, 4:58pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/13 "2021-11-13T16:58:26Z")

</div>

> [@Karl](#):
>
> That would eliminate the need the declare a variable named "package", and allow a more declarative format:
> 
> ```swift
> import PackageManifest
> 
> Package {
> ...
> }
> 
> ```
> 
> It's a small thing but I think it's more visually pleasing.

I _love_ this (and, for the same reason, the “script-like” approach over the `@main` approach). This seems to me the same kind of thinking that deliberately sought to ensure that “Hello World” in Swift is the one-liner `print("Hello World")`—not an accident!

Build tools are complex enough, and making the manifest approachable at first glance is, in my view, _no small thing_. Eliminating the “target” versus “product” definitions and all other things not essential for user intent are _huge_.

* * *

Along the same lines, I don’t understand at first glance why executables need to be declared `public: true`—this seems to be needless copying of Swift’s access control model that for good reason requires public APIs to be declared as such, but who by default would assume that their package is going to publish _private_ executables?

I’m also unclear on the distinction between an “internal public” include and an “external” (“external private”?) include as shown in the example by OP; in access control terminology, `internal` and `public` are mutually contradictory, while external visibility is what `public` does—so I would strongly suggest different terms to be used here. Do you mean instead “local” versus “remote”?

---

<div class="post-metadata">

**Author:** ![Srdan\_Rasic](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/srdan_rasic/32/5396_2.png) [@Srdan\_Rasic](https://forums.swift.org/u/Srdan_Rasic)\
**Post date:** [November 13, 2021, 10:46pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/14 "2021-11-13T22:46:51Z")

</div>

Thank you so much @tomerd for writing this pre-pitch. I can definitely feel the key issues you mentioned every time I work with manifest files. As I care about this topic as well, I have been thinking about same problems for some time now, and I came up with a couple of ideas.

I would like to propose few changes to your proposal that I think could make the package manifest even simpler. While doing about this, I have put focus on the following areas:

- Eliminating internal string references. I think this is a very important topic, and I don't believe that we should have a major manifest redesign without addressing this issue.
- Following the principles of progressive disclosure. A simple package should be easy to declare, while more advance features should be disclosed when needed.
- Trying to make changes a sugar on top of the existing implementation. I didn't stick strongly to this one, but it might be close.

Here it goes.

Firstly, I think the `Package` type initializer should be a result builder where we define public package products. Secondly, I follow your idea of merging products and targets into a single entity.

With that, the simplest package could look like this:

```swift
Package {
    Library("MyLibrary")
}

```

Next, products often have dependencies. The `Library` type gets an initializer to declare dependencies through a result builder. Additionally, we allow declaring products within declarations of other products. Those products are dependencies of the parent product and are private by default, but can be made public, package, products if needed.

```swift
Package {
    Library("MyLibrary") {
        Library("MyUtilities")
        Library("MyDataModels", public: true)
    }
}

```

We also allow declaring external dependencies within declarations of other products. External packages get an API to reference their products.

```swift
Package {
    Library("MyLibrary") {
        Library("MyUtilities")
        Library("MyDataModels")
        ExternalPackage(at: "https://git-service.com/foo/some-package", upToNextMajor: "1.0.0")
            .library("SomeLibrary")
    }
}

```

When external packages are dependencies of multiple products, then they should be pulled out into a variable.

```swift
Package {

    let someLibrary = ExternalPackage(at: "https://git-service.com/foo/some-package", upToNextMajor: "1.0.0")
        .library("SomeLibrary")

    Library("MyLibrary") {
        Library("MyUtilities")
        Library("MyDataModels") {
            someLibrary
        }
        someLibrary
    }
}

```

I believe this is very important as it eliminates the need for inventing external packages name that can be used as string references, which is confusing for users and I've noticed it made a mess in the SwiftPM implementation.

The same should be done for package products that are dependencies of multiple other products.

```swift
Package {

    let myLowLevelLibrary = Library("MyLowLevelLibrary")

    Library("MyLibrary") {
        Library("MyUtilities") {
            myLowLevelLibrary
        }
        Library("MyDataModels") {
            myLowLevelLibrary
        }
    }
}

```

Saving an instance into a variable, instead of using string references, is a natural thing for a developer to do when passing the same thing to multiple functions. It eliminates the possibility of making a typo in one place and allows the compiler to statically evaluate correctness.

Finally, we can declare test products within products they are supposed to test, through a second result builder closure, eliminating the need for referencing the target product, which becomes an implicit dependency. Loving the application of SE-0279 here!

```swift
Package {
    Library("MyLibrary") {
        Library("MyUtilities")
        Library("MyDataModels")
    } tests {
        Tests("MyLibraryTests")
    }
}

```

Of course, we allow multiple test targets, as well as test targets having their own dependencies.

```swift
Package {
    Library("MyLibrary") {
        Library("MyUtilities")
        Library("MyDataModels")
    } tests {
        Tests("MyLibraryTests")
        Tests("MyLibraryOtherTests") {
            Library("MyTestUtilities")
        }
    }
}

```

Your full example:

```swift
import PackageDescription

let someLibrary = ExternalPackage(at: "https://git-service.com/foo/some-package", upToNextMajor: "1.0.0")
    .library("SomeModule")
let someOtherLibrary = ExternalPackage(at: "https://git-service.com/foo/some-other-package", upToNextMajor: "1.0.0")
    .library("SomeOtherModule")
let myDataModelLibrary = Library("MyDataModel", path: "custom/path", swiftSettings: "swiftSettings")

Package {
    Executable("MyExecutable") {
        myDataModelLibrary
        someLibrary
        Library("MyUtilities")
    } tests {
        Tests("MyExecutableTests") {
            Library("MyTestUtilities")
        }
    }
    Library("MyLibrary") {
        myDataModelLibrary
        someOtherLibrary
    } tests {
        Tests("MyLibrary")
    }
}

```

---

<div class="post-metadata">

**Author:** ![wowbagger](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/wowbagger/32/13847_2.png) [@wowbagger](https://forums.swift.org/u/wowbagger)\
**Post date:** [November 14, 2021, 3:50am UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/15 "2021-11-14T03:50:13Z")

</div>

> [@tomerd](#):
>
> That said, the “giant initializer” is the more common form of the manifest, arguably because it looks to be declarative and as such easier to read at a glance.

The "giant initializer" form is also officially recommended during the WWDC 2018 Getting to Know Swift Package Manager session ([around the 13:40 mark](https://developer.apple.com/videos/play/wwdc2018/411/?time=818)).

> [@tomerd](#):
>
> Is “include” a good term for expressing module \<\> module relationship?

I think something like `.depends(on: [Module])` might read better.

> [@tomerd](#):
>
> Should we continue to use a `tools-version` comment at the top of the file? Is there a better technique or name we should consider to express the manifest's schema/api version? For example, would `schemaVersion` or `schemaAPIVersion` be a more accurate name?

A shebang-like directive is often full of hidden traps. Because it relies on a relatively inflexible structure and position not communicated to the user, just having a wrong whitespace or starting on the wrong line can render the entire manifest file uninterpretable. And because it's outside of the manifest/program's logic and the language's syntax rules, the user is not likely to understand the rules of the directive. Even if the user knows the rules, unless the error messages are able to point to the exact problem, it's not often a quick fix, because of the general subtlety and inconspicuousness of the directive's errors.

As someone who spent 2 months to [make the tools-version comment line more flexible](https://github.com/apple/swift-package-manager/pull/2937), I would like to see it deprecated for something more integrated with the manifest's logic.

Maybe we can expose [`ToolsVersion`](https://github.com/apple/swift-package-manager/blob/71f881c4dfdf76fea79b72668d3199a29b0985ee/Sources/PackageModel/ToolsVersion.swift) to the user, and let `Package` and related type's initializers (and static functions that work as initializers) take it as a parameter. For example, something like this:

```Swift
Package(minimumSwiftVersion: v5_3) {
    .modules {
        Library("MyConcurrencyLibrary", minimumSwiftVersion: v5_5)
        Library("MyOtherLibrary")
    }
}

```

And if we add language feature flags to `ToolsVersion`, it could allow us to write something like this:

```Swift
Package(minimumSwiftVersion: v5_3) {
    .modules {
        Library(
            "MyDistributedActorLibrary", 
            .when(currentToolsVersion.supportsDistributedActor)
        )
        Library(
            "MyLinuxCppWrapperLibrary",
            .when([
                .languageFeatures([.cppInterop])
                .platforms([.linux])
            ])
        )
        Library("MyOtherLibrary")
    }
}

```

Something like this where the tools-version requirement is part of the `Package` initialization can also allow all version-specific manifest files into a single one, which should have better code reuse and be easier to maintain.

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [November 14, 2021, 5:02am UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/16 "2021-11-14T05:02:17Z")

</div>

> [@Jon\_Shier](#):
>
> Perhaps my biggest complaint is that building every type of thing in the manifest necessarily requires interacting with `String` values which cannot be validated, autocompleted, or otherwise tied together. If I'm going to write Swift for the manifest there should be some advantage to doing so and there isn't right now.

That's fair. If I imagine how this could be improved, the obvious choice would be to use some sort of enum for products/targets/dependencies. But then we have to declare a bunch of enums and have separate code to hook them all up, and the whole thing ends up being a lot more verbose.

What we'd ideally want is for some kind of new language feature (perhaps an extension to the result builder transform), where elements of a result builder can define cases in an enum.

Other parts of the project would likely benefit from this, too. For example, the recent regex proposals mention that the cases of an `Alternation`/`OneOf` pattern should ideally be available as some kind of enum on the match result, and AFAIK that applies to both regex literals and the `Pattern` result-builder DSL. So that's nice. Having overlapping requirements makes it easier to scope the desired language feature and maybe implement it one day.

In the mean time, there's a much simpler option - we _could_ just give code completion (which is part of the compiler) some specific knowledge of the Package.swift format, allowing it to auto-complete some common string values (e.g. target names). It's a hack, for sure, but it only affects this one file to give you a better editing experience.

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [November 14, 2021, 7:28am UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/17 "2021-11-14T07:28:13Z")

</div>

My suggestion would actually be to adopt some sort of TOML format and use the various existing tools or methods to enable a simple autocomplete environment for most IDEs. Since you're already operating at a string level, suggesting string completions isn't too hard. Plus it will be much more readable than the Swift manifest, and much easier to write to for those without full IDE support.

Thinking about it more, a new builder API would be an improvement, if only to get rid of the parameter ordering restrictions I already mentioned. Given how hard it is to find the docs for those APIs outside of Xcode in package mode, any improvement to remove the need to do so would be appreciated.

---

<div class="post-metadata">

**Author:** ![hfhbd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hfhbd/32/7340_2.png) [@hfhbd](https://forums.swift.org/u/hfhbd)\
**Post date:** [November 14, 2021, 10:27am UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/18 "2021-11-14T10:27:38Z")

</div>

Would this change allow you to use some kind of "typed libraries" too? I would like to prevent copy/pasting the names, especially with big package manifests.

```swift
let myDataModel = Library("MyDataModel")
Library("MyLibrary", public: true) {
    include {
        Internal(myDataModel, public: true)
        External("SomeOtherModule", from: "some-other-package") 
    } 
}

```

---

<div class="post-metadata">

**Author:** ![Joannis\_Orlandos](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joannis_orlandos/32/732_2.png) [@Joannis\_Orlandos](https://forums.swift.org/u/Joannis_Orlandos)\
**Post date:** [November 14, 2021, 6:54pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/19 "2021-11-14T18:54:17Z")

</div>

I've got mixed feeling with this, but I certainly see good usecases.

First of all, I would strongly suggest ignoring the 'added complexity' of writing `@main` in the package definition. If we get more features for basically free, do it. 99.9% of the people will use a premade/generated project and edit things from there. Those who don't will know what they're doing anyways.

I think this move is absolutely great, because it allows us to expand upon the Package manifest extremely easily and in a tonne of directions. The flexibility this would add to the manifest is awesome, especially considering the use of compiler directives such as `#if`.

My main issue would be how verbose it is for sure, but the verbosity makes this extremely easy to reason about. Adding to that the possibility of using conditions and a (future?) context in which we can derive the target platform(s) and environment we can do a lot of stuff with this.

As for the tools-version, couldn't we make multiple `PackageXYZ` protocols that expand upon their previous versions? Ignoring the protocol naming: `protocol Swift5_7Package: Swift5_6Package { .. }`

If I define a package in Swift 5.6+, I'll use `Swift5_6Package`, which'll be available from that Package manager forwards. If I want to support older Swift versions, I can reply upon the corresponding `protocol`. This can also be more easily checked for generating package indexes, since we don't use a comment as a workaround.

---

<div class="post-metadata">

**Author:** ![SDGGiesbrecht](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sdggiesbrecht/32/4768_2.png) [@SDGGiesbrecht](https://forums.swift.org/u/SDGGiesbrecht)\
**Post date:** [November 14, 2021, 8:11pm UTC](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457/20 "2021-11-14T20:11:25Z")

</div>

> [@Joannis\_Orlandos](#):
>
> The flexibility this would add to the manifest is awesome, especially considering the use of compiler directives such as `#if` .

`#if` will remain as ill‐advised as it is now...

> ****
>
> ...because the manifest is compiled for the development device, not the target device. For example, `#if os(Linux)` in macOS/Linux package breaks cross‐compilation in both directions.
> 
> If you need a workaround for some condition absent from `PackageDescription`, it is much better to do adjustments based on the environment (e.g. `if ProcessInfo.processInfo.environment["TARGETING_LINUX"] == "true"`). But even then it should only be used as a last resort.

[Next page](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457.md?page=2)
