Reference

Set up a workspace

Name the member directories in the root project.trb, give each one its own project.trb, and depend on a sibling by name alone - the workspace resolves it from source.

A workspace is a root project.trb that names several member projects, sharing one dependency resolution between them. It is also the one way, until a registry exists, to depend on a package that is not std/*: the member has to be there in source, and the workspace is what makes that legal.

Steps#

  1. Lay out one directory per member, each with its own project.trb. A member is an ordinary project; nothing about its own file changes because it will be listed by a root.

    shop/
    ├ packages/
    ├─ core/            project.trb: name = "acme/shop-core"
    ├─ api/              project.trb: name = "acme/shop-api"
    ├ tools/
    ├─ importer/
    ├ project.trb
    └ project.lock.trb
  2. Name the members from the root, with a directory or a directory/* pattern. members "packages/*" picks up every project directly below packages/; members "tools/importer" names one project exactly.

    workspace {
      members "packages/*", "tools/importer"
    }
  3. Depend on a sibling the same way as any other package, by name in dependencies { }. Because the name matches a member, the workspace resolves it from that member's source instead of anywhere else, and no version is needed.

    dependencies {
      runtime "acme/shop-core"
    }
  4. Run check, build and test at the root to work across every member at once, in the order their dependencies require: torb build builds every program of every member, and a member without a program - a library - is checked and builds nothing. Point the same commands at one member's directory - torb build ./packages/api - to work on it alone.

  5. Commit one project.lock.trb, at the root. Every member shares that resolution, so two members can never end up depending on different versions of the same package.

Pitfalls#

  • A member still has to declare the sibling it uses. Being in the same workspace changes where a dependency's source comes from, not whether dependencies { } has to name it - a member that imports another without declaring it gets the same diagnostic an unrelated project would.
  • A cycle between members is an error. The root works through members in the order their dependencies require, which a cycle between two members makes impossible to compute; a cycle between ordinary modules of one package is fine, because nothing runs when a module is only imported.
  • version, authors, the registries and the profile blocks are inherited from the root, not copied. A member that sets its own overrides the root for itself only, and one that sets nothing changes when the root does.
  • A sibling is imported by its name, never by a relative path. "../../core/src/cart" from inside api is an error that says it leaves the package; "acme/shop-core/cart" is the import.
  • The root does not need an empty src/. workspace { members ... } can stand next to a root that also has its own sources; the two are independent.

Full example#

name = "acme/shop"
version = "1.4.0"

workspace {
  members "packages/*", "tools/importer"
}

The project.trb of one member, depending on a sibling from source:

name = "acme/shop-api"
version = "1.4.0"

dependencies {
  runtime "acme/shop-core"
}
  • Workspaces - the member, root and lock-file rules in full.
  • Add a dependency - declaring what a project or a member depends on.
  • Packages - what an ordinary project is, member or not.
Esc