Update to swift package init templates

Hello folks,

I'm working on some improvements to the swift package init templates and would love to hear what you think:

Here's a summary of the new usage:

OVERVIEW: Initialize a new package

USAGE: swift package init [--type <type>] [--name <name>]

OPTIONS:
  --type <type>           Package type: (default: library)
        library           - A package with a library.
        executable        - A package with an executable.
        tool              - A package with an executable that uses
                            Swift Argument Parser. Use this template
                            if you plan to have a rich set of command-line arguments.
        empty             - An empty package with a Package.swift manifest.
  --name <name>           Provide custom package name
  --version               Show the version.
  -h, -help, --help       Show help information.

You can check out the changes in the pull request for more details. A main goal of these changes is to simplify the template content for new packages without making too many assumptions about what the user is trying to make. For example, there was some usage of private(set) which isn't a super common thing to show someone when they are getting started. So, the executable template uses plain top-level code and the library template just has a public function, both with a simple and friendly "Hello, world!".

Along with that, a couple of interesting enhancements:

  • Use links to documentation like the TSPL, Swift Argument Parser, and XCTest where appropriate to teach people more about writing Swift instead of template content. I think this is a lightweight and more timeless way to get people to fresh instructional content without making assumptions.

  • A new tool template will be available, which starts the user off with a ParsableCommand struct and adds a dependency on the latest stable version of swift-argument-parser. While the executable template will continue to provide a bare executable, this template smooths the path for a more complete tool and promotes Swift's solution for managing command-line arguments.
    After talking with @NeoNacho about this, it would be good to have a "latest" package requirement which allows a dependency to automatically take the latest stable version. For now, I can have swiftpm emit the latest appropriate version of swift-argument-parser explicitly into the generated manifest and adopt that later.

I would love to hear what you think about these changes. Thank you!

16 Likes

I like it, and the PR (at a glance) looks like it adds nice things to bring the templates a bit more up to date with current Swift usage, while providing additional learning references.

I suspect my suggestion is controversial - but what about including an explicit dependency, in the case of library in particular, to swift-docc-plugin? I think it would be a huge benefit if the template included not only the plugin (which is practically needed to make use of the documentation generation), but a basic template structure that included the directory structure akin to what you get when you add a Documentation catalog within Xcode.

I realize my suggestion goes against your intention of simplifying the content, as its explicitly adding more - but think it would be a good addition to implicitly include.

5 Likes

I like the suggestion in general. We are thinking about making the templates more dynamic/interactive in the future, maybe that would be a better time to add this so that we can keep the simplified templates but still offer a starting point for documentation for cases where that's appropriate.

2 Likes

Any thoughts of this vs AsyncParsableCommand?

The original goal of making these changes is to simplify the defaults as much as possible. I wouldn't want to throw anything about async at someone just getting started unless it was the one and only default way to create a command. If ParsableCommand becomes truly deprecated, then we can easily update it.

3 Likes

I always found it slightly odd that one has to create the enclosing folder before invoking Swift package init. e.g.

mkdir MyCoolPackage
cd MyCoolPackage
swift package init -- type executable 

Might it be possible, or rather, useful and more simple to have an option to create the folder as part of the package init process?

Apologies if this is outside the scope of the topic.

4 Likes

Agree, especially if name is provided?

swift package init --type tool --name MyNewCoolTool
cd MyNewCoolTool
swift run

would be nicer for a beginner.

7 Likes

I really like this idea, it's something I've been looking at for a separate improvement to the init command line, which includes things like prompting so that passing --name isn't necessary. For this update, I'm focusing on the template content.

5 Likes

On the whole I like these changes.

However I think some of the changes in the PR don't achieve the goal of making things simpler. In particular the executable target putting the entry point directly into Sources/ (rather than under Sources/<TargetName>/) is more complicated unless the user never adds another target.

If the user does add another target they would need to move the existing source files and update the path for the target in the manifest.

This change also breaks existing automation. SwiftNIO, for example, has tooling to generate packages for running various integration tests which rely on the layout produced from swift package init --type executable. I'm sure there are other SPM users who do the same but have not yet discovered the change.


The other change I'm not so sure about was the addition of the comments linking to various documentation (TSPL, XCTest etc.). I understand why a new user might find these helpful but I suspect that removing them will be the first thing that the majority of developers do!

4 Likes

Understood - there will always be a balance of "is this more complex if I will end up in a more complex configuration?" versus "is this more complex than I will ever need?" We are trying to optimize for the case where simple cases can remain simple. The goal is making Swift more approachable to more people.

Once a developer does need to start adding multiple products they will have to become comfortable with organizing files on disk and matching changes in the Package.swift file. This change is based on the idea that it is better to learn about that complexity when needed, especially as we can't know in advance the developer's end need (dependencies, executables, libraries, tests, macros, etc.)

For many folks, they won't ever need that complexity. They won't need to be two folders deep to see their .swift files, and would like to feel like Swift is generally a bit simpler.

I have to agree with @georgebarnett here - we're going against what the default for SwiftPM is - the simplest option IMO would be to use the default that SwiftPM expects with the target's source code in a folder named after that target. That simplifies the manifest as well. (I also think that the majority of people will want to create multiple targets for any SwiftPM project)

