summaryrefslogtreecommitdiff
path: root/README.md
blob: ddb33f0a7eb45c0487ee963523b4d3ee89579bad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# Justbuild on Nix

This repository demonstrates a way to build locally with
[justbuild](https://github.com/just-buildsystem/justbuild)
using dependencies from [Nix](https://nixos.org/) in a clean and
versioned way.

## Background: Standard Paths on non-Nix Distributions

On many non-Nix Linux distributions the local tools are installed to
a small number of "standard" paths (like `/bin`, `/sbin`, `/usr/bin`,
`/usr/sbin`). Moreover, tools like `sh` (used by default by the
built-in `"generic"` rule) and `env` (the default action launcher)
are configured to take those standard paths into account if the
`PATH` environment variable is not set on their startup. Hence
the tools installed in those standard paths are always available
for local builds, without the need of setting `PATH` in action
definitions.

Such an approach obviously has the disadvantage that the cache has
to be cleared, whenever the tools installed in those standard
paths are updated. For users following a stable release with only
security fixes, this can be acceptable.

While not the idiomatic way of building on Nix, such a
behavior can be simulated by using as launcher a tool
like [withExtendedPath](./src/withExtendedPath/extend-path.cc)
extending the value of the environment variable `PATH` by
a given string, e.g., your `~/.nix-profile` controlled by
the [home-manager](https://rycee.gitlab.io/home-manager/"). To
avoid interfering with the clean Nix-idiomatic builds described
in the following sections, it is recommended to also set the local
build root to a different directory; remember to `just gc` twice
whenever updating your `~/.nix-profile`. Your `~/.just-mrrc` thus
would look something like
```
{ "local launcher":
  [ "/home/YOURUSERNAME/.nix-profile/bin/withExtendedPath"
  , "/home/YOURUSERNAME/.nix-profile/bin"
  ]
, "local build root": {"root": "home", "path": ".cache/just-nix-home"}
}
```
Such an approach can be useful when dealing with a large number of
project repositories all requiring basically the same tool chain.
This discussion also shows how to easily provide a well-defined
remote-execution environment on Nix: local launcher and local build
root can also be set on the command line when starting `just execute`.

## Getting Nix Paths into Justbuild Actions

On Nix, the usual "standard" paths are pretty empty. Instead, all
packages are installed into paths in the nix store containing a recursive
hash of the full build description. So a path to the nix store brings
a well-defined dependency and we're only left with the problem of
getting the right paths into the actions.

Justbuild deliberately ignores any environment variables of the invocation;
the environment of an actions has to be provided through the build description.
When using [`rules-cc`](https://github.com/just-buildsystem/rules-cc),
the `"defaults"` targets can be used to set tools and paths for
targets defined by a particular rule. We link those targets to
the [`nixpkgs`](https://github.com/NixOS/nixpkgs) in a maintable
way as follows.
 - All the `"defaults"` targets simply take their values from appropriate
   parts of the `"TOOLCHAIN_CONFIG"` just configuration variable.
 - The just configuration file is generated by a
   nix [derivation](./nix-dependencies/dependencies.nix) using that
   nix derivations have easy access to the needed paths in the nix
   store. That derivation also generates an rc-file pointing to
   that configuration.
 - The precise [sources](./nix/sources.json) of the `nixpkgs` are
   pinned using [niv](https://github.com/nmattia/niv).
 - A [nix shell](./shell.nix) uses the derivation at the pinned
   snapshot of the `nixpkgs` and sets an alias for `just-mr` to
   use the derived rc-file. It also adds a script `withRc-just-mr`
   that execs `just-mr` with the derived rc-file.

So to build with the correct dependencies for the checked out version,
simply start a `nix-shell` at the top level of this repository and
use `just-mr build` as usual. It should be noted that the `nix-shell`
does not pull in justbuild itself and instead inherits it from the
invoking shell; in that way, when going back to an old snapshot
it is still built using the currect justbuild. The reason is that,
while newer versions of justbuild can work with a local build root
generated by older versions, this is not necessarily the case the
other way round (e.g., versions before `1.3.0` are not aware of
the large-object CAS introduced in that release, and hence will
not find certain artifacts referenced in the action cache). So,
by using the current justbuild when checking out older snapshots,
we can reconstruct the old actions without the need of cleaning up
the local build root.

The just described way of working in a `nix-shell` using the
alias is useful for interactive development. For CI-like usage,
the script [run-tests](./run-tests) can be used that simply runs
`withRc-just-mr` in a `nix-shell`. Arguments are forwarded to the
build command so that you can run, e.g., `./run-tests hello hello`
or `./run-tests -D '{"RUNS_PER_TEST": 3}'`.

## Shell commands

The built-in rule `"generic"` allows to define a target by
executing a shell command (via `sh -c ...`). This is convenient
on systems where the shell is configured to have "the standard
tools" available by default. On Nix, one would have to set
the `"env"` appropriately on every invocation. As would be
quite cumbersome, preference is given to the
rule [`["shell", "cmds"]`](https://github.com/just-buildsystem/rules-cc?tab=readme-ov-file#rule-shell-cmds)
that is a replacement for `"generic"` honoring the shell toolchain (as
defined in the appropriate `"defaults"` target).


## Repository Overview

### Version pinning and updating

There are two files pinning dependencies
 - The `nixpkgs` are pinned in [nix/sources.json](./nix/sources.json). They can
   be updated using [update-nix-dependencies.sh](./update-nix-dependencies.sh)
   which simply calls `niv update` in our `nix-shell`.
 - The dependencies on other other justbuild projects are
   pinned in [etc/repos.json](etc/repos.json). They can be updated using
   [update-just-dependencies.sh](./update-just-dependencies.sh) which
   calls [etc/generate-repos.sh](./etc/generate-repos.sh) in our `nix-shell`,
   which also brings in tools required for this step.

Specific considerations
 - [cargo](./src/rust/cargo/README.md)

### Logical repositories

This project uses several logical repositories.

 - The directories `src` and `test` provide standard main and test
   repositories. The logical structure is, as usual, that the test
   repository has access to the main repository, but not the other
   way round. In this way, the main repository can be imported
   without pulling in the test dependencies. Note that in those
   directories (where the main project-development work will happen)
   there is nothing Nix-specific, except maybe for the choice to
   refrain from using the built-in rule `"generic"`.

 - The rules `rules/nix` and `rules/nix-test` are derived from
   the [rules-cc](https://github.com/just-buildsystem/rules-cc) by
   setting the target-file layer to the directory `etc/defaults`.
   Here the toolchains are defined in a fine-granular way. The toolchains
   of `rules/nix-test` inherit from the ones in `rules/nix`. In this
   way, we can bring in additional tools only for tests. Those extra
   paths are set in `TOOLCHAIN_CONFIG["test"]["PATH"]`.

This fine-granular setting of the toolchains has the effect that actions only
have the paths necessary set and in that way are not unnecessarily affected
by changes of the depedencies. A good example for this granularity can be
seen by building verbosely
```
[nix-shell]$  just-mr --main test build hello '' --log-limit 5
```
and looking at the differnt values of the environment: `PKG_CONFIG_PATH`
is only set in the actions calling `pkg-config`, only the test
action has the additional test tools in `PATH`.

### Nix dependencies

As explained, the nix-dependencies are declared in
a [derivation](./nix-dependencies/dependencies.nix). For
libraries, which probably form the majority of dependencies
added, they way they become available is via `PKG_CONFIG_PATH`
which makes them discoverable
as [`["CC/pkgconfig", "system_library"]`](https://github.com/just-buildsystem/rules-cc?tab=readme-ov-file#rule-ccpkgconfig-system_library).
As in the `buildPhase` of a Nix derivation, the environment variable
`PKC_CONFIG_PATH` is already set appropriately, it is sufficient
to simply add new libraries to `buildInputs`. As an example, note
that `fmt` is available without being explicitly mentioned in
`buildPhase`.