Skip to content

Commit c04c920

Browse files
miggs597Miguel Pereztomerd
authored
[Proposal] New command for creating packages, and template support. (#1349)
Co-authored-by: Miguel Perez <[email protected]> Co-authored-by: tomer doron <[email protected]>
1 parent 9f1bb17 commit c04c920

File tree

1 file changed

+362
-0
lines changed

1 file changed

+362
-0
lines changed

proposals/0318-package-creation.md

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
# Package Creation
2+
3+
* Proposal: [SE-0318](https://github.com/apple/swift-evolution/blob/main/proposal-templates/0318-package-creation.md)
4+
* Authors: [Miguel Perez](https://github.com/miggs597/)
5+
* Review Manager: [Tom Doron](https://github.com/tomerd)
6+
* Status: **Scheduled for review (Jun 8 - June 15 2021)**
7+
* Implementation: https://github.com/apple/swift-package-manager/pull/3514/
8+
9+
# Introduction
10+
11+
In order to clearly separate the roles of transforming an existing directory of source files into a Swift package, from creating a new package from scratch we propose adding a new command `swift package create`. `swift package init` will continue to exist as is, but will be updated to focus on the former, while the new `swift package create` will focus on the latter.
12+
13+
14+
# Motivation
15+
16+
Currently `swift package init` handle two distinct use cases:
17+
18+
1. Transforming an existing directory with sources into a Swift package
19+
2. Creating a new package from scratch.
20+
21+
This one-size-fits-all approach can be confusing and counter-productive, especially for users that are focused on the second use case. It assumes prior knowledge about the command behavior, and specifically about the need to create an empty directory upfront and naming the package after the directory name.
22+
23+
We feel that separating the two concerns into separate commands, will allow SwiftPM to have better default behavior which is more aligned with the users expectations.
24+
25+
26+
## Current Behavior
27+
28+
### Creating new package
29+
30+
To create a new package users perform the following steps:
31+
32+
```console
33+
$ mkdir MyLib
34+
$ cd MyLib
35+
$ swift package init
36+
```
37+
38+
Resulting in the following structure:
39+
40+
```console
41+
.
42+
├── .gitignore
43+
├── Package.swift
44+
├── README.md
45+
├── Sources
46+
│ └── MyLib
47+
│ └── MyLib.swift
48+
└── Tests
49+
└── MyLibTests
50+
└── MyLibTests.swift
51+
```
52+
53+
Note that the user has to first make the `MyLib` directory and then navigate into it.
54+
55+
By default, `swift package init` will use the directory name to define the package name, which can be changed by using the `--name` option.
56+
57+
By default, `swift package init` will set the package type to a library , which can be changed by using the `—type` option.
58+
59+
60+
### Transforming an existing directory of sources into a package
61+
62+
To transform an existing directory of sources into a package using `swift package init` users perform the following steps:
63+
64+
```console
65+
$ cd MySources
66+
$ swift package init
67+
```
68+
69+
Resulting in the following structure:
70+
71+
```console
72+
.
73+
├── .gitignore
74+
├── Package.swift
75+
├── README.md
76+
├── Sources
77+
│ └── MySources
78+
│ └── MySources.swift
79+
└── Tests
80+
└── MySourcesTests
81+
└── MySourcesTests.swift
82+
```
83+
84+
In this case, SwiftPM will only “fill the gaps”, or in other words only add `Source`, `Tests`, and the other files if they are missing.
85+
86+
By default, `swift package init` will use the directory name to define the package name, which can be changed by using the` —name` option.
87+
88+
By default, `swift package init` will set the package type to a library , which can be changed by using the `—type` option.
89+
90+
91+
## Problem Definition
92+
93+
`swift package init` is a utility to get started quickly, and is especially important to new users. The current behavior as described above can often achieve the opposite given its ambiguity and reliance on prior knowledge. Specifically, the default behavior of `swift package init` is geared towards transforming existing source directory to packages, while most new users are interested in creating new programs from scratch so they can experiment with the language.
94+
95+
A secondary issue is that `swift package init` uses a directory structure template which cannot be customized by the users. Given that SwiftPM is fairly flexible about the package’s directory structure, allowing users to define their own directory structure templates could be a good improvement for those that prefer a different default directory structure.
96+
97+
98+
# Proposed Solution
99+
100+
The identified problems could be solved by the introduction of a new command `swift package create`. This new command would live alongside of `swift package init.`
101+
102+
`swift package create` would be used to create a new package from scratch.
103+
104+
`swift package init` would be used to transform pre-existing source directory to a package.
105+
106+
Both commands will gain the capability to use a templating system such that the directory structure used is customizable by the end user.
107+
108+
109+
# Detailed Design
110+
111+
## New command: `swift package create`
112+
113+
Following, is the behavior of the new command:
114+
115+
```console
116+
$ swift package create MyApp
117+
```
118+
119+
Will create a new package with the following directory structure:
120+
121+
```console
122+
.
123+
├── Package.swift
124+
├── Sources
125+
│ └── MyApp
126+
│ └── MyApp.swift
127+
└── Tests
128+
└── MyAppTests
129+
└── MyAppTests.swift
130+
```
131+
132+
Note that `swift package create` makes an executable package by default, which is important for new users trying to get their first Swift program up and running. Such users can immediately run the new package:
133+
134+
```console
135+
$ cd MyApp
136+
$ swift run ## or omit cd, and use swift run --package-path MyApp
137+
[3/3] Linking MyApp
138+
Hello, world!
139+
```
140+
141+
142+
### Customizing the package type
143+
144+
The `--type` option is used to customize the type of package created. Available options include: `library`, `system-module`, or `executable`. For example
145+
146+
```
147+
$ swift package create MyLib --type library
148+
```
149+
150+
Will create a library package with the the following directory structure
151+
152+
```console
153+
.
154+
├── Package.swift
155+
├── Sources
156+
│ └── MyLib
157+
│ └── MyLib.swift
158+
└── Tests
159+
└── MyLibTests
160+
└── MyLibTests.swift
161+
```
162+
163+
Or, an example of creating a `system-module` package.
164+
165+
```console
166+
$ swift package create SysMod --type system-module
167+
```
168+
169+
Will create a library package with the the following directory structure
170+
171+
```console
172+
.
173+
├── Package.swift
174+
└── module.modulemap
175+
```
176+
177+
178+
## User defined templates
179+
180+
By default, `swift package create` and `swift package init` uses the following directory structure:
181+
182+
```console
183+
.
184+
├── Package.swift
185+
├── Sources
186+
│ └── <Module>
187+
│ └── <Module>.swift
188+
└── Tests
189+
└──<Module>Tests
190+
└── <Module>Tests.swift
191+
```
192+
193+
To support use cases in which individuals or teams prefer a different directory structure that they can use consistently in their projects, the proposal introduces a new configuration option named “template”.
194+
195+
Templates are defined by adding a directory to SwiftPM’s configuration directory, e.g. `~/.swiftpm/configuration/templates/new-package/<template-name>`
196+
197+
The template is a Swift package directory that SwiftPM copies and performs transformations on to create the new package. SwiftPM performs the following steps when creating a package from a template:
198+
199+
1. Copy the template directory to the target location.
200+
2. Substitute string placeholders with values that are derived from the new package request or context.
201+
3. Strip git information from the template location.
202+
203+
204+
For example, given a `test` template located in `~/.swiftpm/configuration/templates/new-package/test` with the directory structure:
205+
206+
```console
207+
.
208+
├── .git
209+
├── .gitignore
210+
├── Package.swift
211+
├── README.md
212+
├── LICENSE.md
213+
└── src
214+
└── MyApp.swift
215+
```
216+
217+
The following `Package.swift`:
218+
219+
```swift
220+
import PackageDescription
221+
222+
let package = Package(
223+
name: "___NAME___",
224+
dependencies: [
225+
.package(url: "https://github.com/apple/swift-nio.git", from: "1.0.0"),
226+
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.0.0"),
227+
],
228+
targets: [
229+
.executableTarget(
230+
name: "___NAME_AS_C99___",
231+
sources: "src",
232+
dependencies: [
233+
.product(name: "NIO", pacakge: "swift-nio"),
234+
.product(name: "`Crypto`", pacakge: "swift-crypto")
235+
]
236+
),
237+
]
238+
)
239+
```
240+
241+
And the following `README.md`:
242+
243+
```markdown
244+
### ___NAME___
245+
246+
This is the ___NAME___ package!
247+
```
248+
249+
Running `swift package init --template test --name HelloWorld`
250+
251+
Will result with the following directory structure:
252+
253+
```console
254+
.
255+
├── Package.swift
256+
├── .gitignore
257+
├── README.md
258+
├── LICENSE.md
259+
└── src
260+
└── MyApp.swift
261+
```
262+
263+
The following `Package.swift`:
264+
265+
```swift
266+
import PackageDescription
267+
268+
let package = Package(
269+
name: "HelloWorld",
270+
dependencies: [
271+
.package(url: "https://github.com/apple/swift-nio.git", from: "1.0.0"),
272+
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.0.0"),
273+
],
274+
targets: [
275+
.executableTarget(
276+
name: "HelloWorld",
277+
sources: "src",
278+
dependencies: [
279+
.product(name: "NIO", pacakge: "swift-nio"),
280+
.product(name: "Crypto", pacakge: "swift-crypto")
281+
]
282+
),
283+
]
284+
)
285+
```
286+
287+
And the following `README.md`:
288+
289+
```markdown
290+
### HelloWorld
291+
292+
This is the HelloWorld package!
293+
```
294+
295+
When the `--name` option is omitted, the name of the target directory will be used as the package name.
296+
297+
### Substitutions
298+
299+
While transforming the template directory into a package, SwiftPM performs string substitutions on all text files, using the following metadata fields:
300+
301+
1. `___NAME___`: The name provided by the user using the `--name` flag
302+
2. `___NAME_AS_C99___`: The name provided by the user using the `--name` flag, transformed to be C99 compliant
303+
304+
Future iterations of this feature will include additional metadata fields that can be used in this context.
305+
306+
307+
### Defining the default template
308+
309+
To customize the default template (i.e. when `swift package create` is invoked with the explicit `--template `argument), user define a template named “default”, i.e. `~/.swiftpm/configuration/templates/new-package/default`
310+
311+
312+
### Adding and updating templates
313+
314+
Templates are designed to be shared as git repositories. The following commands will be added to SwiftPM to facilitate adding and updating templates:
315+
316+
`swift package add-template <url> [--name <name>]`
317+
318+
Performs `git clone` of the provided URL into `~/.swiftpm/configuration/templates/new-package/,` making the template available to use immediately. The optional `--name` option can be used to set a different name from the one automatically given via the `git clone` operation.
319+
320+
`swift package update-template <name>`
321+
322+
Performs a `git update` on the template found at `~/.swiftpm/configuration/templates/new-package/<name>`.
323+
324+
325+
## Impact on SwiftPM
326+
327+
When processing `swift package create` or `swift package init`, SwiftPM will do the following
328+
329+
1. If a template is specified with `--template` option: try to load the template and use it as described above, exiting with an error if such template was not found or ran into parsing errors.
330+
2. When no template is specified with `--template` option:
331+
1. Check if a default template is defined in `~/.swiftpm/configuration/templates/new-package/default.` If one is defined, use it as described in #1 above
332+
2. If no default template is defined, construct a default `PackageTemplate` based on the `--type` option when provided, or the default type when such is not.
333+
334+
335+
## Changes to `swift package init`
336+
337+
`swift package init` will be slightly updated to reflect it’s focus on transforming existing source directories to packages:
338+
339+
1. `swift package init` will no longer add a `README.md`, and .`gitignore` files by default, reducing its impact on the existing sources directory.
340+
2. When `swift package init` is used in an empty directory, it will create a new package as it does today but emit a diagnostics message encouraging the user to use `swift package create` in the future, to help transition to the more appropriate command.
341+
3. `swift package init` will accept the new `--template` option and apply it as described above.
342+
343+
344+
# Security
345+
346+
No impact.
347+
348+
349+
# Impact on existing packages
350+
351+
No impact.
352+
353+
354+
# Alternatives considered
355+
356+
The main alternative is to modify the behavior of `swift package init` such that it better caters to the creation of new packages from scratch. The advantage of this alternative is that it maintains the API surface area. The disadvantages are that any changes to make it better for package creation are likely to make it confusing for transforming existing sources to package. More importantly, changes to the existing command may cause impact on users that have automation tied to the current behavior.
357+
358+
For templates, the main alternative is to use a data file (e.g. JSON) that describes how the package should be constructed. This would hone in the implementation as it defines a finite set of capabilities driven by configuraiton. This was not selected in order to provide a better user experience, and greater flexibility with respect to including other files in a template.
359+
360+
# Future Iterations
361+
362+
In order to provide greater flexibility than what copying a Swift package directory can provide, a future version of SwiftPM could allow packages to be created in a procedural manner. SwiftPM could introduce new APIs that provide a toolbox of functionality for creating and configuration various aspects of packages, and could invoke Swift scripts that create new packages using those APIs. Such scripts could make decisions about what content to create based on input options or other external conditions. These APIs would also function when creating a Swift package from scratch, and or transforming existing sources into a Swift Package.

0 commit comments

Comments
 (0)