7 Likes

Why is one folder deep okay but two folders is not? In both cases the user has to know or learn to look in the Sources/ directory. If the goal is to make Swift more approachable to more people then why not place the main.swift file at the top-level alongside Package.swift?

I think there are a number of other things we can do to make creating packages easier for new users while teaching them at the same time and sticking to the defaults that SPM uses. swift package init, could, for example, be interactive and guide the user (rather than defaulting to creating a library). The tool could also print out text explaining the generated package structure ("Package.swift describes the layout and dependencies used in the package, the entry point to MyExecutable is Sources/MyExecutable/main.swift" etc.).

5 Likes

Agree with @georgebarnett here. I think the new default template for executables isn't making it any easier. Rather it makes it actually harder to create more targets then. IMO the default that we had before was quite good.

I also agree that a better solution here would be to make the creation of new packages interactive or provide more options.

5 Likes

I would argue that for the absolutely most trivial use case, one would not use the package manager at all.

> cat test.swift
print("Hello world")
> swiftc test.swift
> ./test
Hello world
>

Just a simple file, if you just want to try a trivial executable without dependencies (so I'd view someone using the package manager as someone who wants to add a little but more structure and complexity).

I don't think that would be related to package templates though. To me personally it would be weird if swift package init generates anything (like a single main.swift file) that has nothing to do with packages. Maybe that's an argument for having a swift init command to satisfy all use cases.

Oh, I agree - it was not what I argued for at all - I just pointed out that the "trivial first executable" just trying the language out, probably don't even use a package manager at all - so it makes sense to target the SPM template generation for slightly more advanced use cases.

1 Like

Good conversation, and thanks for the suggestions! The interactive idea is very compelling. Interactivity is quite a bit of work and design, but when done well could be useful throughout the Swift CLI experience. Definitely looking into this idea to add more flexibility and understanding.

As for using ./Sources versus root, that is a fairly common pattern. e.g. in Rust it is common to use ./src. A reason to have at least one folder rather than root is to make it easier to command "build all files in that folder" whereas the root may have other files (e.g. Package.swift itself) that don't belong in the target. That said, Xcode and others (C#?) use a pattern of ./TargetName so all the targets are then in the root as named folders. That is potentially interesting, too, but feels less "standard" and starts to get kind of ugly when tests are added.

Overall, it doesn't seem very common to create two folders deep for the first source file. That feels optimized for the pro user. Maybe a new template for that case makes sense, though? e.g. swift package init --type multi-target?

Another cool feature may be to go a step further with interactivity and make it easy to add targets and dependencies later. Something like swift package add-target where it then interactively asks what to add.

1 Like

I get where you’re coming from, but one of the issues with setting up a single target default is that it makes stepping into a second target significantly more work, and somewhat surprising at that.

I’m also (clearly) a fan of keeping the dual directory structure because i don’t think it costs anything, and the moment you want to add another target (for example, an external CLI target to run benchmarks), the hints are already well placed to make it clear that SwiftPM organizes its world in a very target specific way, which the directory structure uses and reinforces.

I’ve also found myself adding a second target to encapsulate SwiftUI previews for a package as a means of isolating those previews from folks using the package. That also lets me provide resources for interactively exploring the results of a package containing SwiftUI views, without having all that additional stuff dumped into the app of any consumers use of the package.

I’m providing the two examples because i think stepping into multiple modules isn’t a far step to take when you’re developing a library for consumption, and likewise the dual directories for sources don’t seem to be a notable hinderance.

4 Likes

Thanks Joe.

The costs of the current directory structure when created on day-one via a template is added complexity right from the beginning. Then to see any code file, even if you only have one or two, you will be opening two folders deep in your IDE, Finder, Terminal, etc. Some people work in a way where that isn't bothersome, especially existing Swift expert users, but others find that heavy.

Explaining the reason why code is two folders deep can also be tricky for newcomers learning the language. Going through educational content for other languages doesn't often expose "code for each target" organization (Java being an obvious exception.)

Curious, what is your feeling about the following approaches, original first:

/Sources/Target1/
/Sources/Target1_tests/

... vs. targets at the root:

/Target1/
/Target1_tests/
/Target1_previews/.   (etc...)

.. a more generic variant:

/Sources/
/Tests/
/Previews/

Each of these options set you up for multiple targets. The last two only need one nested level, and aren't as obviously "organization by target" for the newcomer to worry about. The last one, IMO, is more clear about code for different purposes, not necessarily multiple creations (which feels more "pro" than a newcomer wants). Note that the templates' Package.swift file is explicit about the folder and target relationship, so are fairly easy to expand to add another, e.g.:

        .executableTarget(
            name: "tests",
            path: "Sources"),

Again, appreciate the discussion!

1 Like

... and in case this wasn't already clear (sorry!) the --type library template continues to use the ./Sources/TargetName format. Ready to add more targets. The code is just a bit simplified in this update. This is the template used by the default swift package init command with no parameters.

The move to the simpler folder structure is in the executable and tool templates. These templates are especially great for people learning Swift, working to write their first small CLI program. Also great for kicking around ideas in the form of a quick program with output (and maybe parameters via argument parser.)