> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reasonos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Packages and targets

> How workspaces, build files, labels, and target patterns work in rbs

Every rbs workspace is a directory tree with a `WORKSPACE.rbs` file at its root. Inside
it, any directory that contains a build file is a **package**, and each package declares
one or more **targets** — the things you build, test, and run. Targets are addressed by
**labels** like `//services/api:server`.

## The workspace

The workspace root is marked by a `WORKSPACE.rbs` file. Every rbs command finds the root
by walking up from your current directory, so you can run commands from anywhere inside
the workspace. `WORKSPACE.rbs` is also where you declare the hermetic toolchains your
workspace uses — rbs downloads and manages them itself, so nothing needs to be installed
on the machine:

```python theme={null}
# WORKSPACE.rbs
load("@rbs//go/toolchain.rbs", "go_toolchain")

go_toolchain(
    name = "go",
    version = "1.24.3",
)
```

<Note>
  rbs keeps per-workspace build state in a `.rbs/` directory next to `WORKSPACE.rbs`
  (build outputs, launchers, test logs). It is disposable — safe to delete, never commit
  it. Cached action results live in the shared content-addressed cache outside the
  workspace, so deleting `.rbs/` does not throw away cached work.
</Note>

You can point a command at a specific workspace with `--workspace-root` or the
`RBS_WORKSPACE_ROOT` environment variable instead of relying on auto-discovery.

## Packages and build files

A **package** is any directory containing a file named `BUILD.rbs` (lowercase
`build.rbs` is also accepted). The package's path relative to the workspace root is its
name: the build file at `services/api/BUILD.rbs` defines the package `services/api`. A
build file at the workspace root defines the root package.

Build files are written in the RBS language — a small, deterministic, Python-like
language. A build file loads the rules it needs and calls them to declare targets:

```python theme={null}
# services/api/BUILD.rbs
load("@rbs//go/rules.rbs", "go_binary", "go_library")

go_library(
    name = "core",
    srcs = glob(["internal/**/*.go"]),
)

go_binary(
    name = "server",
    srcs = ["main.go"],
    deps = [":core"],
)
```

`glob()` is available in every build file without a load. It matches files relative to
the package directory:

```python theme={null}
glob(["src/**/*.ts"], exclude = ["src/**/*.test.ts"])
```

Directories starting with `.` are never treated as packages, so a build file inside a
hidden directory is ignored.

## Labels

A label names one target. The full form is `//package/path:target_name`:

| Form                    | Meaning                                                                         |
| ----------------------- | ------------------------------------------------------------------------------- |
| `//services/api:server` | Target `server` in package `services/api`                                       |
| `:server`               | Target `server` in the **current** package (relative to your working directory) |
| `services/api:server`   | Same as the `//` form, written relative to the workspace root                   |
| `server`                | Bare name — target `server` in the current package                              |

Inside a build file, use `:name` for targets in the same package and
`//package:name` for targets in other packages.

## Target patterns

The `build`, `test`, `run`, `query`, and `coverage` commands all accept the same pattern
syntax for addressing sets of targets:

| Pattern                 | Matches                                            |
| ----------------------- | -------------------------------------------------- |
| `//...`                 | Every target in the workspace                      |
| `//services/...`        | Every target in every package under `services/`    |
| `...`                   | Every target under the current directory's package |
| `//services/api:all`    | Every target in exactly one package                |
| `:all`                  | Every target in the current package                |
| `//services/api:server` | One target                                         |

```bash theme={null}
rbs build //...                # build everything
rbs query //services/...       # list all targets under services/
rbs test :all                  # run this package's tests
```

Most commands also accept a **natural syntax**: a package path followed by one or more
target names, which is convenient for building several targets in one package:

```bash theme={null}
rbs build services/api server migrate    # same as //services/api:server //services/api:migrate
```

## Depending on other targets

The `deps` attribute wires targets together. rbs resolves the whole dependency closure,
builds it in dependency order, and runs independent targets in parallel:

```python theme={null}
go_binary(
    name = "server",
    srcs = ["main.go"],
    deps = [
        ":core",                    # same package
        "//libs/auth:auth",         # another package
    ],
)
```

Third-party packages from a language ecosystem are addressed with `@external://` labels,
pinned to a version:

```python theme={null}
load("@rbs//web-vite/vitest.rbs", "vitest_test")

vitest_test(
    name = "counter_test",
    srcs = ["src/Counter.tsx", "src/Counter.test.tsx"],
    deps = [
        "@external://react:19.0.0",
        "@external://react-dom:19.0.0",
        "@external://@testing-library/react:16.3.0",
    ],
)
```

rbs resolves external dependencies itself and shares them across workspaces through the
shared cache — there is no `npm install` or `pip install` step.

## `load()` semantics

`load()` imports named symbols from another `.rbs` module. Four path forms exist:

```python theme={null}
load("@rbs//go/rules.rbs", "go_binary")      # built-in rules shipped with rbs
load("//tools/macros.rbs", "release_binary") # workspace-relative
load("helpers.rbs", "common_srcs")           # relative to the current package
load("@mycompany//lint/rules.rbs", "lint")   # an external rule package (managed by `rbs ext`)
```

You can rename a symbol as you load it:

```python theme={null}
load("@rbs//go/rules.rbs", go_bin = "go_binary")
```

### Gotchas worth knowing

<Warning>
  **`load()` bindings are file-local — they are not re-exported.** If module `a.rbs` loads
  `y` from `x.rbs`, a file that loads `a.rbs` does **not** see `y`. An aggregator module
  must re-export explicitly:

  ```python theme={null}
  # prelude.rbs
  load("x.rbs", _y = "y")
  y = _y          # now loadable from prelude.rbs
  ```

  Without the assignment, the aggregator silently exports nothing.
</Warning>

<Warning>
  **No implicit string concatenation.** Adjacent string literals are a parse error in the
  RBS language. Use explicit `+` or `join`:

  ```python theme={null}
  # WRONG - parse error
  cmd = "echo hello" " world"

  # Right
  cmd = "echo hello" + " world"
  lines = "\n".join(["a", "b", "c"])
  ```
</Warning>

Other language properties to keep in mind:

* Build files are **declarative**: evaluating a build file only registers targets — no
  compilation or command runs until you invoke `rbs build`.
* `glob()` is the way to enumerate source files; there is no general file I/O in build
  files.
* Loaded modules are cached, so loading the same module from many files is cheap.
