Example of Using Nix to Run Software Using Different Build Flags
Tags: programming.nix
I recently wanted to use FreeCAD to export a glTF file from a STEP file.
Unfortunately, I found that FreeCad needed to be built with OCC built with rapidjson.
The suggested solution isn’t hard, as such; but it is more tedious than just installing a package:
Building from OCC from scratch: 1. get the source code from (https://gitlab.com/blobfish/occt ) –> fillet fix included 2. setup your machine with cmake 3. get freetype2 and tcl/tk 4. setup cmake 5. build occ
Per the opencascade documentation
(or e.g. this forum post),
this means building opencascade, but with cmake flags: -D USE_RAPIDJSON:BOOL="ON"
.
It turns out Nixpkgs allows for doing this in a concise way:
e.g. with a file freecad.nix
:
# By convention: this file declares function
# which takes in a set of arguments;
# 'pkgs' has a default value of 'import <nixpkgs> {}'
{ pkgs ? import <nixpkgs> {} }:
let
# declare a package opencascade-occt, but with rapidjson support
opencascade-occt-with-rapidjson =
-occt.overrideAttrs(oldAttrs: {
pkgs.opencascade# opencascade-occt depends on rapidjson if built with USE_RAPIDJSON
nativeBuildInputs = oldAttrs.nativeBuildInputs ++ [ pkgs.rapidjson ];
# and change which extra cmakeFlags are passed to the configuration.
cmakeFlags = ''-D USE_RAPIDJSON:bool="ON"'';
});
in
# The result of the function is the freecad package,
# but using the opencascade package we declared above.
{
pkgs.freecad.override opencascade-occt = opencascade-occt-with-rapidjson;
}
The syntax may look esoteric, but it’s hardly more intimidating than the fancy parts of ECMAScript syntax.
The code declares a function for building a package for freecad.
The code is a function as a way of injecting dependencies.
The code uses nixpkgs
as a whole for our dependency. The code makes use of
the pkgs.freecad
and pkgs.opencascade-occt
, so it doesn’t need to
duplicate the effort needed to write those packages. The opencascade here
is built with -D USERAPIDJSON:BOOL="ON"
as a flag to CMake; and
rapidjson
is also given as a dependency. Then this is version of
opencascade is given to the pkgs.freecad
package.
The Overriding section
of the nix manual explains <pkg>.override
vs <pkg>.overrideAttrs
.
Roughly: <pkg>.overrideAttrs
changes things about the way <pkg>
is built; e.g. the
code above changes the CMake flags. Whereas, <pkg>.override
changes what
versions of dependencies are used to build a package; e.g. the code above
changes the opencascade-occt
dependency of freecad to one which was built
with rapidjson.
Stuff like this is what makes Nix both very powerful, but also very hard to get used to.
How to use some Nix file like freecad.nix
might also be unclear.
There are several different ways:
e.g. running nix-build freecad.nix
then running the ./result/bin/freecad
which results.. or using the footgun that is nix-env --install
and running
nix-env --install --file freecad.nix
, among others.
The code is more concise without the comments; or without the let
:
{ pkgs ? import <nixpkgs> {} }:
with pkgs;
{
freecad.override opencascade-occt =
-occt.overrideAttrs(oldAttrs: {
opencascadenativeBuildInputs = oldAttrs.nativeBuildInputs ++ [ rapidjson ];
cmakeFlags = ''-D USE_RAPIDJSON:bool="ON"'';
});
}
That said, I don’t know whether anyone would write code like that without having written similar code previously.
I arrived at the above code through refactoring. The naive approach is to
copy and paste the Nix files from the nixpkgs code, and make adjustments as
necessary. This works, but isn’t very elegant.
– One nice thing about refactoring Nix code, though, is that packages which
are built with the same set of inputs don’t require rebuilding. e.g. after
building either of the code snippets, the building the other wouldn’t require
any further code to be compiled.
I reckon that even that effort is still less tedious than the suggested
solution above.
– Or perhaps I’m thinking of this from a yak-shaving perspective: e.g. with
the good ol’ just clone and install dependencies and configure and build,
you’ll have to repeat this understandable but awkward process on every computer
you want to use FreeCAD with glTF export. With the Nix approach, at worst you’d
just copy the snippet and rebuild on your computer.
Since rebuilding GUI programs like this can take quite a beefy computer or quite a long time, another nice aspect of Nix is the ability to build with other computers. – I’ll have to try that at some point.