Hi Folks,
This is a promising effort and great discussion. As a distant observer my urge is to simplify and ship!
Why not just have a .swift file declare a PackageDependencies.Script object with a bootstrap static package member and a main entry point for SwiftPM tooling to build/run?
Easy scripting could really help expand the Swift ecosystem. This could have HUGE benefits to the community.
But proliferating swift syntax and alternate build systems can really complicate the ecosystem. Scripting should add near-zero complexity or confusion.
I prefer a single source of truth for configuration, and would not want to see @package
url's in Doit.swift
code conflicting with Package.swift
statements. I also don't see package-imports as the only problem now with scripts (hash-bang's? shared build locations? reusing scripts?), so I don't see language tweaks converging in a summer on something viable (assuming we prefer shipping over blogging :)
So, perhaps reduce requirements to minimum-viable and strengthen constraints...
Goal is to write the smallest tool extension to build+run a smallish single-file script with only package dependencies.
So, instead of
MyPackage/
Sources/
Source.swift
main.swift
Package.swift
We have
SomeScripts/
Dothis.swift
Dothat.buildrun.swift
...
Some constraints:
-
No syntax changes in the language. sourcekit-lsp should handle scripts without modification, and scripts should compile like any other .swift file (if moved to an appropriate package).
-
Scripts use exactly the same build SwiftPM caching and build logic as swift packages. No new ./built-here locations (but see below).
-
(Scripts should not be run automatically during builds. I shouldn't be running untrusted code just by building a package I import.)
As for implementation, I would add type Script
to PackageDependencies
; Script
would declare the package and main entry point. Then new buildrun
tool mode (i.e., your SwiftPM script
) could bootstrap:
If the swift file only declares a single Script object, swift buildrun .../Doit.swift
will interpret the Script static member package: Package
declaration to bootstrap the build for the script object, and optionally run the declared main entry point.
Then it's just a matter of API design and coding conventions for a Script implementation to reduce the package declaration and CLI using reasonable defaults/conventions, and some tooling shims (or script wrappers) to implement build-run.
Notice in v1 that scripts are leaves of a dependency tree:
- Nothing depends on script code.
- Script code only depends on packages, not on other swift files.
This I think reduces scripts to small CLI controllers assembling functionality from normal packages; anything larger should just be in a regular package (or deferred to v-1+). (Thus the dev cycle: hack a script, if anything reusable evolves, throw it into a package, iterate...)
Swift tooling would need to support this bootstrapping, and the .build directory layout updated to handle multiple nested scripts, e.g., if scripts are in SomeScripts/
, then SomeScripts/.build/scripts/Doit/{normal package layout}
(or an option for a user-wide build directory). swift buildrun
might support a base directory for local packages (preferably relative to the jailed script directory tree in v1).
Simple IDE launch support might depend on using an extended suffix, e.g., Doit.buildrun.swift
. For v1 CLI, users would deploy shell mechanisms (e.g. aliases/functions) to avoid a hash-bang at the top of the file, perhaps based on the same Doit.buildrun.swift
convention.
As for script boundaries:
-
Script package names are not visible outside the script and might not be consistent across builds.
-
There is no sharing between script build environments (at least in v1), resulting in significant duplication of commonly-imported packages.
-
When migrating the script code into a package, the excess package declaration is ignored. I.e., it is an error to include the script file in a regular package, unless the regular package dependencies are sufficient. In this case, no attempt is made to detect deltas between the script-internal package declaration and the external package declaration.
If this could be implemented in a summer, it would be a great first step!
Wes