135 lines
5.6 KiB
Text
135 lines
5.6 KiB
Text
= 3: C++
|
|
:page-pagination: prev
|
|
|
|
Following up from Rust, Zilch has support for using the build graph from a Ninja
|
|
file. However, unlike Rust and Go, there's no one "blessed" way to build C/C++
|
|
code, so this isn't as plug-and-play as the other two languages.
|
|
|
|
To start, the support in Zilch rests upon Nixpkgs' existing derivations. If you
|
|
don't have an existing Nixpkgs derivation for the program, you'll need one. As
|
|
an example, we'll use `libinput` here.
|
|
|
|
[,scheme]
|
|
----
|
|
(environment: "pkgs.libinput"
|
|
depfile-path: "libinput-deps.scm")
|
|
----
|
|
|
|
In this sample, `pkgs.libinput` is a Nix expression run inside `nixpkgs`, similar
|
|
to how `nix-shell -p` behaves. When you run `zilch-cli-ninja -f libinput.scm build`,
|
|
Zilch splits up the derivation into three steps, split up by the `stdenv` build phases:
|
|
|
|
[,subs="verbatim,macros"]
|
|
----
|
|
unpackPhase
|
|
patchPhase
|
|
configurePhase
|
|
----8<---- cut derivation up here...
|
|
pass:[<del>buildPhase</del>] replace this with Zilch magic...
|
|
---->8---- ...and cut here!
|
|
checkPhase
|
|
installPhase
|
|
fixupPhase
|
|
installCheckPhase
|
|
distPhase
|
|
----
|
|
|
|
The original derivation is run up to the point where it would actually build
|
|
code, then the information is extracted from it, and Zilch does its magic: it
|
|
finds the Ninja file, and evaluates it just as Ninja itself would do; just with
|
|
each output being generated in its own little sandboxed derivation. Header files,
|
|
as well as source files (e.g. `.c`, and `.cpp`) are only visible to a build step
|
|
when it needs it, as each of these files causes spurious rebuilds if they aren't
|
|
actually necessary.
|
|
|
|
Once the build is complete, Zilch runs the last few phases, and stitches the
|
|
resulting derivation back together; making for a store path that is identical
|
|
to what `nixpkgs` normally generates!
|
|
|
|
The `depfile-path`, as passed to Zilch, is the one exception to the "stateless"
|
|
behavior: When compiling a source file, a side effect is figuring out all the
|
|
header files that get included. To save compute time (and because having them
|
|
around shouldn't materially affect builds), Zilch will build files with all
|
|
headers the first time, and use that to figure out which headers to include
|
|
next time. This information is stored in the `depfile`, which has a big map
|
|
of files to their dependencies. When a file fails to build (because it includes
|
|
a new header that wasn't previously visible), it will get rebuilt with all
|
|
headers again, to regenerate this database. This way, the `depfile` always
|
|
contains the minimal amount of headers necessary to build each file.
|
|
|
|
---
|
|
|
|
In some cases, projects may not compile inside the Nix sandbox as-is, due to
|
|
various reasons, e.g. symlinks, or missing dependencies. In this case, it's
|
|
possible to add targeted patches. As an example, the expression I use to build
|
|
Lix inside the sandbox is as follows:
|
|
|
|
[,scheme]
|
|
----
|
|
(environment: "(pkgs.lixPackageSets.lix_2_93.override { enableDocumentation = false; }).overrideAttrs (a: { doCheck = false; doInstallCheck = false; separateDebugInfo = false; __structuredAttrs = false; })"
|
|
depfile-path: "lix-deps.scm"
|
|
|
|
; The `config.h` file needs to be available to _all_ builds this file is
|
|
; inserted via -include on the command line, which doesn't make it into the
|
|
; dependency files, so dependency tracking thinks it's unused.
|
|
disallow-elide: (lambda (path) (string=? path "config.h"))
|
|
|
|
patch:
|
|
(begin
|
|
(import (zilch lang ninja) (srfi 152))
|
|
(lambda (target)
|
|
(if
|
|
; If our first implicit dependency is "python"...
|
|
(and
|
|
(not (null? (build-edge-implicit-dependencies-target target)))
|
|
(string-contains (car (build-edge-implicit-dependencies target)) "python"))
|
|
|
|
; Materialize (copy the actual files, rather than symlink them) the code-generation logic,
|
|
; python resolves imports relative to the target of the symlink, which is somewhere in the
|
|
; Nix store, as opposed to the source build tree we build up.
|
|
(string-append
|
|
"rm -rf src/lix/code-generation; "
|
|
"cp -rf -L --no-preserve=ownership \"$_ZILCH_ROOT\"/src/lix/code-generation src/lix/code-generation; "
|
|
"chmod ugo+rw -R src/lix/code-generation")
|
|
#f))))
|
|
----
|
|
|
|
This is a bit more of a complicated configuration, but shows the
|
|
configurability that's (sadly necessarily) possible for Zilch.
|
|
|
|
---
|
|
|
|
Editing the source of a project in a way that is compatible with Zilch is a bit
|
|
more complex than Go and Rust. To simplify this, `zilch-cli-ninja` has two
|
|
subcommands to handle this complexity: `zilch-cli-ninja source [DIR]` and
|
|
`zilch-cli-ninja diff`. The former will extract the source code of the project
|
|
just before build, and the latter will show the current diff between the
|
|
original derivation's source and the exposed one.
|
|
|
|
Once you've made changes to the project, you can incrementally rebuild it using
|
|
`--source` (`-s`), or by adding `override-source: "new/path"` to the
|
|
configuration file. The latter approach is mostly convenient for the next feature
|
|
we'll cover: nested patches!
|
|
|
|
---
|
|
|
|
The `--rewrite` feature on `zilch-cli-go` and `zilch-cli-rust` has an equivalent
|
|
in its Ninja support. However, as with the previous explanations, it's not _quite_
|
|
as simple. It needs to be configured inside the configuration file:
|
|
|
|
[,scheme]
|
|
----
|
|
(environment: "pkgs.labwc"
|
|
depfile-path: "labwc-deps.scm"
|
|
rewrite:
|
|
("libinput-1.29.1"
|
|
environment: "pkgs.libinput"
|
|
override-source: "./libinput-src"
|
|
depfile-path: "libinput-deps.scm"))
|
|
----
|
|
|
|
As you can see, the original libinput configuration is now nested _inside_ labwc's,
|
|
and using `-p libinput-1.29.1` lets you operate on it. But now, running just
|
|
`zilch-cli-ninja build` builds `labwc`, with the patched `libinput`! And it's
|
|
possible to arbitrarily nest these, of course; and any changes should
|
|
incrementally percolate down the full chain.
|