Skip to content

Latest commit

 

History

History
422 lines (321 loc) · 21 KB

README.org

File metadata and controls

422 lines (321 loc) · 21 KB

Pkgs-make Library for Nix

About Pkgs-make

Pkgs-make is a library that factors away boilerplate we often end up when writing Nix expressions based solely on Nixpkgs.

If you have a C, Haskell, or Python project, this library should be usable as is. If you’re using another language, it can likely be extended.

One of the accompanying tutorials provides an overview of using Pkgs-make. Additionally, as a Haskell tutorial and Python tutorial both showcase language-specific usage.

This document specifies Pkgs-make in more rigor and completeness.

Importing

Pkgs-make is just a Nix library, similar to Nixpkgs, so once we fetch the repository we just need to import it from a Nix expression. Please see the tutorials if you don’t know how to do that.

There’s a few paths we can import in this repository:

PathVariant
example-nixplain
example-nix/pkg-makeplain
example-nix/variant/curatedcurated
example-nix/variant/plainplain

Sometimes changes haven’t gotten into Nixpkgs. As an experiment, the Pkgs-make contributors curate a set of overrides for Nixpkgs. In particular, many of these overrides help keep some machine learning libraries more up-to-date.

As a result Pkgs-make has two variants:

  • plain (no overrides)
  • curated (with overrides)

By default, you get the plain variant. But you can import variant/curated if you’d like to try out these overrides:

…
pkgsMake = import "${pkgsMakePath}/variant/plain";
…

Please note, we don’t have a lot of people managing this curation. Also, it would be even better if the work from curation within Pkgs-make could work back into Nixpkgs. Any help is much appreciated.

Specification of Functions and Types

Notation and PkgsMake

Since Nix doesn’t have an official type notation, for this discussion we’ll assume the following:

Type NotationDescription
Nixpkgsan imported nixpkgs (e.g. from import <nixpkgs> {})
Drva Nix derivation
Boola boolean
Stringa string
Patha path
Path<v>a path that when imported yields a value of type v
[e]a list with elements of type e
Set<v>an attribute set with values of type v
Functiona function with arbitrary input and output types
i -> oa function inputting type i, returning type o
a + ba value either of type a or of type b

Pkgs-make is a higher-order function with the following types:

PkgsMake = PkgsMakeArgs -> Builder -> Build
Builder = BuildArgs -> Set<Drv>

Here PkgsMakeArgs, BuildArgs, and Build are all attribute sets (some nested).

In the following sections, we’ll specify each of these types in greater detail.

PkgsMakeArgs

PkgsMakeArgs are the top-level configuration of a Pkgs-make call. No configuration is mandatory, and an empty set can be passed to Pkgs-make as the PkgsMakeArgs for defaults that should address common cases. You can see those defaults in the =config.nix= file

Here’s some details its attributes:

PkgsMakeArgs AttributeTypeDescription
nixpkgsRevStringcommit ID of base Nixpkgs
nixpkgsSha256Stringhash parity check for nixpkgsRev
nixpkgsArgsNixpkgsArgsstandard arguments for Nixpkgs
bootPkgsPathPathpath of explicitly set boot Nixpkgs
bootPkgsNixpkgsNixpkgs to use to fetch base Nixpkgs
basePkgsPathPathpath of explicitly set base Nixpkgs
overlayOverlayoverlay for Nixpkgs replacing defaults
extraOverlayOverlayoverlay for Nixpkgs extending defaults
srcFilterSrcFiltersource filter replacing defaults
extraSrcFilterSrcFiltersource filter extending defaults
srcTransformSrcTransformsource-to-source transformation
haskellArgsHaskellArgsHaskell-specific arguments
pythonArgsPythonArgsPython-specific arguments

Getting a base Nixpkgs

Many of PkgsMakeArgs’s attributes involve the retrieval and configuration of a base Nixpkgs. By default the dynamic <nixpkgs> path is used as the boot Nixpkgs only to fetch the real Nixpkgs used as a base (pinned to nixpkgsRev). If you prefer not to boot with the dynamic <nixpkgs>, you can supply a bootPkgsPath to import instead, or a pre-imported/configured bootPkgs. Or if you already have your base Nixpkgs pulled, you can provide its path as basePkgsPath for importing. We anticipate most people will just set nixpkgsRev and nixpkgsSha256 and let Pkgs-make do the rest, trusting that the single fetch call we do with <nixpkgs> is benign.

Overlaying Nixpkgs

The overlay and extraOverlay attributes have a type of Overlay, which is the standard “overlay” in Nixpkgs:

Overlay = NixpkgsSelf -> NixpkgsSuper -> Set<Drv>=
NixpkgsSelf = Nixpkgs
NixpkgsSuper = Nixpkgs

These types for an Overlay have the following values:

Overlay ParameterTypeValue
1NixpkgsSelffinal Nixpkgs after all overlays applied
2NixpkgsSuperNixpkgs before overlay are applied
returnSet<Drv>overlaid packages.

Default source filtering:

Source files used to build derivations are by default filtered by Pkgs-make. This way, things like temporary/intermediate files left behind by editors and other tools don’t invalidate Nix’s caching and trigger a new build.

You can modify this filtering with three attributes: srcFilter, ~extraSrcFilter, and srcTransform. These attributes have values of types SrcFilter and SrcTransform.

SrcFilter is a similar to the standard Nix source filter predicate but with a extra leading PkgsMakeLib parameter:

SrcFilter = PkgsMakeLib -> PathStr -> TypeStr -> Bool
PathStr = String
TypeStr = String

These types for a SrcFilter have the following values:

SrcFilter ParameterTypeValue
1PkgsMakeLiba library of useful Nix functions
2PathStrpath to the file to be filtered
3TypeStrresult of calling builtins.typeOf on the path
resultBoolfalse to discard file/directory, true to keep

SrcTransform is a function to modify standard Nix sources:

SrcTransform = Src -> Src

Here Src is a standard source type for Nix’s derivation building. Often it’s just a Path, but after filtering, it might be a more complicated attribute set that Nix can interpret as a filtered set of files.

Nixpkgs offers a some functions for filtering under ~lib.sources~. The lib.sources.cleanSource is an instance of the SrcTransform type, and lib.sources.cleanSourceFilter is an instance of the SrcFilter type. Pkgs-make provides more of these functions on the lib attribute of the BuildArgs set discussed later.

Language-specific configuration

The language-specific HaskellArgs and PythonArgs are described more later, but their common attributes include the following:

Language-specific AttributeTypeDescription
overridesOverrideoverrides replacing defaults
extraOverridesOverrideoverrides extending defaults
srcFilterSrcFiltersource filter replacing defaults
extraSrcFilterSrcFiltersource filter extending defaults
srcTransformSrcTransformsource-to-source transformation
envMoreToolsNixpkgs -> [Drv]extra env tools replacing defaults

Override is has the following type:

Override = Nixpkgs -> PkgsSelf -> PkgsSuper -> Set<Drv + Function>
PkgsSelf = Set<Drv + Function>
PkgsSuper = Set<Drv + Function>

These types for an Override have the following values:

Override ParameterTypeValue
1Nixpkgspinned Nixpkgs with all overlays applied
2PkgsSelflanguage-specific builds before overriding
3PkgsSuperlanguage-specific builds after overriding
returnSet<Drv + Function>overrides by name

Note that PkgsSelf and PkgsSuper are platform-specific packages (like Nixpkgs’s haskellPackages for Haskell or pythonPackages for Python), not top-level Nixpkgs packages.

Builder function

Pkgs-make will pass to a Builder function an input of type BuildArgs, which has the following attributes:

Builder AttributeTypeDescription
libPkgsMakeLibvarious utilities in nested sets
callPkgsMakeCallvarious call-package functions (Path -> Drv)

PkgsMakeLib

The lib attribute of BuildArgs provides utilities that are largely the same utilities provided by Nixpkgs, but with some extension:

BuildArgs AttributeTypeDescription
nixSet<Function>Nixpkgs’ lib with some extras
haskellSet<Function>Nixpkgs’ haskell.lib with some extras

PkgsMakeCall

The call attribute provides call-package styled functions. These types of functions are commonly used in Nixpkgs, and are often called “callPackage” in code. The call-package style uses reflection to call functions with less boilerplate and is described in more detail in our Pkgs-make tutorial, and also in one of the Nix Pills.

In summary, call-package functions call functions that take a destructured attribute set as an argument, and reflects over the attributes expected. The call-package function then chooses a value to pass in based upon the attribute name.

Four call-package functions are provided on the call attribute:

AttributeTypeDescription
packagePkgsMakeCallPkgstandard call-package
haskell.appPkgsMakeCallPkgcall-package for Haskell executables
haskell.libPkgsMakeCallPkgcall-package for Haskell libraries
haskell.cabal2nix.appPkgsMakeCallPkgsame as haskell.app, but forces cabal2nix
haskell.cabal2nix.libPkgsMakeCallPkgsame as haskell.lib, but forces cabal2nix
pythonPkgsMakeCallPkgcall-package for Python packages

All of the call-package functions have a similar type:

PkgsMakeCallPkg = Path<Set -> Drv> + (Set -> Drv)

The call-package function takes a set as an input and returns a derivation. Or alternatively, the call-package function can take a path that returns this function when imported.

These call-package functions accept sets similar to the following call-package functions in Nixpkgs (which are delegated to ultimately). Additionally, these sets can include derivations being returned by the Builder function passed to Pkgs-make. This allows us to conveniently weave together code we’re developing with other packages coming from Nixpkgs.

