Skip to main content
aspect run builds a target and runs the resulting binary, as bazel run does, inside the Aspect task lifecycle: health checks, status reporting and task hooks, the same as aspect build.
Arguments after -- go to the binary unchanged. The binary runs with the args and env attributes of its target, the way bazel run runs it.

Flag passthrough

Type Bazel flags directly. A flag that aspect run doesn’t define itself is forwarded to Bazel, in the slot you typed it in: Both spellings of a value work, --jobs=8 and --jobs 8, as do short forms such as -c opt. Changing startup options restarts the Bazel server. The rule is the same for every task that runs Bazel. A flag the task does define is the task’s, so when a Bazel flag has the same name as one of the task’s own, pass it with --bazel-flag (or --bazel-startup-flag for a startup option):

Options

Set --run-in for a binary that resolves file arguments against its working directory instead of $BUILD_WORKSPACE_DIRECTORY, such as a bare Go binary with no shell wrapper.

Watch mode

Pass --watch to keep the binary current while you edit. The task watches the Bazel workspace and rebuilds the target after each settled batch of file changes:
  • Successful rebuild that changes the target’s runfiles: unless the binary uses the ibazel protocol or the Incremental Build Protocol, the task terminates the running process tree and starts the new executable with the same arguments.
  • Successful rebuild with unchanged runfiles: the current process keeps running, and the task reports the target as unchanged. An edit that doesn’t affect the binary or its runfiles, such as a change to an unrelated package, doesn’t restart it.
  • Failed rebuild: the current process keeps running, and the task waits for the next change.
  • Target exits on its own: the task reports the exit code and keeps watching. The next successful rebuild starts it again.
When the task can’t tell exactly what changed, for example after the file watcher reports a full rescan, it restarts the target to be safe. The task picks how to update the running target from its tags:
  1. ibazel_notify_changes tag: the task uses the ibazel protocol and notifies the target on stdin.
  2. Otherwise: the task offers the Incremental Build Protocol. The supports_incremental_build_protocol tag makes the connection required.
  3. No protocol in use: the task restarts the target when its runfiles change.
Changes under .git, .jj and Bazel’s convenience symlinks (bazel-bin, bazel-out and so on) don’t trigger a rebuild. Stop the task with Ctrl-C. On Linux and macOS the task starts the target in its own process group and terminates the whole group on restart, so child processes the binary spawned stop too. On Windows it terminates only the immediate child process, so processes that the binary starts can outlive a restart. Without --watch, aspect run builds and runs the target once.

ibazel protocol

aspect run --watch speaks the live-reload protocol of ibazel (bazel-watcher). Targets written for ibazel run, such as dev servers that reload when they read a notification on stdin, work with aspect run --watch without changes. To opt a target in, add the ibazel_notify_changes tag:
For a tagged target, the task:
  • Sets IBAZEL_NOTIFY_CHANGES=y in the target’s environment and connects a pipe to its stdin.
  • Writes IBAZEL_BUILD_STARTED to stdin when a rebuild starts.
  • Writes IBAZEL_BUILD_COMPLETED SUCCESS or IBAZEL_BUILD_COMPLETED FAILURE to stdin when the rebuild finishes, including after failed builds.
  • Keeps the process running after the notification. The target decides how to reload.
The task still restarts the process when the target’s executable or entrypoint changes, or when writing a notification fails. A tagged target always uses the ibazel protocol, even if it also carries supports_incremental_build_protocol. The ibazel protocol works on every platform, including Windows.

Incremental Build Protocol

A binary that implements the Incremental Build Protocol (IBP) can apply changes in place instead of being restarted. Dev servers and bundlers with hot reload use this to keep their state across rebuilds. In watch mode, the task opens a Unix socket and passes its path to the binary in the IBP_WATCH_SOCKET_FILE environment variable. The task also sets ABAZEL_WATCH_SOCKET_FILE to the same path for older clients. When the binary connects, the two sides negotiate a protocol version and a change scope. After each build, the task sends the changed files to the running process as a cycle message rather than killing it. To require the protocol for a target, add the supports_incremental_build_protocol tag:
  • Tagged targets: the task waits for the binary to connect. If it doesn’t connect within the timeout, or negotiation fails, aspect run stops the target and fails.
  • Untagged targets: the socket variables are still set. If the binary connects, the task uses the protocol. If negotiation fails, the task warns and falls back to process restarts for the rest of the session.
The binary requests one of two scopes during negotiation: If the client requests both, the task uses sources. The task still restarts the process when a restart is required: when the target’s executable or entrypoint changes, when it can’t determine the change set (for example after a full rescan), or when sending an update fails. A build with no changes in the negotiated scope leaves the process running unchanged. Set ASPECT_WATCH_CONNECTION_TIMEOUT_MS to change how long the task waits for a tagged binary to connect. The default is 10000 (10 seconds). The value must be a positive integer:
The protocol isn’t available on Windows. There, the task warns about tagged targets and uses process restarts.

When to use aspect run

  • Local development: the same lifecycle and output as aspect build and aspect test.
  • CI steps: running a binary as a pipeline step reports like a build or test step.
  • One-off tools: run a binary built from source without a wrapper script.
  • Iterating on a service: --watch rebuilds and restarts or reloads it as you edit, including targets built for ibazel.
aspect run waits for the binary to exit, so start long-running services in CI some other way.

Comparison with bazel run