Ecosystems

An ecosystem is a set of target kinds for one language, platform, or build domain. It gives Once enough information to validate dependencies, explain what a target can do, and cache work from declared inputs and outputs.

Choose Between a Typed Target and a Script#

Use an ecosystem target kind when Once should understand the artifact and its relationships. This is a good fit when you want to:

  • query targets and capabilities before doing work;
  • validate dependencies and attributes early;
  • reuse build outputs across targets or machines;
  • expose focused build, run, and test operations to coding agents.

Use a script target when the workflow is still exploratory or the native feature you need has not been modeled. A native package manager can remain the source of truth while a typed dependency target imports its locked result into Once. Scripts still participate in the graph, so a project can begin there and adopt typed targets one boundary at a time.

An existing build system can use the same bridge. For CMake, cmake_project wraps configure and build as a typed coarse action, while cmake_workspace imports a checked configured graph for queries. CMake remains authoritative in both cases.

Import Locked Third-Party Graphs#

Dependency targets do not replace package managers. The native manager owns manifest semantics, version selection, registry policy, and lock updates. Once imports the locked result, creates one queryable target per package instance, and connects those targets to first-party consumers.

EcosystemImport targetLocked inputFirst-party consumer
Gogo_dependenciesgo.mod, go.sum, and vendor/modules.txtgo_library, go_binary, or go_test
Rustcargo_dependenciesCargo.lock and Cargo metadatarust_library, rust_binary, or rust_test
Elixirmix_dependenciesmix.lock and the active Mix graphelixir_library
Swiftswift_package_dependenciesPackage.resolved and a package graph snapshotApple libraries, frameworks, applications, or tests
React Nativereact_native_dependenciespackage.json, an npm, pnpm, Yarn, or Bun lockfile, and an optional native-module snapshotMetro bundles or Apple and Android applications
Zigzig_dependenciesbuild.zig.zon package recordsZig modules, binaries, libraries, or tests

The update workflow is deliberately separate from an ordinary build:

  1. Use the native manager to update the manifest and lock file.
  2. Fetch or vendor the exact sources and verify their native integrity values.
  3. Query the imported package targets before building a consumer.
  4. Build or run a first-party target whose deps includes the dependency set.

Ordinary builds do not update lock files. Cargo, Mix, and Zig integrations expect exact sources to be materialized before graph execution. Swift supports local packages or a complete vendored package-manager scratch tree. Use one of those Swift layouts to keep network access and mutable registry state outside compile actions. Live inspection of an unvendored remote Swift package requires an explicit allow_network = true. Once disables automatic version selection, but it does not independently sandbox Swift Package Manager network access when a vendored scratch tree is incomplete. In every case, the actual source contents remain part of action keys.

Full Ecosystem Guides#

Full build ecosystems have incremental guides with a first project, query-before-build workflow, current limitations, and follow-up steps:

  • Apple covers libraries, frameworks, applications, and tests written in Swift, Objective-C, C, and C++.
  • Swift Packages covers locked package import, static products, and Apple consumers.
  • Android covers resources, Java and Kotlin libraries, application packages, and host or device tests.
  • C and C++ covers headers, source compilation, static libraries, and native consumers.
  • CMake covers coarse cached execution, exact products, checked graph snapshots, and incremental adoption.
  • Elixir covers compiled applications and ExUnit tests.
  • Kotlin covers Java virtual machine libraries and binaries, Android Kotlin sources, and Kotlin/Native Apple frameworks.
  • Go covers package archives, executables, tests, cgo, cross-compilation, and locked vendored modules.
  • Rust starts from an existing Cargo.toml, then covers optional manifest adoption, libraries, binaries, tests, procedural macros, Cargo dependencies, and native mobile outputs.
  • React Native covers New Architecture dependencies, native modules, JavaScript bundles, Apple and Android applications, and Fast Refresh.
  • Zig covers modules, binaries, tests, libraries, and C or C++ dependencies.

Test Runner References#

Test-only integrations have focused reference pages with prerequisites, copyable declarations, runnable starters, and first-run commands:

Continue with Testing and Scheduling for the shared first-run workflow, affected selection, exact execution, and automatic batching across these runners.

Use the target kind index after choosing an ecosystem. It lists the exact schema, dependencies, capabilities, outputs, and current limitations for every kind.

Shared Mobile Code#

Some target kinds cross platform boundaries while preserving normal graph dependencies:

Use these after the application builds without shared code. That sequence keeps toolchain and linking problems separate from the first graph setup.

Adopt an Ecosystem Incrementally#

  1. Pick the smallest artifact that has stable inputs and outputs.
  2. Declare one target and run once query schema <kind>.
  3. Run once query capabilities <target> and build that same target.
  4. Connect one consumer through deps and query the resulting graph.
  5. Add run or test targets only after the artifact build is reliable.
  6. Keep unsupported edges behind scripts until a typed target kind covers them.

This sequence leaves the native package manager or build system in place for the parts Once does not own. A project does not need to move an entire ecosystem at once.

A coding harness can also fetch the authoritative external rule or plugin, query Once's live module-authoring contract, and maintain that local target kind for the project. This is useful when the project needs a narrow dependency slice and a general built-in integration would be unnecessary. See Coding harnesses.

Check the Boundary Before Adopting#

Feature coverage varies by ecosystem. Before moving a production workflow, check the target kind reference for:

  • supported source, resource, and dependency shapes;
  • ownership of third-party dependency resolution;
  • required compilers and platform tools;
  • cacheable artifacts and non-cacheable runtime effects;
  • unsupported attributes and whether they fail validation;
  • editor or language-service integration the existing workflow still needs.

Unsupported does not have to mean blocked. Keep that behavior in a script, define a checked-in local module, or contribute the missing typed behavior. The important part is to keep the boundary visible so readers and agents can tell which system owns each step.