Hi all,
Based on the earlier discussion and feedback (especially around resolution timing and scope), I’ve refined the proposal for introducing a systemExecutable target type.
This draft clarifies:
-
Target graph semantics
-
Resolution timing (before build planning, aligned with systemLibrary)
-
Provider model (including npm and pip)
-
Explicit non-goals to keep scope controlled
I’d especially appreciate feedback on:
-
Whether resolution before build planning is the correct integration point.
-
Whether restricting dependencies to plugin targets is the right constraint.
-
Whether PATH-only resolution is sufficient for the initial version.
-
Whether including npm and pip providers is appropriate for initial scope.
Full draft below:
[Pitch] Add systemExecutable Target Type to SwiftPM
Introduction
This proposal introduces a new target type in Swift Package Manager (SwiftPM) named systemExecutable. This target enables packages to declaratively specify dependencies on externally installed command-line tools required by plugins.
The goal of this proposal is to improve developer experience, increase build determinism, and provide structured diagnostics for plugin-driven workflows that rely on external tooling.
Motivation
SwiftPM models Swift source dependencies declaratively in Package.swift, but external executable tools required by plugins remain implicit and unmanaged. This asymmetry creates friction in modern plugin-based workflows and weakens the declarative guarantees SwiftPM otherwise provides.
Common examples include:
-
Protocol Buffers (protoc) code generation
-
OpenAPI / Swagger generators
-
GraphQL schema compilers
-
IDL and code-generation tools distributed via npm or pip
Today, these tools are typically resolved via PATH lookup during plugin execution. If the executable is missing, users encounter a late-stage “command not found” error without structured guidance.
This results in:
SwiftPM provides structured dependency modelling for Swift code, but executable tool dependencies remain invisible and undeclared.
Proposed Solution
Introduce a new systemExecutable target type that allows packages to declaratively specify external executable dependencies.
Example manifest syntax:
.systemExecutable(
name: "protoc",
providers: [
.brew(["protobuf"]),
.apt(["protobuf-compiler"]),
.npm(["@bufbuild/protoc"])
]
)
Plugin targets may depend on these targets:
.plugin(
name: "GenerateProtos",
capability: .buildTool(),
dependencies: ["protoc"]
)
Detailed Design
Target Graph Semantics
A systemExecutable target:
-
Does not produce artifacts
-
Does not participate in linking
-
Exists solely to validate the presence of an external executable
Only plugin targets may depend on systemExecutable targets.
If a non-plugin target depends on one, SwiftPM should emit a manifest error.
This models executable tools as declarative, pre-build dependencies rather than runtime assumptions.
Resolution Timing
Executable resolution occurs before the build phase, during target graph construction or build planning. This mirrors the behaviour of systemLibrary targets.
Resolution happens prior to plugin invocation to ensure early, deterministic failure.
Resolution Algorithm
For each declared systemExecutable target:
-
SwiftPM attempts to locate the executable using standard PATH resolution.
-
If the executable is found, resolution succeeds.
-
If the executable is not found, SwiftPM emits a structured diagnostic including installation suggestions derived from declared providers.
-
The build process is halted prior to plugin invocation.
This proposal does not introduce:
Provider Model
Providers extend the existing system library provider concept to include both OS-level and language-specific ecosystems:
-
.brew([String])
-
.apt([String])
-
.yum([String])
-
.npm([String])
-
.pip([String])
Providers are advisory only and are used solely to generate installation suggestions in diagnostics.
Cross-Platform Behaviour
systemExecutable targets are platform-neutral.
SwiftPM is responsible only for validating executable presence. Platform-specific command-line differences remain the responsibility of the plugin implementation.
Provider suggestions may vary by operating system.
User Experience Comparison
| Aspect |
Current Behaviour |
With systemExecutable |
| Tool Declaration |
Implicit |
Declarative in Package.swift |
| Resolution Timing |
During plugin execution |
Before build planning |
| Failure Mode |
Generic “command not found” |
Structured diagnostic with install suggestions |
| CI Reliability |
Environment-dependent |
Deterministic validation |
| Onboarding |
Manual setup documentation |
Guided installation suggestions |
This elevates external executable tools to first-class declarative dependencies.
Impact on Existing Packages
This proposal is fully additive and does not modify the behavior of existing packages.
Packages that do not declare systemExecutable targets are unaffected.
Plugins currently relying on implicit PATH resolution may optionally migrate to systemExecutable targets for improved diagnostics.
Alternatives Considered
Status Quo (PATH-only Resolution)
Rejected because:
-
Failures occur late
-
Diagnostics lack structure
-
CI behaviour becomes non-deterministic
-
External tool dependencies remain invisible
Extending Plugin Targets Directly
Rejected because:
Automatic Tool Installation
Rejected due to:
-
Security concerns
-
Scope expansion
-
Ecosystem complexity
Version Management
Rejected for initial scope due to:
Security Considerations
This proposal does not execute, download, or install external software.
It only validates executable presence and emits advisory diagnostics.
SwiftPM does not modify system state or elevate privileges. Existing trust boundaries remain unchanged.
Future Directions
Possible future enhancements include:
-
Version validation
-
Explicit path configuration
-
Swift SDK bundle integration
-
Additional provider ecosystems
These are intentionally out of scope for the initial proposal.
Implementation Overview
Implementation will require:
-
Adding a new systemExecutable target type to the manifest model
-
Extending target graph construction to validate executable presence before build
-
Implementing PATH-based lookup logic
-
Extending provider enums for additional ecosystems
-
Emitting structured diagnostics consistent with systemLibrary targets
-
Adding test coverage across both build systems
No changes to the Swift compiler are required.
Questions for Feedback
I would especially appreciate feedback on:
-
Whether resolution timing before build planning aligns with SwiftPM architecture expectations.
-
Whether restricting dependencies to plugin targets is the right constraint.
-
Whether supporting npm and pip providers is appropriate for the initial scope.
-
Whether PATH-only resolution is sufficient for v1.
Thank you for taking the time to review this proposal.