Example of Using Nix to Run Software Using Different Build Flags

Posted on October 31, 2021 by Richard Goulter
Tags:

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 =
    pkgs.opencascade-occt.overrideAttrs(oldAttrs: {
      # 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 =
    opencascade-occt.overrideAttrs(oldAttrs: {
      nativeBuildInputs = 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.


Newer post Older post