call AttributeExpects Set -> Drv Similar To
packageNixpkgs’s callPackage
haskell.appNixpkgs’s haskellPackages.callPackage
haskell.libNixpkgs’s haskellPackages.callPackage
haskell.cabal2nix.appNixpkgs’s haskellPackages.callPackage
haskell.cabal2nix.libNixpkgs’s haskellPackages.callPackage
pythonNixpkgs’s pythonPackages.callPackage

For the Haskell API, we have two call-package functions, call.haskell.lib and call.haskell.app, which are the same with the exception of the latter calling lib.haskell.justStaticExecutables on the derivation.

Also, the Haskell API is slightly different in that if you don’t have a default.nix in the path you supply, one will be automatically generated from the Cabal file found in the path using the tool =cabal2nix=. If you have a default.nix in your path, but don’t want the call-package call too use it, you can use haskell.cabal2nix.app or haskell.cabal2nix.lib instead to force the call to cabal2nix.

Returned derivations

Using the library functions on the lib attribute and the call-package functions on the call attribute we can return a Set<Drv>.

As mentioned, all of the call-package functions will pull derivations from this set by name in addition to the normal packages they’d pull from in Nixpkgs.

With the normal “callPackage” functions in Nixpkgs, we’d have to set up some boilerplate of overlays and overrides. Pkgs-make does this for us. Additionally, Pkgs-make has a few defaults like source filtering that remove boilerplate we’d end up with on every project (we almost always want to filter away the “result” symlinks a Nix build can leave behind).

Returned Build

What Pkgs-make returns you is largely the same Set<Drv> you define, but augmented with a few extra attributes:

Build AttributeTypeDescription
env.haskellDrvfor nix-shell Haskell development
env.pythonDrvfor nix-shell Python development
nixpkgsNixpkgsfinal fully overlaid Nixpkgs

From this returned set, you can select out the packages you want by attribute for packaging and distribution.

Nix-shell environments

The “env.*” attributes in the returned Build set have derivations for use with nix-shell. env.haskell unifies all the dependencies of Haskell packages in your build, excluding packages you’re developing locally. Similarly, env.python does the same for Python packages. This allows us to get multi-package project support from Nix.

Also, both env.haskell and env.python derivations have an added attribute withEnvTools of type Nixpkgs -> [Drv] for including additional development tools beyond what’s pulled in by dependencies.

Note that the Haskell and Python environments already include some common development tools by default. You can see =config.nix= to see what’s included.

Haskell-specific configuration

lib.haskell on the PkgsMakeLib instance has many functions from Nixpkgs’s haskell.lib attribute that yield a changed derivation. One example of this is lib.haskell.dontCheck, which disables running tests when building.

HaskellArgs includes one more attribute:

HaskellArgs AttributeTypeDescription
ghcVersionStringwhich compiler to use (of the form “ghc822”)
pkgChangesPkgsMakeLib -> Set<Drv -> Drv>changes for packages by name
changePkgsSet<[String]>names of packages to apply changes to by name

ghcVersion chooses the version of GHC we’re building with by the attribute name convention in Nixpkgs.

pkgChanges is a function that maps the names of packages to modify to functions built from those in PkgsMakeLib that are of the type Drv -> Drv.

changePkgs is a less expressive, but possibly more concise API that inverts what do with pkgsChanges. Here the attribute names of the changePkgs set are the names of functions from lib.haskell that must already be of the type Drv -> Drv. These function names are mapped to the names of packages to be modified.

For both pkgChanges and changePkgs, the packages modified can either come from the third-party Haskell libraries already in Nixpkgs, or they can be the packages we’re currently building with the Pkgs-make call.

Python-specific configuration

PythonArgs includes two more attribute:

AttributeTypeDescription
pyVersionStringwhich interpretter to use (of the form “36”)
envPersistsBooleanwhether to keep “editable” install files around

pyVersion chooses the version of Python by the attribute name convention in Nixpkgs.

The returned env.python derivation, designed for use with nix-shell has a Nixpkgs “shellHook” that senses local Python projects built by Pkgs-make and installs them locally in =pip=’s “editable” mode. This sets up the PATH and PYTHONPATH to not only have pinned version of third-party Python libraries, but also references to the local unpinned projects under development

This setup by default involves some files persisted/cached in a temporary directory to save time entering the Nix shell after the first time. This persistence can be disabled with envPersists.

Navigating the Code

If you’d like to follow the implementation, this project is organized as follows:

File/DirectoryDescription
haskell/Haskell-specific Nix expressions
haskell/overrides/curated Haskell package overrides
haskell/tools/Haskell-specific Nix development tools
lib/platform-agnostic functions
python/Python-specific Nix expressions
python/overrides/curated Python package overrides
top/overrides/curated top-level package overrides
config.nixconfiguration defaults
default.nixtop-level entry point for convenience
tools.nixstand-alone tools