May 31, 2017·5 min read·engineeringswiftpackagescli

The accepted swift run command preserves the caller's directory

SE-0179 combines building and running package executables. The accepted revisions settle how executable selection and working directories should behave at the shell.

Running an executable under development should not require knowing the package manager's output directory. Swift Package Manager currently makes the developer build first and then invoke a file beneath .build/debug. A planned platform-specific output layout makes that path more cumbersome. SE-0179 gives the package manager responsibility for locating and running the executable it builds.

The interesting part of the May 31 acceptance decision is the treatment of the working directory. Reviewers supported the command, but the core team revised several details so that using it would behave naturally at the shell. Choosing a package must not silently redefine the directory in which the resulting program operates. That matters for any command whose arguments name files relative to the caller.

I find Swift's design strongest when ordinary behavior has a clear rule. This proposal applies that preference to a small development operation. Building and running an executable becomes simpler, while the accepted directory behavior keeps the program's environment understandable to someone already using a shell.

The package manager knows the executable product

David Hart's proposal as reviewed describes the existing two-step workflow. The developer runs swift build, then executes .build/debug/myexecutable. The proposal says Swift 4 will add a platform subdirectory to the build path, with examples for macOS and Linux. A developer who spells the output path directly therefore takes on knowledge of the build layout.

The accepted command builds an executable product when necessary and runs it. Its selection comes from the products declared by the package manifest. For a package with an executable product named myexecutable, the intended invocation is the following.

swift run myexecutable
swift run myexecutable input.txt

The second form passes the remaining argument to the executable. The package manager handles the build step and product lookup, so the caller does not have to reconstruct an output path. This is particularly useful when the same package is developed on platforms with different output directories.

No-argument swift run is also accepted. It selects the executable when the package defines exactly one executable product. With several executable products, the name is necessary to resolve the ambiguity. The rule uses package structure to provide a convenient default only where the structure supplies one answer.

The proposal includes --skip-build for callers who deliberately want to omit building. Building remains the default, consistent with swift test. That consistency gives the usual development operation a predictable meaning. The command is intended to run the product corresponding to the current source after any necessary build work, unless the caller explicitly chooses otherwise.

The acceptance removes an ambiguous shorthand

The reviewed text allowed an argument separator to select an implicit executable and pass arguments to it. The decision explicitly rejects that form. Its example of the rejected syntax is useful because an older copy of the proposal can still show it.

swift run -- arg1 arg2 arg3

The core team did not consider this shorthand sufficiently useful compared with spelling the executable name. Keeping it would create two ways to express the same operation. The decision leaves open the possibility of adding it later if experience establishes a need, but it is outside the accepted design now.

This distinction between proposal text and decision is material for documentation. The accepted no-argument form does not imply that every form omitting the executable name is accepted. Once arguments need forwarding, the executable name supplies an explicit boundary between product selection and arguments for that product. An example copied unchanged from the reviewed draft can describe behavior the review removed.

The review ran from May 15 through May 24. Daniel Dunbar's decision reports unanimous general support alongside discussion of argument forms, default building and directory behavior. Acceptance with revision therefore records a specific design outcome. It should not be read as approval of every option in the original help listing.

Package location and execution location are independent

The original draft described both --chdir and --in-dir. One changed the working directory before other operations, while the other changed it before running the executable. The decision agrees that this arrangement is confusing and removes the second option.

The existing --chdir option will become --package-path. Its job will be to identify the package to operate on, without changing directories as a side effect. Swift 4 will deprecate --chdir, with removal planned for a later date. The name --package-path also leaves --package available for a possible future package-name operation, according to the decision.

Most importantly, swift run will execute the product in the directory from which the caller invoked swift run. A package stored elsewhere can provide the executable while the caller's directory continues to determine the meaning of relative file paths. This follows the decision's stated aim of making the invocation behave like a product built, installed and run through the shell normally.

Consider an executable that consumes input.txt. Identifying the package and identifying the input file are different operations. Changing into the package directory as an incidental step could make the same argument refer to a different file. Keeping execution in the caller's directory avoids that hidden change in the program's interpretation of its arguments.

A development command has a defined scope

The proposal considers maintaining compatibility symlinks for .build/debug and .build/release. Such links might help tools that already depend on those locations, but they would not remove the value of a command that knows the selected executable product. Product lookup and a conventional output path solve related but different problems.

Skipping the build also changes what an invocation establishes. A successful run then shows the behavior of an existing executable, which may not contain the latest source edits. Leaving building enabled by default avoids requiring every ordinary development invocation to make that choice explicitly.

I value the resulting reduction in build-specific knowledge at the command line. The accepted design still requires an executable name when forwarding arguments, and it preserves the ordinary meaning of the caller's directory. As of this decision it is accepted for Swift 4. The implementation work can now follow those rules, including the revisions that distinguish the accepted command from the earlier draft.