There are three main parts to this change:
- Support a new `setupHookFunc` in `mkDerivation`
- Using attribute sets for `cmakeFlags` in Nix
- Passing `cmakeFlags` to the builder more smartly
== Introducing `setupHookFunc`
The `setupHook` attribute allows a derivation to register arbitrary
modifications to run in the context of downstream dependents' builds.
To enable similar modifications to be made to the attributes passed
to `mkDerivation`, teach `mkDerivation` a new trick: `setupHookFunc`.
A derivation can set `setupHookFunc` to a Nix function
which will be called for downstream dependencies
(TODO using the same logic as the setupHook inclusion),
and which takes the attribute set which was passed to `mkDerivation`
and returns a new attribute set with any desired changes.
These will stack (TODO: resolution order?) for composability,
and having these live alongside the original derivation
avoids cluttering `mkDerivation` with cmake-specific code
(which was done in an earlier iteration).
TODO: benchmark
== Using Attribute Sets for `cmakeFlags`
Almost all flags passed to `cmakeFlags` are setting CMake cache options
with "-D<name>=<value>" or "-D<name>:<type>=<value>".
Because the CMake cache is essentially a set of keys and values,
model `cmakeFlags` as an attribute set in Nix as well.
The cmake `setupHookFunc` will take a `cmakeFlags` set
and convert each key/value pair to an appropriate "-D" option for cmake.
String values are used as is, while integers are converted to strings
and booleans are converted to `"ON"` and `"OFF"`.
This presents the following benefits:
- Removal of "-D" visual noise everywhere for ease of reading.
- The `=` operater is literal, allowing extra whitespace for more ease
of reading.
- CMake options are now first class Nix values that can be operated on
directly with the full strength of the Nix language.
For example, boolean values can be used directly because the
serialization logic is consolidated into the cmake `setupHookFunc`,
getting rid of helpers like `edf`
and making it easier to re-use feature flags like `pythonSupport`.
- Sets automatically sort options, improving cache re-use.
- An override which updates a CMake flag option will only incur
(amortized) O(1) cost with a set, as opposed to O(n) cost with an
(unsorted) list, which would require a full linear search.
It's also shorter to write: just use `//`.
A few other CMake flag types are supported:
- Setting a value to null will cause its key to be unset via "-U<name>".
- The `generator` key is mapped to the "-G<value>" option.
- The `extraArgs` key is an escape hatch; any flags in this list of
strings are not pre-processed, but concatenated to the generated list.
Because `setupHookFunc`s are processed during `mkDerivation`,
when overriding `cmakeFlags` `overrideAttrs` should be used instead of
`overrideDerivation` so the cmake `setupHookFunc` has a chance to rerun.
== Passing `cmakeFlags` to the builder more smartly
Nix's regular conversion of lists to an environment variable
simply concatenates the contents with spaces,
which breaks if any of the flags themselves contain spaces.
`cmakeFlagsArray` was added as a way to get around this limitation,
but is unsatisfactory to use because items must be appended via bash
in a preConfigure hook.
Instead, pass the list to the builder in a smarter way:
instead of relying on Nix's conversion to a string,
perform our own conversion by escaping double quotes,
double quoting each item, and passing the flags as a Bash array,
which is hydrated via `eval`.
This handles flags with spaces, newlines and other whitespace,
as well as double quotes, faithfully.
TODO: does it handle eg some_var="$out/blah"
Make the list available during preConfigure as a bash array, so any
dynamic modifications to the CMake flags can be done there.
These changes make also `cmakeFlagsArray` redundant, so it has been
removed and replaced in all cases with `cmakeFlags`.