Packages · Lesson 22.5

Workspace

Source
Keep a program and its library in one tree, listed as explicit members of a root [Workspace] manifest.
You'll need: Dependency, For

A project often grows into several packages that you work on together: a program, plus the libraries it is built from. You could keep them in unrelated folders and connect them with path dependencies, as Dependency did. A workspace goes one step further: it puts them in one tree under a single root manifest, so one command can check, build or lint all of them at once.

A root manifest with [Workspace]

The Rux.toml at the root of this lesson has no [Package] section. It has [Workspace] instead:

Rux.toml
[Manifest]
Version = 1
MinRux = "0.4.0"

[Workspace]
Packages = [
    "App",
    "Temperature",
]

Packages lists every member by its folder. The list is explicit: there are no wildcards, and every member must sit inside the root, so adding a package to the project means adding a line here. A manifest has either [Package] or [Workspace], never both — a workspace is not a package. It has no source of its own, declares no dependencies and has nothing to run.

Workspace/
├── Rux.toml              [Workspace], listing the two members
├── App/
│   ├── Rux.toml          Type = "Executable"
│   └── Src/Main.rux
└── Temperature/
    ├── Rux.toml          Type = "SourceLibrary"
    └── Src/Temperature.rux

Members are ordinary packages

Each member has its own manifest and is written exactly as a lone package would be. Being listed changes nothing about it — and in particular, membership does not create dependencies. The program still names the library in its own App/Rux.toml:

Temperature = { Path = "../Temperature" }

Then App/Src/Main.rux imports it like any other package:

import Io::PrintLine;
import Temperature::ToFahrenheit;

So a workspace answers "which packages make up this project?", while each member's [Dependencies] still answers "which packages does this one use?":

flowchart LR
    root(["Workspace/Rux.toml<br/>[Workspace]"]) -. "member" .-> app["App<br/>Executable"]
    root -. "member" .-> temp["Temperature<br/>SourceLibrary"]
    app -- "Path = ../Temperature" --> temp
    app --> exe["App.exe"]

Commands at the root and in a member

At the root, rux works on every member in turn:

Where you areCommandWhat happens
rootrux checkchecks both members: Checked 2 packages
rootrux buildbuilds App; the source library has nothing to build
rootrux lintlints every member
rootrux runrefused — the workspace has nothing to run
rootrux --manifest App/Rux.toml runruns the program without changing folder
App/rux runbuilds and runs the program, as for any package

--manifest is a global option: it tells any rux command which Rux.toml to use instead of the one in the current folder.

The program

The whole lesson is one package in the Examples repository. Its comments explain every step.

Temperature/Src/Temperature.rux
// The library member of the workspace. It is an ordinary package with its own manifest; being
// listed in the workspace changes nothing about how it is written.
pub func ToFahrenheit(celsius: float64) -> float64 {
    return celsius * 9.0 / 5.0 + 32.0;
}

Run it

cd Examples/Packages/Workspace/App
rux run
-40.0 C = -40.0 F
0.0 C = 32.0 F
21.5 C = 70.7 F
100.0 C = 212.0 F

From the root, rux check, rux build and rux lint work on every member. rux run there stops, because the workspace "has nothing to run", and suggests rux --manifest App/Rux.toml run, which runs the program without changing directory.

Common mistakes

Running the workspace.
rux run at the root stops with error: manifest '…\Workspace\Rux.toml' is a workspace and has nothing to run, and the help line suggests rux --manifest App/Rux.toml run. Run that, or change into App/ first.
Expecting membership to connect packages.
Remove the Temperature line from App/Rux.toml and the import fails, even though both packages are members: error: package 'Temperature' is not listed in [Dependencies]. Each member declares what it uses.
A wildcard in the member list.
Packages = ["*"] is not a pattern. rux takes it as a folder name and reports error: workspace member '*' has no Rux.toml. List every member by name.
A member outside the root.
"../Dependency" in the list fails with error: '[Workspace].Packages item' cannot contain a '..' component. A workspace owns its members, so they live inside it. A package elsewhere can still be a path dependency.

Try it yourself

  1. From the workspace root, run rux check, then rux --manifest App/Rux.toml run.
  2. Delete "Temperature", from the root manifest and run rux check at the root again. How many packages are checked now — and does App still build?
  3. Add a third member: a source library called Kelvin with a pub function ToKelvin(celsius: float64) -> float64. List it in the workspace, depend on it from App, and print each reading in kelvin too.

Learn more