docs: More samples.
Change-Id: Ibcab3b04ae1650cd213a5a0915b7c6056a6a6964
This commit is contained in:
parent
8385c3ac9b
commit
3ee4d894f5
14 changed files with 552 additions and 0 deletions
135
docs/modules/ROOT/pages/samples/3-cpp.adoc
Normal file
135
docs/modules/ROOT/pages/samples/3-cpp.adoc
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
= 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue