Movatterモバイル変換


[0]ホーム

URL:


Hix Manual

Nix development tools for Haskell projects


Table of Contents

Introduction
About
Cabal builds
Nix flakes
Declaring Hix builds
Package definitions
Multiple systems and IFD
User Interface
Environments and commands
Environments
Commands
Services
Other tools
Haskell Language Server
GHC version checks
Hackage upload
CTags
Cross-compilation and static linking
Automatic dependency management
Miscellaneous tools

Introduction

Table of Contents

About
Cabal builds
Nix flakes

About

Hix is a toolkit for Haskell development that usesNix to provide a unified,declarative interface for a range of build related tasks:

  • Reproducible environments and dependency overrides

  • Cabal file generation

  • Hackage upload

  • Rapid-recompilation testing with GHCid

  • Haskell Language Server

  • CTags generation

  • Virtual Machines for testing

  • Compatibility checks for multiple GHC versions

The following two sections explain the basics of Cabal builds and Nix flakes.If you’re already familiar with those, you can skip ahead tothe section called “Package definitions”, but the later sections will build upon theexamples introduced here.

Cabal builds

Cabal is the basic build tool for Haskell projects.It reads package configuration from a.cabal file and performs compilation and execution of tests and applications.A Cabal package consists of one or morecomponents like libraries, test suites and executables.

The example project for this tutorial is calledparser and defines a library and an executable.The library has a single module namedParser.hs in the subdirectorylib/ with the content:

module Parser whereimport Data.Aeson (encode, object, toJSON, (.=))import Data.ByteString.Lazy.Char8 (unpack)import Data.String (fromString)import System.Environment (getArgs)import Text.Read (readMaybe)createJson :: Int -> StringcreateJson n = unpack (encode (object [fromString "number" .= toJSON n]))parseNumber :: IO StringparseNumber = parse <$> getArgs  where    parse (input : _) = maybe ("Not a number: " ++ input) createJson (readMaybe input)    parse _ = "No argument given."

The functionparseNumber converts the first command line argument into an integer and returns a JSON string thatlooks like{number:5}, or an error message if the argument is not a number.It uses the JSON libraryaeson and the GHC core librarybytestring, which have to be specified as dependencies in the Cabalfile like this:

library  exposed-modules:      Parser  hs-source-dirs:      lib  build-depends:      aeson ==2.0.*    , base ==4.*    , bytestring

We need to configure the source directorylib, the moduleParser as well as the dependencies, including thestandard librarybase.

The second file in this project is the executable’s entry point, located atapp/Main.hs:

module Main whereimport Parser (parseNumber)main :: IO ()main = putStrLn =<< parseNumber

Themain function callsparseNumber and prints the returned string to stdout.It has no dependencies except forbase and the library component, therefore its Cabal section is:

executable parser  main-is: Main.hs  hs-source-dirs:      app  build-depends:      base ==4.*    , parser

The only difference here is that we need to specify the module that contains themain function with the keymain-is.

When these two fragments are written toparser.cabal in the project root along with a bit of boilerplate, the CabalCLI tool be used to compile and run the application by invoking it ascabal build andcabal run.

In order for this to work, the developer has to ensure that GHC and Cabal are installed and use the right version andpackage set snapshot to be certain that the application is built the same way on different machines.Hix aims to reduce the overhead of this process to requiring only the presence of Nix, with the goal of replicatingthe development environment and all involved tools identically on any machine.While Cabal offers some mechanisms for this kind of reproducibility, Nix provides a unified and ergonomic interface toit on multiple levels of the build.

Nix flakes

The build definition for a Nix project is declared in the fileflake.nix with the followingprotocol:

{  inputs = {...};  outputs = inputs: {    packages = {...};    apps = {...};    ...  };}

The input set declares dependencies on other repositories, which are downloaded and passed to the output function assource directories when a Nix command is executed.Depending on the command, Nix chooses one of the entries in the output set and builds the requested package.

A basic example for an application that prints “Hello” could look like this:

{  inputs = { nixpkgs.url = "github:nixos/nixpkgs/b139e44d78c36c69bcbb825b20dbfa51e7738347"; };  outputs = {self, nixpkgs}: let    pkgs = import nixpkgs { system = "x86_64-linux"; };    drv = pkgs.writeScriptBin "greet" "echo Hello";  in {    apps.x86_64-linux.greet = {      type = "app";      program = "${drv}/bin/greet";    };  };}

The single input is the nixpkgs repository at a specific commit, which contains both the build definitions for tens ofthousands of available packages and many tools for creating new packages.

The single output is an app namedgreet declared only for the architecturex86_64-linux, which can be executedwith:

nix run .#greet

where the. denotes the directory of the flake and the part after the# is the name of the output, which Nix triesto locate in different categories depending on the command – in the case ofrun, it starts looking inapps usingthe current system’s architecture.

To gain access to nixpkgs’ functionality, we import thedefault.nix (implicitly) at the root of the repository,passing the identifier of the architecture we want to use as thesystem argument.The functionwriteScriptBin is a so-called “trivial builder”, a function that produces a very simple package, like asingle shell script.

Under the hood, Nix wraps the builder in a data structure calledderivation that serves as the universal protocolfor the specification of dependencies, build steps and build outputs.When Nix is instructed to process a derivation, it follows its build steps and writes the resulting files to adirectory in/nix/store.In this case, the textecho Hello is written to/nix/store/bkz0kkv0hxhb5spcxw84aizcj5rm4qq9-greet/bin/greet, wherethe hash is calculated from the text and other environmental parameters.When the valuedrv is interpolated into the string in the declaration of the app output, Nix builds the derivationand inserts the resulting store path (minus thebin/greet).

Derivations are ubiquitous – when we build a Haskell application with Nix, it is represented as a derivation, justlike all of its dependencies.For the Cabal project described in the previous section, we can create a derivation and an app with this flake:

{  inputs = { nixpkgs.url = "github:nixos/nixpkgs/b139e44d78c36c69bcbb825b20dbfa51e7738347"; };  outputs = {self, nixpkgs}: let    pkgs = import nixpkgs { system = "x86_64-linux"; };    parser = pkgs.haskell.packages.ghc925.callCabal2nix "parser" ./. {};  in {    packages.x86_64-linux.parser = parser;    apps.x86_64-linux.greet = {      type = "app";      program = "${parser}/bin/parser";    };  };}

Now the app can be executed with:

$ nix run .#parser -- 63{"number":63}

To create a derivation for the Haskell app, we select a package set for the GHC version 9.2.5 withpkgs.haskell.packages.ghc92 and call the builder exposed by that set that wraps the toolcabal2nix, which converts the Cabal file in the specified directory to aderivation.Cabal2nix reads the dependencies from the file and ensures that they are accessible during the build, in which Cabalis invoked like in the previous section, though using a lower-level interface.

If this project were cloned on a different machine, like in a CI pipeline, the nixpkgs snapshot, and hence the entirebuild environment, would be identical to that on your development machine – not just because the Git revision ishardcoded in the flake input, but because Nix records the revision in the lockfile,flake.lock.If the revision were omitted, the latest commit at the time the project was first evaluated would be used, and updatedonly when explicitly requested by executing eithernix flake update (to updateall inputs) ornix flake lock --update-input nixpkgs.

For a trivial Cabal project like this, the build definition may not look complicated enough to elicit the desire forhigher abstractions.However, when the requirements become more elaborate, the basic tools provided by nixpkgs may prove inadequate, andthe following sections attempt to illustrate some of the circumstances that warrant a more complex toolkit.

Declaring Hix builds

Table of Contents

Package definitions
Generating flakes
Cabal configuration
Cabal options
Package configuration
Package options
General options
Multiple systems and IFD
On-the-fly derivations
User Interface
UI options

Package definitions

Hix provides two fundamental services to combine Cabal and Nix:

  • Generating Cabal build files from configuration declared in Nix expressions.

  • Creating reproducible derivations from those files for different configurable environments that obtain dependenciesfrom the Nix package set, which can be built and run via the flake interface.

The package from the tutorial section, consisting of an executable inapp/ and a library inlib/ with dependencieson the packagesaeson andbytestring, can be declared in a flake like this:

{  description = "Example";  inputs.hix.url = "github:tek/hix?ref=0.8.0";  outputs = {hix, ...}: hix {    packages.parser = {      src = ./.;      library = {        enable = true;        dependencies = ["aeson ^>= 2.0" "bytestring"];      };      executable.enable = true;    };  };}

In order to build the project, we first have to generate the Cabal file:

$ nix run .#gen-cabal

Note

Flake commands ignore files that are not under version control when operating in a git repository.If there are untracked files in your project, runningnix build .#gen-cabal might fail, so eithergit addeverything or run the command asnix run path:.#gen-cabal, which bypasses this mechanism.

This command will create the fileparser.cabal in the project directory, with the following content:

cabal-version: 1.12-- This file has been generated from package.yaml by hpack version 0.35.0.---- see: https://github.com/sol/hpackname:           parserversion:        0.1.0.0description:    See https://hackage.haskell.org/package/parser/docs/Parser.htmllicense:        GPL-3build-type:     Simplelibrary  exposed-modules:      Parser  other-modules:      Paths_parser  hs-source-dirs:      lib  build-depends:      aeson ==2.0.*    , base ==4.*    , bytestring  default-language: Haskell2010executable parser  main-is: Main.hs  other-modules:      Paths_parser  hs-source-dirs:      app  ghc-options: -threaded -rtsopts -with-rtsopts=-N  build-depends:      base ==4.*    , parser  default-language: Haskell2010

Using the package definition and the generated Cabal file, Hix creates flake outputs for building and running theapplication withnix build andnix run:

$ nix run .#parser -- 63{"number":63}

The generated flake outputs have roughly the following structure, analogous to the example in {#nix-flakes}:

{  outputs = let    parser = callCabal2nix "parser" ./. {};  in {    packages.x86_64-linux.parser = parser;    apps.x86_64-linux.parser = { type = "app"; program = "${parser}/bin/parser"; };  };}

Generating flakes

Rather than writing the boilerplate yourself, the Hix CLI application can generate it for you.

The CLI commandnew will create a project skeleton with an executable and test suite in the current directory:

nix run 'github:tek/hix?ref=0.8.0#new' -- --name 'project-name' --author 'Your Name'

If you have an existing project with Cabal files in it, thebootstrap command will create a flake that configuresthe more basic components:

nix run 'github:tek/hix?ref=0.8.0#bootstrap'

Cabal configuration

There are three levels of generality at which Cabal options can be specified:

  • Global options in the modulecabal apply to all packages and components

  • Package-level options in the modulepackages.<name>.cabal apply to all components in a package

  • Component-level options in all component modules

The structure looks like this:

{  cabal = {    dependencies = ["global-dep"];  };  packages = {    core = {      cabal = {        dependencies = ["core-dep"];      };      library = {        dependencies = ["core-lib-dep"];      };      executables.run = {        dependencies = ["core-exe-dep"];      };    };    api = {      cabal = {        dependencies = ["api-dep"];      };      library = {        dependencies = ["api-lib-dep"];      };      tests.unit = {        dependencies = ["api-test-dep"];      };    };  };}

Each component getsglobal-dep, while all components incore getcore-dep.Sincedependencies is a list option, the values are merged, so that theunit component inapi will have thedependencies["global-dep" "api-dep" "api-test-dep"].

Verbatim configuration for individual components that have no specific option may be specified in thecomponentmodule:

{  packages = {    core = {      library = {        component = {          other-modules = ["Prelude"];        };      };    };  };}

Cabal options

These are the Cabal options for packages and components that can be specified at any level.

enable

Whether to enable this <component type>.

Type:boolean

Default:true

Example:true

author

The author of the packages in this option tree.May benull to omit it from the config.

Type:null or string

Default:null

base

The dependency spec for thebase package.

Type:string or (submodule)

Default:"base >= 4 && < 5"

baseHide

The dependency spec for thebase package used whenprelude is set.

Type:string or (submodule)

Default:

{  mixin = [    "hiding (Prelude)"  ];  name = "base";  version = ">= 4 && < 5";}
benchSuffix

This string is appended to the package name to form the single benchmark component.SeetestSuffix for an example.

Type:string

Default:"-bench"

build-type

The build type for the packages in this option tree.May benull to omit it from the config.

Type:null or string

Default:"Simple"

component

Verbatim Cabal configuration inHPack format.Values defined here will be applied to components, not packages.

Cascades down into all components.

Note

This unconditionally overrides all option definitions with the same keys if they are not mergeable (like lists andattrsets).

Type:attribute set of unspecified value

Default:{ }

copyright

The copyright string for the packages in this option tree.The default is to combinecopyrightYear andauthor;May benull to omit it from the config.

Type:null or string

Default:null

copyrightYear

The year for the copyright string.

Type:string

Default:"2025"

default-extensions

GHC extensions for all components in this option tree.

Type:list of string

Default:[ ]

Example:["DataKinds" "FlexibleContexts" "OverloadedLists"]

dependOnLibrary

Convenience feature that automatically adds a dependency on the library component to all executable components, ifthe library exists.

Type:boolean

Default:true

dependencies

Cabal dependencies used for all components in this option tree.

Type:list of package dependency in HPack format

Default:[ ]

Example:["aeson" "containers"]

env

The environment used when running GHCi with a module from this component.

Type:null or name of an environment defined in config.envs

Default:null

exeSuffix

This string is appended to the package name to form the single executable component.SeetestSuffix for an example.The default is to use no suffix, resulting in the same name as the package and library.

Type:string

Default:""

ghc-options

GHC options for all components in this option tree.

Type:list of string

Default:[ ]

Example:["-Wunused-imports" "-j6"]

ghc-options-exe

GHC options for all executables in this option tree.The purpose of this is to allowghc-options to use it as the default for executables without requiringcomplicated overrides to disable it.If you don’t want to use these options, set this option to[] instead of forcing other values inghc-options.These options are not used for benchmarks.

Type:list of string

Default:

[  "-threaded"  "-rtsopts"  "-with-rtsopts=-N"]
internal.single

Whether this is the main component of its sort, declared astest rather thantests.foo.

Type:boolean(read only)

Default:false

internal.sort

Sort of the component (test, executable etc)

Type:one of “library”, “executable”, “test”, “benchmark”(read only)

Default:"<component type>"

language

The default extension set used for all components in this option tree.It is set toGHC2021 if the GHC versions of all defined envs are 9.2 or greater, andHaskell2010 otherwise.

Type:string

Default:"GHC2021"

license

The license for all packages in this option tree.May benull to omit it from the config.

Type:null or string

Default:"GPL-3"

license-file

The name of the file containing the license text for all packages in this option tree.May benull to omit it from the config.

Type:null or string

Default:null

meta

Verbatim top-level Cabal configuration inHPack format.Values defined here will not be applied to components, only packages.

Cascades down into all packages.

This should only be used for keys that have no corresponding module option, otherwise the values defined in apackage might be overridden by option definitions in the global config.

Type:attribute set of unspecified value

Default:{ }

name

The name of the <component type>, defaulting to the attribute name in the config or the package name.

Type:string

Default:"<name>"

paths

Cabal generates the modulePaths_packagename for each component, which provides access to datafiles included in a package, but is rarely used.This may cause trouble ifprelude is configured to use an alternative Prelude that does not export someof the names used in this module.Setting this option tofalse prevents this module from being generated.

Type:boolean

Default:true

prelude

Configure an alternative Prelude package.

Type:submodule

Default:{ }

prelude.enable

Whether to enable an alternative Prelude.

Type:boolean

Default:false

Example:true

prelude.package

The package containing the alternative Prelude.

Type:string or (submodule)

Default:"base"

Example:"relude"

prelude.module

The module name of the alternative Prelude.

Type:string

Default:"Prelude"

Example:"Relude"

source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"<name>"

testSuffix

This string is appended to the package name to form the single test component.For example, given the config:

{  packages.spaceship = {    test.cabal.testSuffix = "-integration";  }}

The name of the generatedtestsuite will bespaceship-integration.

Type:string

Default:"-test"

version

The version for all packages in this option tree.

Type:string

Default:"0.1.0.0"

Package configuration

Packages may contain multiple components: an optional library and any number of sublibraries, executables, test suitesor benchmarks.

Each of those can be specified as the single component of its type using the singular-name option, likeexecutable,or as a value in the plural-name submodule, or both:

{  packages.api = {    executable = { enable = true; };    executables = {      server = { enable = true; source-dirs = "api-server"; };      client = {};      debug = { enable = false; };    };  };}

This configuration generates three Cabal executables:

  • The defaultexecutable requires the optionenable to be set explicitly and uses the source directoryappunless specified otherwise.

  • The optionserver configures a custom source directory

  • The optionclient uses the default source directory, which is the same as the attribute name.All components configured in the plural-name submodule are enabled by default, so this one is also generated.

  • The optiondebug setsenable tofalse, so it is omitted from the configuration.

If no component was enabled, Hix defaults to enabling the default executable.

Multiple libraries are supported with the same syntax as other components.You can depend on them with the dependency string<pkg>:<lib>; when depending from another package, the library musthavepublic = true; set.

Package options

benchmark

The single benchmark for this package.To define multiple benchmarks, usebenchmarks.

Type:submodule of cabal-options and cabal-component

Default:{ }

benchmark.name

The name of the benchmark, defaulting to the attribute name in the config or the package name.

Type:string

Default:"<name>-bench"

benchmark.source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"benchmark"

benchmarks

Benchmarks for this package.Ifbenchmark is defined, it will be added.

Type:attribute set of (submodule of cabal-options and cabal-component)

Default:{ }

benchmarks.<name>.name

The name of the benchmark, defaulting to the attribute name in the config or the package name.

Type:string

Default:"‹name›"

benchmarks.<name>.source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"‹name›"

buildInputs

Additional non-Haskell dependencies required by this package.

Type:(function that evaluates to a(n) list of package) or list of package

Default:[ ]

cabal

Cabal options that are applied to all components.

Note: In order to enable cascading of these options, the definitions are not evaluated in-place, but whenevaluating components. Therefore, referring to these values with e.g.config.packages.name.cabal.version does not work as expected if the value uses an option property likemkIf ormkOverride.You can usecabal-config for this purpose, though.

Type:module

Default:{ }

cabal-config

Evaluated version ofcabal, for referencing in other config values.May not be set by the user.

Type:submodule of cabal-options(read only)

Default:{ }

dep.exact

Dependency string for referencing this package with its version from other Cabal package.Likedep.minor, but uses exact version equality, likecore ==0.4.1.0.

Type:package dependency in HPack format(read only)

dep.minor

Dependency string for referencing this package with its version from other Cabal package.Uses the minor version dependency bound, strictly greater than the precise version.

{config, ...}: {  packages = {    core = { version = "0.4.1.0"; };    api = {      dependencies = [config.packages.core.dep.minor];    };  }}

This results in the dependency stringcore >= 0.4.1.0 && < 0.5 in the Cabal file.

Also works when usingversionFile.

Type:package dependency in HPack format(read only)

description

The Cabal description of this packages.The default is a link to therootModule on Hackage, using the optionhackageRootLink.May benull to omit it from the config.

Type:null or string

Default:null

executable

The single executable for this package.To define multiple executables, useexecutables.

Type:submodule of cabal-options and cabal-component

Default:{ }

executable.name

The name of the executable, defaulting to the attribute name in the config or the package name.

Type:string

Default:"<name>"

executable.source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"app"

executables

Executables for this package.Ifexecutable is defined, it will be added.

Type:attribute set of (submodule of cabal-options and cabal-component)

Default:{ }

executables.<name>.name

The name of the executable, defaulting to the attribute name in the config or the package name.

Type:string

Default:"‹name›"

executables.<name>.source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"‹name›"

expose

The parts of this package that should be accessible as flake outputs, like being able to runnix build .#<env>.<package>.If the value is boolean, all parts are affected.If it is a set, submodule options configure the individual parts.

Type:boolean or (submodule)

Default:true

hackageLink

A convenience option containing the URL to the Hackage page using the package name.

Type:string

hackageRootLink

A convenience option containing the URL to the root module’s documentation on Hackage using the package name androotModule.

Type:string

libraries

The sublibraries of this package.Unlikelibrary, these are treated specially by cabal.To depend on them, use<pkg>:<lib>.Iflibraries.<name>.public is set tofalse, you can only depend on them from other componentsin the same package (this is then called an internal library – default istrue).

Type:attribute set of (submodule of cabal-options and cabal-component)

Default:{ }

libraries.<name>.enable

Whether to enable this library.

Type:boolean

Default:true

Example:true

libraries.<name>.author

The author of the packages in this option tree.May benull to omit it from the config.

Type:null or string

Default:null

libraries.<name>.base

The dependency spec for thebase package.

Type:string or (submodule)

Default:"base >= 4 && < 5"

libraries.<name>.baseHide

The dependency spec for thebase package used whenprelude is set.

Type:string or (submodule)

Default:

{  mixin = [    "hiding (Prelude)"  ];  name = "base";  version = ">= 4 && < 5";}
libraries.<name>.benchSuffix

This string is appended to the package name to form the single benchmark component.SeetestSuffix for an example.

Type:string

Default:"-bench"

libraries.<name>.build-type

The build type for the packages in this option tree.May benull to omit it from the config.

Type:null or string

Default:"Simple"

libraries.<name>.component

Verbatim Cabal configuration inHPack format.Values defined here will be applied to components, not packages.

Cascades down into all components.

Note

This unconditionally overrides all option definitions with the same keys if they are not mergeable (like lists andattrsets).

Type:attribute set of unspecified value

Default:{ }

libraries.<name>.copyright

The copyright string for the packages in this option tree.The default is to combinecopyrightYear andauthor;May benull to omit it from the config.

Type:null or string

Default:null

libraries.<name>.copyrightYear

The year for the copyright string.

Type:string

Default:"2025"

libraries.<name>.default-extensions

GHC extensions for all components in this option tree.

Type:list of string

Default:[ ]

Example:["DataKinds" "FlexibleContexts" "OverloadedLists"]

libraries.<name>.dep.exact

Dependency string for referencing this library with its version from other Cabal package.Likelibraries.<name>.dep.minor, but uses exact version equality, likecore ==0.4.1.0.

Type:package dependency in HPack format(read only)

libraries.<name>.dep.minor

Dependency string for referencing this library with its version from other Cabal package.Likedep.minor, but for sublibraries.

Type:package dependency in HPack format(read only)

libraries.<name>.dependOnLibrary

Convenience feature that automatically adds a dependency on the library component to all executable components, ifthe library exists.

Type:boolean

Default:true

libraries.<name>.dependencies

Cabal dependencies used for all components in this option tree.

Type:list of package dependency in HPack format

Default:[ ]

Example:["aeson" "containers"]

libraries.<name>.env

The environment used when running GHCi with a module from this component.

Type:null or name of an environment defined in config.envs

Default:null

libraries.<name>.exeSuffix

This string is appended to the package name to form the single executable component.SeetestSuffix for an example.The default is to use no suffix, resulting in the same name as the package and library.

Type:string

Default:""

libraries.<name>.ghc-options

GHC options for all components in this option tree.

Type:list of string

Default:[ ]

Example:["-Wunused-imports" "-j6"]

libraries.<name>.ghc-options-exe

GHC options for all executables in this option tree.The purpose of this is to allowghc-options to use it as the default for executables without requiringcomplicated overrides to disable it.If you don’t want to use these options, set this option to[] instead of forcing other values inghc-options.These options are not used for benchmarks.

Type:list of string

Default:

[  "-threaded"  "-rtsopts"  "-with-rtsopts=-N"]
libraries.<name>.internal.single

Whether this is the main component of its sort, declared astest rather thantests.foo.

Type:boolean(read only)

Default:false

libraries.<name>.internal.sort

Sort of the component (test, executable etc)

Type:one of “library”, “executable”, “test”, “benchmark”(read only)

Default:"library"

libraries.<name>.language

The default extension set used for all components in this option tree.It is set toGHC2021 if the GHC versions of all defined envs are 9.2 or greater, andHaskell2010 otherwise.

Type:string

Default:"GHC2021"

libraries.<name>.license

The license for all packages in this option tree.May benull to omit it from the config.

Type:null or string

Default:"GPL-3"

libraries.<name>.license-file

The name of the file containing the license text for all packages in this option tree.May benull to omit it from the config.

Type:null or string

Default:null

libraries.<name>.meta

Verbatim top-level Cabal configuration inHPack format.Values defined here will not be applied to components, only packages.

Cascades down into all packages.

This should only be used for keys that have no corresponding module option, otherwise the values defined in apackage might be overridden by option definitions in the global config.

Type:attribute set of unspecified value

Default:{ }

libraries.<name>.name

The name of the library, defaulting to the attribute name in the config or the package name.

Type:string

Default:"‹name›"

libraries.<name>.paths

Cabal generates the modulePaths_packagename for each component, which provides access to datafiles included in a package, but is rarely used.This may cause trouble ifprelude is configured to use an alternative Prelude that does not export someof the names used in this module.Setting this option tofalse prevents this module from being generated.

Type:boolean

Default:true

libraries.<name>.prelude

Configure an alternative Prelude package.

Type:submodule

Default:{ }

libraries.<name>.prelude.enable

Whether to enable an alternative Prelude.

Type:boolean

Default:false

Example:true

libraries.<name>.prelude.package

The package containing the alternative Prelude.

Type:string or (submodule)

Default:"base"

Example:"relude"

libraries.<name>.prelude.module

The module name of the alternative Prelude.

Type:string

Default:"Prelude"

Example:"Relude"

libraries.<name>.public

Whether to expose an internal library.

Type:boolean

Default:true

libraries.<name>.reexported-modules

Modules from dependencies that this library exposes for downstream projects to import.

Type:list of string

Default:[ ]

Example:["Control.Concurrent.STM" "Data.Text"]

libraries.<name>.source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"‹name›"

libraries.<name>.testSuffix

This string is appended to the package name to form the single test component.For example, given the config:

{  packages.spaceship = {    test.cabal.testSuffix = "-integration";  }}

The name of the generatedtestsuite will bespaceship-integration.

Type:string

Default:"-test"

libraries.<name>.version

The version for all packages in this option tree.

Type:string

Default:"0.1.0.0"

library

The library for this package.

Type:submodule of cabal-options and cabal-component

Default:{ }

library.name

The name of the library, defaulting to the attribute name in the config or the package name.

Type:string

Default:"<name>"

library.source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"lib"

name

The name of the package, determined by the attribute name in the config.

Type:string(read only)

Default:"<name>"

override

Manipulate the package’s derivation using the combinators described inthe section called “Override combinators”.

Type:function that evaluates to a(n) function that evaluates to a(n) unspecified value

Default:<function>

relativePath

A string representation ofsrc relative to the project root.Its value is inferred if possible, but ifsrc is not a plain path, it must be set explicitly.A common reason for this is when the path is constructed with a source filter, causing the creation a separatestore path for the subdirectory.In the basic case, Hix infers the root directory (forbase) by taking the prefix/nix/store/*/from one of the package paths, and stripping it from each package’ssrc.This works well for simple projects, but it helps to providebase, as well as this option,explicitly.If the package is at the project root, this value should be".".

Type:null or string

Default:null

rootModule

A convenience option that is used to generate a Hackage link.It should denote the module that represents the most high-level API of the package, if applicable.The default is to replace dashes in the name with dots.

Type:string

src

The root directory of the package.

Type:path

Example:./packages/api

subpath

The computed relative path of the package root directory.

Type:string(read only)

test

The single test suite for this package.To define multiple test suites, usetests.

Type:submodule of cabal-options and cabal-component

Default:{ }

test.name

The name of the test suite, defaulting to the attribute name in the config or the package name.

Type:string

Default:"<name>-test"

test.source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"test"

tests

Test suites for this package.Iftest is defined, it will be added.

Type:attribute set of (submodule of cabal-options and cabal-component)

Default:{ }

tests.<name>.name

The name of the test suite, defaulting to the attribute name in the config or the package name.

Type:string

Default:"‹name›"

tests.<name>.source-dirs

Directories with Haskell sources.

Type:string or list of string

Default:"‹name›"

versionFile

The version file for this package, defaulting to the globalhackage.versionFile ifnull.When generating Cabal files, the version field will be set to the content of this file, unlessversion is set explicitly.When bumping the version of a package withnix run .#release, this file is updated.Should be relative to the project root.

Type:null or string

Default:null

General options

packages

The project’s Cabal packages, with Cabal package names as keys and package config as values.The config is processed withHPack.

Type:attribute set of (submodule)

Default:{ }

Example:

{  core.src = ./.;  api = { src = ./api; cabal.dependencies = ["aeson"]; library.enable = true; };}
base

The project’s base directory.

Will be inferred from package source directories if unset.If the Hix project is a subdirectory of a git repository andflake.nix isn’t tracked by git, this option has tobe set to./. explicitly.

Type:null or path

Default:null

Example:./.

buildInputs

Additional non-Haskell dependencies provided to all packages and environments.

Type:(function that evaluates to a(n) list of package) or list of package

Default:[ ]

buildOutputsPrefix

Some of Hix’s features are exposed as top level outputs, likenix run .#release.Since these are quite numerous, it becomes increasingly likely that these clash with some of the project’s packageor command names, making them inaccessible.

For this reason, built-in Hix outputs are added to the outputs with lower precedence, to ensure that user-definedoutputs aren’t shadowed.To keep the built-in outputs accessible as well, they are additionally exposed in a dedicated prefix set namedbuild, as innix run .#build.release.

If you prefer a different prefix, set this option to the desired string.

Type:string

Default:"build"

cabal

Cabal options that are applied to all packages and components.

If you define any options here, they will be merged with definitions that are set in packages or components.This means that the default priority handling applies – definitions in components don’t automatically overridethose in packages or the global config.You will need to usemkDefault ormkForce, or evenmkOverride if you define an option at all three levels.

Note

In order to enable cascading for these options, the definitions are not evaluated in-place, but when evaluatingpackages and components. Therefore, referring to these values with e.g.config.cabal.version does not work asexpected if the value uses an option property likemkIf ormkOverride.You can usecabal-config for this purpose, though.

Type:module

Default:{ }

cabal-config

Evaluated version ofcabal, for referencing in other config values.May not be set by the user.

Type:submodule(read only)

Default:{ }

commands

Commands are shell scripts associated with an environment that are exposed as flake apps.All commands are accessible as attributes of.#cmd.<name>, and those that setexpose = true are additionallyexposed at the top level.

Type:attribute set of (submodule)

Default:{ }

compat.enable

Create derivations inoutputs.checks that build the packages with different GHC versions.The set of versions is configured bycompat.versions.

Type:boolean

Default:true

compat.ifd

Whether to allow IFD for compat checks.

Type:boolean

Default:false

compat.versions

The GHC versions for which to create compat checks. Defaults toghcVersions.There has to be an env inenvs with the version as its name for each of these.

Type:list of string

Default:

[  "ghc94"  "ghc96"  "ghc98"  "ghc910"]
compiler

The GHC version used for internal tasks and for the default environment.This is an attribute name in the nixpkgs sethaskell.packages, which is usually in the formatghc96.

Type:string

Default:"ghc98"

deps

Flake inputs containing hix projects whose overrides are merged into this project’s.Thelocal overrides are ignored to prevent the dependencies’ project packages from beinginjected into the compat checks.

Type:list of path

Default:[ ]

depsFull

Flake inputs containing hix projects whose overrides are merged into this project’s.Unlikedeps, this includes thelocal overrides.

Type:list of path

Default:[ ]

devGhc

Backwards-compat alias forenvs.dev.ghc.

Type:submodule(read only)

envs

All environments for this project.

Type:attribute set of (submodule)

Default:{ }

exportedOverrides

These overrides are exposed from the flake for integration in downstream projects via the optionsdeps anddepsFull.

This is an attrset whose keys indicate where to put the overrides in the dependent project – each version env andthedev env has their own, while theall key is applied globally.The special keyslocal andlocalMin contain the local packages and their minimal build variants, respectively.Local packages are only propagated whendepsFull is used.

Type:lazy attribute set of Haskell package override function specified in the Hix DSL

Default:{ }

forceCabal2nix

Whether to use cabal2nix even if there is no Cabal file.

Type:boolean

Default:false

forceCabalGen

Whether to generate a Cabal file from Nix config even if there is one in the source directory.

Type:boolean

Default:false

gen-overrides.enable

The flake app.#gen-overrides collects all cabal2nix-based derivations from theoverrides that wouldrequire IFD when computed on the fly.

Setting this flag instructs Hix to read the generated derivations when building, and to abort the build whenthey are missing or outdated.

Type:boolean

Default:false

gen-overrides.file

The relative path of the file in which the overrides are stored.

Type:string

Default:"ops/overrides.nix"

gen-overrides.gitAdd

Git-addthe overrides file after the first run.Since nix ignores untracked files in flakes, the next command would complain if the file wasn’t added.

Type:boolean

Default:true

ghcVersions

The GHC versions for which to create envs, specified by their attribute names inpkgs.haskell.packages.

Type:list of string

Default:

[  "ghc94"  "ghc96"  "ghc98"  "ghc910"]
haskellTools

Function returning a list of names of Haskell packages that should be included in every environment’s$PATH.This is a convenience variant ofbuildInputs that provides the environment’s GHC package set (withoutoverrides) as a function argument.This is intended for tooling likefourmolu.The default consists ofcabal-install, since that’s a crucial tool most users would expect to be available.If you want to provide a customcabal-install package, you’ll have to sethaskellTools = lib.mkForce (...),since the built-in definition doesn’t usemkDefault to ensure that adding tools in your project won’tmysteriously removecabal-install from all shells.

Type:function that evaluates to a(n) list of package

Example:ghc: [ghc.fourmolu]

hls.genCabal

When running HLS withnix run .#hls, the command first generates Cabal files from Hix config to ensure that HLSworks.If that is not desirable, set this option tofalse.

Type:boolean

Default:true

ifd

Whether to usecabal2nix, which uses Import From Derivation, or to generate simple derivations, for localpackages.

Type:boolean

Default:false

inheritSystemDependentOverrides

Overrides can be exported without a dependency on a system, such that a dependent could use them even if thedependency wasn’t declared for the desired system.However, ififd isfalse in the dependency, the local packages will need thesystem option,and therefore need to be imported fromlegacyPackages.<system>.overrides.

Type:boolean

Default:true

inputs

The inputs of the Hix flake.

Type:lazy attribute set of unspecified value(read only)

main

The name of a key inpackages that is considered to be the main package.This package will be assigned to thedefaultPackage flake output that is built by a plainnix build.If this option is undefined, Hix will choose one of the packages that are not in the dependencies of any otherpackage.

Type:string

manualCabal

Don’t use the options inpackages as Cabal configuration for the ghci preprocessor.

Type:boolean

Default:false

name

The name of the project is used for some temporary directories and defaults tomain.If no packages are defined, a dummy is used if possible.

Type:string

Default:"hix-project"

output.expose.appimage

Include AppImage derivations for all executables in the outputs.

Type:boolean

Default:true

output.expose.cross

Include full cross-compilation system sets in the outputs (likehix.cross.{mingw32,aarch64-android,...}).

Type:boolean

Default:true

output.expose.internals

Include the config set, GHC packages and other misc data in the outputs.

Type:boolean

Default:true

output.expose.managed

Include apps for managed dependencies in the outputs, even as stubs if the feature is disabled.

Type:boolean

Default:true

output.extraPackages

Names of packages that will be added to the flake outputs, despite not being declared inpackages.

This may be a simple Hackage package likeaeson or a local package that is added inoverrides due to the way its source is obtained.

Type:list of string

Default:[ ]

output.final

The final flake outputs computed by Hix, defaulting to the set inoutputs andits siblings.May be overriden for unusual customizations.

Type:raw value

outputs.packages

The flake output attributepackages.

Type:lazy attribute set of package

outputs.apps

The flake output attributeapps.

Type:lazy attribute set of flake output of type ‘app’

outputs.checks

The flake output attributechecks.

Type:lazy attribute set of package

outputs.devShells

The flake output attributedevShells.

Type:lazy attribute set of package

outputs.legacyPackages

The flake output attributelegacyPackages.

Type:lazy attribute set of raw value

overrides

Cabal package specifications and overrides injected into GHC package sets.Each override spec is a function that takes a set of combinators and resources like nixpkgs and should return anattrset containing either derivations or a transformation built from those combinators.

The combinators are described inthe section called “Override combinators”.

Type:Haskell package override function specified in the Hix DSL

Default:[ ]

Example:

{hackage, fast, jailbreak, ...}: {  aeson = fast (hackage "2.0.0.0" "sha54321");  http-client = unbreak;};
pkgs

The nixpkgs attrset used by the default GHC.

Type:nixpkgs attrset(read only)

services

Services are fragments of NixOS config that can be added to an environment to be started as virtual machines whenthe environment is used in a command or shell.

Type:attribute set of module

Default:{ }

system

The system string likex86_64-linux, set by iterating oversystems.

Type:string(read only)

systems

The systems for which to create outputs.

Type:list of string

Default:

[  "aarch64-linux"  "aarch64-darwin"  "i686-linux"  "x86_64-darwin"  "x86_64-linux"]

Multiple systems and IFD

A set of flake outputs is defined for a specific architecture/system combination likex86_64-linux, which is thesecond level key in theoutputs set.When a flake command likenix build is executed, Nix determines the system identifier matching the current machineand looks up the target in that set, likepackages.x86_64-linux.parser.This means that you have to define a (not necessarily) identical output set for each architecture and system you wouldlike to support for your project.

While Hix takes care of structuring outputs for all supportedsystems (viaflake-utils), this feature comes with a significant caveat:When any part of an output set cannot be purely evaluated because it imports a file that was created by a derivationin the same evaluation (which is called Import From Derivation, IFD), evaluating this output will cause thatderivation to be built.

This becomes a critical problem when running the commandnix flake check, which is a sort of test suite runner forflakes, because it evaluatesall outputs before running the checks.Hix defines checks for all packages and GHC versions, so it is generally desirable to be able to run this command inCI.Unfortunately,cabal2nix (which generates all Haskell derivations) uses IFD.

On-the-fly derivations

To mitigate this problem, Hix can generate derivations in a similar manner tocabal2nix, controlled by the optionifd (on by default).For local packages, this is rather straightforward – Hix can use the package config to determine the derivationsdependencies and synthesize a simple derivation.

It gets more difficult whenoverrides are used, since these are usually generated from Hackage or flakeinputs, where the only information available is a Cabal file.In order to extract the dependencies, we would either have to run a subprocess (via IFD) or implement a Cabal configparser in Nix (which hasalso been done).

As a pragmatic compromise, Hix instead splits the override derivation synthesis into two separate passes:

  • The flake appgen-overrides collects allcabal2nix overrides and stores their derivations in a file in therepository.

  • Whenever a build is performed afterwards, it loads the derivations from that file, avoiding the need for IFD!

In order to use this feature, the optiongen-overrides.enable must be set totrue.

The derivations can be written to the file configured bygen-overrides.file with:

$ nix run .#gen-overrides

Of course this means thatgen-overrides will have to be executed every time an override is changed, but don’t worry– Hix will complain when the metadata doesn’t match!

Note

This feature is experimental.

Note

If your configuration evaluates overridden packages unconditionally, the command will not work on the first execution,since it will try to read the file and terminate with an error.In that (unlikely) case, you’ll have to setgen-overrides.enable = false before running it for the first time.

Another potential source fornix flake check to fail is when an environment with a virtual machine is exposed as ashell.For that purpose, the optionsystems can be used to define for which systems the environment should beexposed.

User Interface

Options for UI behavior:

UI options

ui.warnings.all

Whether to enable all warnings.Set this tofalse to suppress all warnings and useui.warnings.keys to enable themselectively.

Type:boolean

Default:true

ui.warnings.keys

The set of warnings that should be enabled or disabled individually.Keys are warning IDs and values are booleans, wheretrue enables a warning andfalse disables it.If a key is not present in this set, the warning is printed ifui.warnings.all istrue.

Type:attribute set of boolean

Default:{ }

Environments and commands

Table of Contents

Environments
Using environments
Configuring GHC
Override combinators
Configuring nixpkgs
Commands
Component-dependent environments
Built-in commands
Services
Defining modular services
Environment options
GHC options
Command options
GHCi(d) options
Service options

Environments

Actions like building a derivation and running HLS or GHCi are executed in an environment, which is a set ofconfiguration options consisting of:

  • A GHC at a specific version

  • A set of packages that include project dependencies and tools like Cabal and HLS

  • An optional set of services that may be run in a virtual machine, like database servers

  • Environment variables

This example configuration defines an environment that uses GHC 9.4, addssocat to the packages in$PATH and runsa PostgreSQL server:

{  outputs = {hix, ...}: hix ({config, ...}: {    envs.example = {      ghc.compiler = "ghc94";      buildInputs = [config.pkgs.socat];      services.postgres = {        enable = true;        config = { name = "test-db"; };      };    };  });}

Using environments

An environment can be used in different ways: By a derivation, a devshell, or acommand.A derivation only uses the GHC, Cabal and the project dependencies as inputs, while a command execution, like GHCid,is preceded by booting a VM if any services are configured.For the latter, the environment provides a script that wraps the command with the startup/shutdown code for the VM andadds all build inputs to the$PATH.

The simplest way to use an environment is as a devshell:

$ nix develop .#example>>> Starting VM with base port 20000>>> Waiting 30 seconds for VM to boot...$ ghc --versionThe Glorious Glasgow Haskell Compilation System, version 9.4.3$ psql "host=localhost port=25432 user=test-db password=test-db dbname=test-db" -c 'select 1'?column?----------        1(1 row)$ exit>>> Killing VM

Note

Usingnix develop -c some-command to execute a shell command in an environment will fail to stop the VM afterwards.Use acommand for one-shot executions.

The default environment is calleddev and is used for everything that doesn’t have a custom environment.For instance, the default devshell uses this environment when entered withnix develop without an explicit argument.Runningcabal build in that shell will use the configured GHC.

Configuring GHC

The most important part of an environment is the associated GHC.As demonstrated in the above example, the optionghc.compiler in an environment is used to select the package setcorresponding to a version.These package sets are predefined by nixpkgs – each snapshot has a certain number of GHC versions with packagesobtained from Stackage and Hackage.

The GHC executable provided by an environment comes preloaded with all dependencies declared in the project’s Cabalfiles (which amounts to those declared inpackages).When runningghci, those dependencies are importable.

The default package set doesn’t always provide the version of a package that the project requires – for example,sometimes you want to use a newer version than the stable one in a Stackage snapshot.For this purpose, Hix offers a DSL for package overrides:

{  overrides = {hackage, ...}: {    streamly = hackage "0.9.0" "1ab5n253krgccc66j7ch1lx328b1d8mhkfz4szl913chr9pmbv3q";  };}

Overrides are defined as a function that produces a set mapping package names to dependency specifications and takesas its argument a set of combinators and metadata for declaring those specifications, like thehackage combinatorused above that takes a version and Nix store hash to fetch a package directly from Hackage.They can be specified at different levels, like dependencies: At the top level for all environments, in eachindividual environment, and on theghc module in an environment (although the latter is populated by Hix with themerged global and local overrides).

Note

nixpkgs’ shipped GHC package sets come with a few special derivations whose attribute names are suffixed with aconcrete version, likeCabal_3_10_2_1, to be used as overrides for packages with incompatible requirements.If you create a Hackage override for such a package, there’s a chance that it will result in aninfinite recursionerror.The precise reason for this is not clear to me yet, but it can be avoided by using that special derivation, if it iscompatible with your dependency set:

{overrides = {super, …}: {broken-dep = super.broken-dep_1_2_3;};}

Override combinators

There are different classes of combinators.The most fundamental ones are used to declare package sources:

  • hackage takes two arguments, version and hash, to pull a dependency directly from Hackage.

  • source.root takes a path to a source directory (like a flake input) and builds the dependency from its contents.

  • source.sub is the same assource.root, but takes a second argument describing a subdirectory of the path.

  • source.package is the same assource.sub, but it prependspackages/ to the subdirectory.

  • hackageAt is likehackage, but takes an additional first argument specifying the address of the server.

  • hackageConf is likehackageAt, but instead of an address, it takes the name of an item inhackage.repos.If a server with the keyhix-override is configured, the combinatorhackage will automatically use it instead ofnixpkgs’ built-in logic.

  • disable ornull (the literal) results in the final derivation being assignednull.This may be useful to force the dependency by that name to be provided by some other mechanism; for example, bootlibraries are available in the compiler package and are set tonull in nixpkgs.

When an override is defined usingsource combinators, the derivation is generated bycabal2nix.Consider the following override for a package nameddep that uses a flake input as the source path:

{  inputs = {    hix.url = "...";    dep.url = "...";  };  outputs = {hix, dep, ...}: hix {    overrides = {source, ...}: {      dep = source.root dep;    };  };}

The implementation ofsource.root roughly consists of the function callhaskellPackages.callCabal2nix "dep" dep { <optional overrides>; } which invokeshaskellPackages.haskellSrc2nix,which executes the programcabal2nix, which in turn analyzes thedep.cabal in the source tree of the flake inputand generates a function that looks like this:

{  cabal2nix_dep = {mkDerivation, aeson, base, pcre}: mkDerivation {    pname = "dep";    src = <path passed to source.root>;    libraryHaskellDepends = [aeson base];    librarySystemDepends = [pcre];    ...  }}

Its arguments consist ofdep’s Haskell and system dependencies as well as themkDerivation function.All of these are ultimately provided by the GHC package set, andcallCabal2nix wraps this generated function withthe commoncallPackagepattern, which allows individual arguments to be overridden.For example, to sketch a simplified representation of how our local package would be merged into the nixpkgs GHCpackage set, the above function would be called like this:

let  callPackage = lib.callPackageWith ghc;  ghc = {    # These two are defined in `pkgs/development/haskell-modules/make-package-set.nix`    mkDerivation = pkgs.callPackage ./generic-builder.nix { ... };    aeson = callPackage aesonDrv {};    ...    api = callPackage cabal2nix_dep {      aeson = someCustomAesonDerivation; # Haskell package override      pcre = pkgs.pcre; # System package override    };  };in ghc

Now, this is already very convoluted, and the real thing is even worse; but you can see:

  • ThecallPackage variant used for the Haskell derivations is created withlib.callPackageWith by recursivelyproviding the setghc, which consists of all the lazy attributes that are themselves constructed with that samefunction.Just like most of nixpkgs, the fixpoint of this computation is the final value ofghc, allowing each package todepend on others from the same set without having to provide the deps directly.

  • mkDerivation isalso created with acallPackage variant, but it’s the top-level nixpkgs set that provides thevalues.

  • mkDerivation will be passed to the calls inaeson anddep automatically.

  • The call to ourcabal2nix derivation created bysource would get the sibling attributeaeson, but the secondargument tocallPackage here contains some custom override (as well as another forpcre), which takes precedenceover the “default arguments” in the recursive set.Now, of course, these overrides need to be specified somehow, and the mechanism for that is the third argument tocallCabal2nix that is labeled with<overrides> in the above example.However, this argument is not directly exposed insource.root.Instead, another type of override combinator provides that functionality, described below (cabal2nixOverrides).

Other combinators

The second class consists of “transformers”, which perform some modification on a derivation.They can either be applied to a source combinator or used on their own, in which case they operate on whatever theprevious definition of the overridden package is (for example, the default package from nixpkgs, or an override fromanother definition ofoverrides).

Transformers also compose – when using multiple of them in succession, their effects accumulate:

{  overrides = {hackage, notest, nodoc, configure, jailbreak, nobench, ...}: {    # Fetch streamly from Hackage and disable tests and haddock    streamly = notest (nodoc (hackage "0.9.0" "1ab5n253krgccc66j7ch1lx328b1d8mhkfz4szl913chr9pmbv3q"));    # Use the default aeson package and disable tests    aeson = notest;    # Disable version bounds, tests, benchmarks and Haddock, and add a configure flag    parser = configure "-f debug" (jailbreak (notest (nobench nodoc)));  };}

The available transformers are:

  • unbreak – Override nixpkgs’ “broken” flag

  • jailbreak – Disable Cabal version bounds for the package’s dependencies

  • notest – Disable tests

  • nodoc – Disable Haddock

  • bench – Enable benchmarks

  • nobench – Disable benchmarks

  • minimal – Unbreak and disable profiling libraries, Haddock, benchmarks and tests

  • fast – Disable profiling and Haddock

  • force – Unbreak, jailbreak and disable Haddock, benchmarks and tests

  • noprofiling – Disable profiling libraries for the package

  • profiling – Enable executable profiling for the package

  • configure – Add a Cabal configure CLI option

  • configures – Add multiple Cabal configure CLI options (in a list)

  • enable – Enable a Cabal flag (-f<flag>)

  • disable – Disable a Cabal flag (-f-<flag>)

  • ghcOptions – Pass GHC options to Cabal (--ghc-options)

  • override – CalloverrideCabal on the derivation, allowing arbitrary Cabal manipulation

  • overrideAttrs – CalloverrideAttrs on the derivation

  • buildInputs – Add Nix build inputs

The third class consists of “options”, which modify the behavior of other override combinators:

  • cabal2nixOverrides – An attrset used as the third argument ofcallCabal2nix when called bysource combinators,as described in the example in the previous section, used as follows:

    {  overrides = {cabal2nixOverrides, source, pkgs}: {    dep = cabal2nixOverrides { aeson = ...; pcre = pkgs.pcre.override {...}; } (source.root dep);  }}

    This would result in bothaeson andpcre being overridden, for the derivation ofdep, by the packages suppliedexplicitly.

  • cabal2nixArgs – CLI options forcabal2nix.

    cabal2nix doesn’t have many options that would make a lot of sense in this context, but one is particularlyuseful:

    Likecabal-install,cabal2nix recognizes the option-f to enable or disable Cabal flags, which are often usedto configure optional dependencies.The problem with passing these only to Cabal in the final derivation is that the function generated bycabal2nixwill require exactly those dependencies that were enabled when it was invoked.

    Imagine that our example package has a Cabal flaglarge-records that controls two effects: an additionaldependency on the packagelarge-records and some CPP-guarded code that uses this library.Disabling a flag withdisable "large-records" (source.root dep) will ultimately cause Cabal to builddep withoutlarge-records visible in the GHC package DB, as well as ignore the additional code, but the derivation will stillhave the dependency onlarge-records, since that is not interpreted by Cabal.However,cabal2nixArgs "-f-large-records" (source.root dep) will avoid the dependency entirely.

Finally, there are some special values that are injected into the override function:

  • self andsuper – The final and previous state of the package set, as is common for nixpkgs extension functions

  • pkgs – The nixpkgs set

  • keep – Reset the previous combinators and use the default package

  • hsLib – The entire nixpkgs tool sethaskell.lib

  • hsLibC – The entire nixpkgs tool sethaskell.lib.compose.This is preferable tohsLib, but for historic reasons that one ended up in here first, and it’s gonna stay forcompatibility.

  • compilerName – Thename attribute of the GHC package

  • compilerVersion – Theversion attribute of the GHC package

Transitive overrides

Overrides can be exported from the flake in order to reuse them in other projects.When a downstream flake has projectfoo as an input, settingdeps = [foo] will causefoo’s overrides to beincorporated into the local ones.Furthermore, the optiondepsFull will additionally includefoo’s local packages in the overrides:

{  inputs.dep1.url = github:me/dep1;  inputs.dep2.url = github:me/dep2;  outputs = { hix, dep1, dep2, ... }: hix {    deps = [dep1];    depsFull = [dep2];  };}

Configuring nixpkgs

The GHC package set uses the same nixpkgs snapshot that is also used for Hix internals, which is configured as a flakeinput of the Hix repository.You can override this globally:

{  inputs.hix.inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";}

In order to avoid incompatiblities with the Hix internals, it might be advisable to only override the nixpkgs used byGHC:

{  inputs.hix.url = "github:tek/hix";  inputs.ghc_nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";  outputs = { hix, ghc_nixpkgs, ... }: hix {    envs.dev.ghc.nixpkgs = ghc_nixpkgs;  };}

Since the usage of nixpkgs within the library is tightly interwoven with the GHC package set, this might have a slightpotential for breakage, but (like the global variant) it should be minimal.

Commands

Services in an environment are relevant when executing a command, which consists of an arbitrary shell scriptassociated with an environment.When the command is executed, the environment’scode option will be run beforehand, which boots the VM with theservices and sets up an exit hook for shutting it down.code is a script assembled from other options, notablysetup-pre,setup,exit-pre andexit.

A command can be defined as follows:

{  commands.integration-test = {    env = "example";    command = "cabal test api-integration";  };}

This assumes that your project contains some Cabal component namedapi-integration that needs a PostgreSQL serverrunning.When executing this command, the setup code from the previously defined environmentexample will be executed,starting the virtual machine, before running the specified Cabal command line:

nix run .#cmd.integration-test

Component-dependent environments

When the command optioncomponent is set totrue, the command will take two argument lists, separated by a--.The first batch of arguments is passed to the Hix CLI to select the environment, the second one is assigned to theenvironment variable$cmd_args to be used by the command script.

This allows the user to select an environment dynamically from the command line, without having to staticallyassociate it in the Nix config or use complex expressions with nested invocations of Nix, realized by encoding allcomponents and environments as JSON and extracting the environment runner based on the CLI arguments.The primary use case for this is to use a different environment when running a GHCid test, like running a databaseserver for integration tests.

There are three alternative selection methods, illustrated by this example:

{  description = "hix test project";  inputs.hix.url = "github:tek/hix?ref=0.8.0";  outputs = {hix, ...}: hix ({config, ...}: {    envs = {      one.env = { number = 1; };      two.env = { number = 2; };      three.env = { number = 3; };    };    packages.root = {      src = ./.;      executable.env = config.envs.two;    };    commands.number = {      env = config.envs.one;      command = ''      echo $number      '';      component = true;    };  });}

The first method is to use the flake app path to select an environment by name, then append the command name:

$ nix run .#env.three.number3

The second method is to select a component by its package name or directory and component name or source directory:

$ nix run .#cmd.number -- -p api -c server$ nix run .#cmd.number -- -p packages/api -c app2

The third method is to specify a Haskell source file and let Hix figure out which component it belongs to:

$ nix run .#cmd.number -- -f /path/to/packages/api/test/NumberTest.hs2

This method needs to know the root directory of the project, which is determined by searching forflake.nix with afallback to the current working directory.The root directory may also be specified explicitly using the CLI option--root.

If no selection arguments are given, the command’s default environment is used:

$ nix run .#cmd.number1

Built-in commands

Hix provides two special commands for executing a function in GHCi or GHCid.

Theghcid command in particular is highly useful for repeatedly executing a test whenever a source file is written.Compilation is very fast since GHCi doesn’t need to link the executable and doesn’t perform optimization in thedefault mode, leading to sub-second feedback loops on small projects (or dependency closures).

nix run .#ghci -- -p root -r servernix run .#ghcid -- -p root -t test_server -r hedgehog-unit

Their interface is mostly identical to the generic commands described above, while taking three additional, optionalcommand line options:

  • The name of a module (-m), defaulting toMain.This module is loaded in GHCi and hence only its dependencies are (re)compiled.

  • The name of a Haskell test function (-t) that will be called after the module was loaded successfully.See next bullet for defaults.

  • The name of an attribute in the Hix optionghci.run (-r).This option contains a Haskell expression for each key which will be executed after the module was loadedsuccessfully.If a test function was specified, this expression will be applied to it.If the runner wasn’t specified, the test function will be run directly.If neither this nor the test function was specified, theghci command will drop to interactive mode directly whiletheghcid command defaults tomain.

For example, the built-inhedgehog-unit entry inghci.run has the valuecheck . withTests 1 . property . test.When specifying the name of a Hedgehog test like in the example above, the evaluated epression will be:

ghci> (check . withTests 1 . property . test) test_server

You can specify arbitrary additional command line arguments for GHCi and GHCid with--ghci-options and--ghcid-options.

Another built-in command isrun, which executes an arbitrary shell command in an environment:

$ nix run .#env.ghc92.run -- "ghc --version"The Glorious Glasgow Haskell Compilation System, version 9.2.4

Note

If you depend on the GHC library packageghc, you’ll have to setghci.args = ["-package ghc"];.Otherwise it won’t be visible, due to a bug.

Services

Services used in environments and commands can be user-defined in a flake:

{  services.http = {    nixos.services.nginx = {      enable = true;      virtualHosts.localhost.locations."/test".return = "200 Yay!";    };    ports.nginx = { host = 2000; guest = 80; };  };  envs.test = {    basePort = 10000;    services = { http = { enable = true; }; };  };}

This defines a service namedhttp that runs an nginx with a single endpoint at/test and exposes it in the hostsystem at port offset 2000 (relative to an environment’sbasePort).The environmenttest references the service, so when a command uses this environment, a VM will be started:

nix run .#env.test.ghcid -- -p root

Since the environment usesbasePort = 10000, the nginx server will listen on port 12000.You can refer to the effective port from other options withconfig.envs.hostPorts.nginx (the attribute name inports).

Hix provides built-in services, like the previously mentioned PostgreSQL server, that have specialized configurationoptions.They can be configured in the same option as the definition of a new service, still allowing the specification ofadditional NixOS config as described before, inservices.<name>.nixos.

Furthermore, an environment may provide Hix config overrides inenvs.<name>.services.<name>.config that is combinedwith the config inservices.<name>.

{  outputs = {hix, ...}: hix ({config, ...}: {    services.postgres = {      # Add NixOS config to the default config computed by Hix      nixos.users.users.postgres.extraGroups = ["docker"];      # Configure PostgreSQL specifically, used by `services.postgres.nixos-base` internally      creds.user = "root";    };    envs.example = {      services.postgres = {        # Declare that this environment uses `config.services.postgres` above        enable = true;        # Add overrides for the configuration in `config.services.postgres`        config = { name = "test-db"; };      };    };  });}

In order to define a service with specialized config, an entry in the optioninternal.services.myservice must beprovided that contains a module with option declarations.This option has typedeferredModule, which means that it’s not evaluated at the definition site, but used in a magicway somewhere else to create a new combined module set consisting of all the configs described before.

You can log into a service VM via ssh.The default configuration sets the root password to the empty string and exposes the ssh port in the host system atbasePort + 22:

ssh -p 1022 root@localhost

Defining modular services

A service can be defined inservices with plain NixOS config, but it is useful to allow the serviceto be specially configurable.For that purpose, the value assigned to the entry inservices should be a full module that definesoptions as well as their translation to service config:

{  services.greet = ({config, lib, ...}: {    options.response = mkOption {      type = lib.types.str;      default = "Hello";    };    config = {      ports.greet = { guest = 80; host = 10; };      nixos-base.services.nginx = {        enable = true;        virtualHosts.localhost.locations."/greet".return = "200 ${config.response}";      };    };  });  envs.bye = {    services.greet = {      enable = true;      config.response = "Goodbye";    };  };}

This defines a service running nginx that has a single endpoint at/greet, which responds with whatever isconfigured in the service optionresponse.The environmentbye uses that service and overrides the response string.

Any option defined withinservices.greet.options is configurable from within the environment, and the values inservices.greet.config correspond to the regular service options.

Environment options

enable

Whether to enable this environment.

Type:boolean

Default:false

Example:true

packages

The subset of localpackages that should be built by this environment.

Entries must correspond to existing keys inpackages.If the value isnull, all packages are included.

This is useful when the project contains multiple sets of packages that should have separate dependency trees withdifferent versions.

Setting this has a variety of effects:

  • These packages will be exposed as outputs for this env

  • The GHC package db will not contain the excluded packages, so they won’t be built when entering a shell orstarting.#ghci for this env

  • Managed dependencies envs use this to produce separate sets of bounds

Type:null or (list of name of a package defined in config.packages)

Default:null

basePort

The number used as a base for ports in this env’s VM, like ssh gettingbasePort + 22.

Type:16 bit unsigned integer; between 0 and 65535 (both inclusive)

Default:20000

buildInputs

Additional system package dependencies for this environment.

Note

These are only made available to shells and commands, not added to packages, like when they are set in overrides.

Type:(function that evaluates to a(n) list of package) or list of package

Default:[ ]

code

The shell script code that starts this env’s services and sets its environment variables.

Type:string

Default:

''  _hix_unrestricted() {    [[ -z ''${DIRENV_IN_ENVRC-} ]] && [[ -z ''${HIX_ONLY_ENV-} ]]  }  quitting=0  quit() {    if [[ $quitting == 0 ]]    then      quitting=1      if [[ -n ''${1-} ]]      then        echo ">>> Terminated by signal $1" >&2      fi                        # kill zombie GHCs      /nix/store/nyqwmqlnnhq3x5718309wrkif0wrppvp-procps-4.0.4/bin/pkill -9 -x -P 1 ghc || true    fi    if [[ -n ''${1-} ]]    then      exit 1    fi  }  if _hix_unrestricted  then    if [[ -z ''${_hix_is_shell-} ]]    then      trap "quit INT" INT    fi    trap "quit TERM" TERM    trap "quit KILL" KILL    trap quit EXIT  fi    export PATH="/nix/store/0px0carb0vl8bzjviz3siqa7asq97a5x-cabal-install-3.14.1.0/bin:/nix/store/x6zmmsvwyfa1yxgpzl0dlkdllcd8jckm-ghcid-0.8.9-bin/bin:/nix/store/d9v1lxs84wq29796v1s8b558g9kc5cf5-ghc-9.8.4/bin:$PATH"  export env_args  if _hix_unrestricted  then    :                      fi''
defaults

Whether to use the common NixOS options for VMs.

Type:boolean

Default:true

env

Environment variables to set when running scripts in this environment.

Type:attribute set of (signed integer or string)

Default:{ }

exit

Command to run when the env exits.

Type:string

Default:""

exit-pre

Command to run before the service VM is shut down.

Type:string

Default:""

expose

The parts of this environment that should be accessible as flake outputs, like being able to runnix build .#<env>.<package>.If the value is boolean, all parts are affected.If it is a set, submodule options configure the individual parts.

Type:boolean or (submodule)

Default:false

ghc

The GHC configuration for this environment.

Type:submodule

Default:{ }

ghcWithPackages

The fully configured GHC package exposing this environment’s dependencies.

Type:package(read only)

ghcWithPackagesArgs

Additional arguments to pass toghcWithPackages.

Type:attribute set of unspecified value

Default:{ }

ghcid.enable

Whether to enable GHCid for this env.

Type:boolean

Default:true

Example:true

ghcid.package

The package for GHCid, defaulting to the one from the env’s GHC without overrides.

Type:package

Default:<derivation ghcid-0.8.9>

haskellPackages

Names of Haskell packages that should be added to this environment’s GHC’s package db, making them available forimport.These may include the local packages.

Type:(function that evaluates to a(n) list of package) or list of string

Default:[ ]

haskellTools

Function returning a list of names of Haskell packages that should be included in the environment’s$PATH.This is a convenience variant ofbuildInputs that provides the environment’s GHC package set (withoutoverrides) as a function argument.This is intended for tooling likefourmolu.

Type:function that evaluates to a(n) list of package

Default:<function>

Example:ghc: [ghc.fourmolu]

hls.enable

Whether to enable HLS for this env.

Type:boolean

Default:false

Example:true

hls.package

The package for HLS, defaulting to the one from the env’s GHC without overrides.

Type:package

Default:<derivation haskell-language-server-2.9.0.0>

hoogle

Whether to enable Hoogle in this environment.

Type:boolean

Default:false

hostPorts

The effective ports of the VM services in the host system.Computed frombasePort andports.

Type:attribute set of 16 bit unsigned integer; between 0 and 65535 (both inclusive)(read only)

ifd

Whether to use cabal2nix, which uses Import From Derivation, or to generate simple derivations.

Type:boolean

Default:false

libraryProfiling

Whether to build local libraries with profiling enabled.This is the default mode for Haskell derivations.

Type:boolean

Default:true

localDeps

Whether to add the dependencies of the env’s local packages to GHC’s package db.

Type:boolean

Default:true

localPackage

A function that takes override combinators and a derivation and returns a modified version of that derivation.Called for each cabal2nix derivation of the local packages before inserting it into the overrides.Likeoverrides, but applies too all packages when building with this env.

Type:unspecified value

Default:<function>

Example:

{ fast, nobench, ... }: pkg: nobench (fast pkg);
main

The name of the main package of this env, defaulting tomain if that is inpackagesor one of those packages determined by the same method as describedfor the global equivalent.

Type:null or string

Default:null

managed

Whether this env’s dependencies arethe section called “Automatic dependency management”.This has the effect that its bounds and overrides are read from the managed state inmanaged.file.

Type:boolean

Default:false

name

Name of this environment.

Type:string

Default:"<name>"

overrides

Likeoverrides, but used only when this environment is used to build packages.

Type:Haskell package override function specified in the Hix DSL

Default:[ ]

profiling

Whether to build local libraries and executables with profiling enabled.

Type:boolean

Default:false

runner

An executable script file that sets up the environment and executes its command line arguments.

Type:path

Default:<derivation env--name--runner.bash>

services

Services for this environment.

Type:attribute set of (submodule)

Default:{ }

setup

Commands to run after the service VM has started.

Type:string

Default:""

setup-pre

Commands to run before the service VM has started.

Type:string

Default:""

shell

The shell derivation for this environment, starting the service VM in theshellHook.

Note

If this shell is used withnix develop -c, the exit hook will never be called and the VM will not be shut down.Use a command instead for this purpose.

Type:package

systems

The architecture/system identifiers likex86_64-linux for which this environment works.This is used to exclude environments from being exposed as shells when they are system-specific, for example whenusing a VM that only works with Linux.If those shells were exposed, the commandnix flake check would fail while evaluating thedevShells outputs,since that doesn’t only select the current system.

If set tonull (the default), all systems are accepted.

Type:null or (list of string)

Default:null

vm.enable

Whether to enable the service VM for this env.

Type:boolean

Default:false

Example:true

vm.derivation

The VM derivation

Type:path

vm.dir

Type:string

Default:"/tmp/hix-vm/app/<name>"

vm.exit

Commands for shutting down the VM.

Type:string

vm.headless

VMs are run without a graphical connection to their console.For debugging purposes, this option can be disabled to show the window.

Type:boolean

Default:true

vm.image

The path to the image file.

Type:string

Default:"/tmp/hix-vm/app/<name>/vm.qcow2"

vm.monitor

The monitor socket for the VM.

Type:string

Default:"/tmp/hix-vm/app/<name>/monitor"

vm.name

Name of the VM, used in the directory housing the image file.

Type:string

Default:"<name>"

vm.pidfile

The file storing the qemu process’ process ID.

Type:string

Default:"/tmp/hix-vm/app/<name>/vm.pid"

vm.setup

Commands for starting the VM.

Type:string

vm.system

The system architecture string used for this VM, defaulting tosystem.

Type:string

Default:"x86_64-linux"

wait

Wait for the VM to complete startup within the given number of seconds. 0 disables the feature.

Type:signed integer

Default:30

GHC options

compiler

The attribute name for a GHC version in the sethaskell.packages.

Type:string

crossPkgs

This option can be used to override the pkgs set used for the Haskell package set, for example an element ofpkgsCross:envs.dev.ghc.crossPkgs = config.envs.dev.ghc.pkgs.pkgsCross.musl64

Type:nixpkgs attrset

gen-overrides

Allow this GHC to use pregenerated overrides.Has no effect whengen-overrides.enable isfalse.

Disabled by default, but enabled for GHCs that are defined in an environment.

Type:boolean

Default:false

ghc

The package set with overrides.

Type:Haskell package set(read only)

name

A unique identifier of the package set.

Type:string

nixpkgs

The path to a nixpkgs source tree, used as the basis for the package set.

This can be a flake input or a regular type of path, like the result offetchGit.

Type:nixpkgs snapshot

nixpkgsOptions

Additional options to pass to nixpkgs when importing.

Type:attribute set of unspecified value

overlays

Additional nixpkgs overlays.

Type:list of (overlay)

overrides

The overrides used for this package set – seethe section called “Configuring GHC” for an explanation.

This option is set by environments (seethe section called “Environments”), but GHC modules can be used outside of environments, so thismight be set by the user.

Type:Haskell package override function specified in the Hix DSL

Default:[ ]

pkgs

The nixpkgs set used for this GHC.

Type:nixpkgs attrset

vanillaGhc

The package set without overrides.

Type:Haskell package set(read only)

version

The GHC version as a canonical string, like9.2.5, for use in conditions.

Type:string(read only)

Command options

command

The script executed by this command.

Type:string

component

Whether this command should determine the env based on a target component specified by command line arguments.

Note

The component selector chooses a default component when no arguments are given.If that component has an explicit environment configured, it will be used instead of the one configured in thiscommand.

Type:boolean

Default:false

env

The default env for the command.

Type:name of an environment defined in config.envs

Default:"dev"

expose

Whether this command should be a top-level flake app.

Type:boolean

Default:false

ghci.enable

Create a command that runs GHCi (like the built-in command) with some static options.For example, you can specify arunner, and the app will be equivalent to runningnix run .#ghci -r <runner>.

Type:boolean

Default:false

ghci.package

The name of the package passed to the GHCi runner with-p.

Type:null or string

Default:null

ghci.component

The name of the component passed to the GHCi runner with-c.

Type:null or string

Default:null

ghci.ghcid

Whether to run this command with GHCid instead of plain GHCi.

Type:boolean

Default:false

ghci.module

The name of the module passed to the GHCi runner with-m.

Type:null or string

Default:null

ghci.runner

The name of a runner inghci.run andghci.run.

Type:null or string

Default:null

name

Name

Type:string

Default:"<name>"

GHCi(d) options

ghci.args

The command line arguments passed to GHCi.Setting this option appends to the defaults, so in order to replace them, usemkForce.To only override basic GHC options like-Werror, useghci.ghcOptions.

Type:list of string

ghci.cores

The value for the GHC option-j, specifying the number of system threads to use.

Type:signed integer or string

Default:"\${NIX_BUILD_CORES-}"

ghci.ghcOptions

Command line arguments passed to GHCi that aren’t related to more complex Hix config like the preprocessor.

This option is initialized with values that use the Nix settingcores to set the number ofthreads GHCi should use. If you want to control this yourself, usemkForce here.

Type:list of string

Default:[ ]

ghci.preprocessor

The preprocessor script used to insert extensions and a custom Prelude into source files.This is generated by Hix, but may be overridden.

Type:path

ghci.run

Test functions for GHCi commands.The attribute name is matched against the command line option-r when running apps likenix run .#ghci.

Type:attribute set of string

ghci.setup

Scripts that should be executed when starting a GHCi command, like imports.The attribute name is matched against the command line option-r when running apps likenix run .#ghci.

Type:attribute set of string

Service options

enable

Enable this service

Type:boolean flag that uses conjunction for merging

Default:true

messages

Informational messages that will be echoed when an environment starts this service.

Type:function that evaluates to a(n) list of string

Default:<function>

nixos

NixOS config used for the service VM.

Type:module

Default:{ }

nixos-base

NixOS base config used for the service VM.

Type:module

Default:{ }

ports

Simple ports forwarded relative to the env’sbasePort.

Type:attribute set of (submodule)

Default:{ }

ports.<name>.absolute

Whether the host port is an absolute number. Iffalse (default), the port is added tobasePort.

Type:boolean

Default:false

ports.<name>.guest

Port used in the VM.

Type:16 bit unsigned integer; between 0 and 65535 (both inclusive)

ports.<name>.host

Port exposed in the system, relative to the env’sbasePort unlessports.<name>.absoluteis set.

Type:16 bit unsigned integer; between 0 and 65535 (both inclusive)

Other tools

Table of Contents

Haskell Language Server
GHC version checks
Hackage upload
Hackage options
CTags
Cross-compilation and static linking
AppImage bundles
Automatic dependency management
Upper bounds and latest versions
Lower bounds
Target sets
Fine tuning
Managed dependencies options
Miscellaneous tools
Show overrides
Show config
Show dependency versions
Access to intermediate outputs

Haskell Language Server

Hix provides a flake app for running HLS with the proper GHC (for thedev env) and the project dependencies:

nix run .#hls

In order to use it with your IDE, you need to specify the command in the editor configuration as:nix run .#hls --,sincenix consumes all options until a-- is encountered, and only passes what comes afterwards to the program(which in this case would be--lsp).

This app corresponds to thecommand namedhls, which uses thedev environment but gets the HLSexecutable from a special environment namedhls.This allows the HLS package and its dependencies to be configured separately from the project dependencies.For example, to use the package exposed from the HLS flake:

{  inputs.hls.url = "github:haskell/haskell-language-server?ref=1.9.0.0";  outputs = {hix, hls, ...}: ({config, ...}: {    envs.hls.hls.package = hls.packages.${config.system}.haskell-language-server-925;  });}

Additionally, all other environments can expose HLS as well:

{  envs.ghc94.hls.enable = true;}
nix run .#env.ghc94.hls

This is disabled by default to avoid building HLS for environments whose GHCs don’t have its derivation in the Nixcache.

Since the dev environment exposes HLS by default, the executable (haskell-language-server) is in$PATH in thedefault devshell, so it can also be run withnix develop -c haskell-language-server.

GHC version checks

The environments created for each entry inghcVersions are intended primarily as a CI tool to ensurethat the project builds with versions other than the main development GHC.For that purpose, thechecks output contains all packages across those environments, which can be built with:

nix flake check

Hackage upload

Hix provides flake apps that run the flake checks and upload package candidates, releases or docs to Hackage:

nix run .#candidatesnix run .#releasenix run .#docs

IfversionFile is set, the script will substitute theversion: line in that Cabal file after asking forthe next version.If you usenix run .#gen-cabal to maintain the Cabal files, this should be a.nix file containing a string that’salso used forversion:

{  packages.parser = {    cabal.version = import ./parser-version.nix;    versionFile = ./parser-version.nix;  };}

The optionshackage.versionFileExtract andhackage.versionFileUpdate can becustomized to allow for arbitrary other formats.

The command line option--version/-v may be used to specify the version noninteractively.Furthermore, if a package name is specified as a positional argument, only that package will be uploaded.

For example, to publishparser at version2.5.0.1:

nix run .#release -- parser -v 2.5.0.1

The upload command will use your global Cabal config to obtain credentials, please consult the Cabal docs for more.

The options modulehackage.hooks provides a way to execute scripts at certain points in the release process.

Hackage options

hackage.packages

The set of packages that will be published to Hackage when the release command is run without arguments.If it isnull, all packages are published.The items in the list should be Cabal package names as defined inoptions.packages.

Type:null or (list of string)

Default:null

hackage.add

Whenhackage.add is set tofalse, this option can be enabled to git-add the files but notcommit them.

Type:boolean

Default:false

hackage.allPackages

There are two modes for versioning: Either all packages share the same version, in which case the release appwill publish all packages at the same time, or each package has an individual version, in which case the releaseapp expects the name of a package to be specified.

Type:boolean

Default:true

hackage.askVersion

Whether to interactively query the user for a new version when releasing.

Type:boolean

Default:true

hackage.cabalArgs

Extra global CLI arguments forcabal.

Type:string

Default:""

hackage.cabalUploadArgs

Extra CLI arguments forcabal upload to use inhackage.uploadCommand.

Type:string

Default:""

hackage.check

Whether to runnix flake check before the release process.

Type:boolean

Default:true

hackage.commit

After successfully uploading a new release, the changes to the version file, cabal files and changelog will becommitted unless this is set tofalse.

Type:boolean

Default:true

hackage.commitExtraArgs

Extra CLI options forgit commit.

Type:string

Default:""

hackage.confirm

Whether to ask for confirmation before uploading.

Type:boolean

Default:true

hackage.formatTag

Function that creates a tag name from a version and an optional package name.

Type:function that evaluates to a(n) string

Default:<function, args: {name, version}>

hackage.hooks.postCommitAll

Shell script lines (zsh) to run after commiting the version change after publishing all packages.

Type:strings concatenated with “\n”

Default:""

hackage.hooks.postUploadAll

Shell script lines (zsh) to run after uploading all packages.

Value is a function that gets the set{source, publish}, two booleans that indicate whether the sources (oronly docs) were uploaded, and whether the artifacts were published (or just candidates).

Type:function that evaluates to a(n) strings concatenated with “\n”

Default:<function>

hackage.hooks.preCommitAll

Shell script lines (zsh) to run before commiting the version change after publishing all packages.

Type:strings concatenated with “\n”

Default:""

hackage.repos

Hackage repos used by the CLI for several tasks, like resolving managed dependencies and publishing packages andrevisions.The default config consists of the usual server athackage.haskell.org.

Type:attribute set of (submodule)

Default:{ }

hackage.repos.<name>.enable

Whether to enable this Hackage server.

Type:boolean

Default:true

hackage.repos.<name>.description

Arbitrary short text for presentation, like ‘local Hackage’.

Type:null or string

Default:null

hackage.repos.<name>.indexState

When resolving, use the index at this time.

Type:null or string

Default:null

Example:"2024-01-01T00:00:00Z"

hackage.repos.<name>.keys

Security keys for this server.

Type:null or (non-empty (list of string))

Default:null

hackage.repos.<name>.location

Server URL with scheme and optional port.

Type:string

Default:"https://hackage.haskell.org"

Example:"https://company-hackage.com:8080"

hackage.repos.<name>.name

Name used to refer to this server in other components.Uses the attribute name and cannot be changed.

Type:string(read only)

hackage.repos.<name>.password

Password for uploading.

Type:null or string or (submodule)

Default:null

hackage.repos.<name>.publish

Publish packages to this server.

Type:boolean

Default:false

hackage.repos.<name>.secure

Use the newer Cabal client that verifies index signatures viahackage-security.

Type:null or boolean

Default:true

hackage.repos.<name>.solver

Use this server for the Cabal resolver when managing dependency versions.

Type:boolean

Default:true

hackage.repos.<name>.user

User name for uploading.

Type:null or string

Default:null

hackage.setChangelogVersion

Whether to substitute the word ‘Unreleased’ with the new version in changelogs.

Type:boolean

Default:false

hackage.tag

After successfully uploading a new release, a tag with the version name will be created unless this is set tofalse.

Type:boolean

Default:true

hackage.tagExtraArgs

Extra CLI options forgit tag.

Type:string

Default:""

hackage.uploadCommand

The command used to upload a tarball, specified as a function that takes a set as a parameter with theattributes:

{  publish = "Boolean indicating whether this is a candidate or release";  doc = "Boolean indicating whether this is a source or doc tarball";  path = "The tarball's file path";}

Type:function that evaluates to a(n) string

hackage.versionFile

If multiple packages use the same file for the version (like when using shared hpack files) this option maypoint to that file.Ifhackage.allPackages istrue and this option isnull, the version will not be modified by the releaseapp.If the project uses the feature for hpack config synthesis from nix expressions, the version must be defined ina nix file.In that case, the simplest mechanism would be to use a separate file that only contains a string and isintegrated into the config withversion = import ./version.nix;.The default version handlers make this assumption; if a different method is used, the optionshackage.versionFileExtract andhackage.versionFileUpdate must be adapted.

Type:null or string

Default:null

hackage.versionFileExtract

A function that returns a shell script fragment that extracts the current version from a version file.The default assumes hpack/cabal format, likeversion: 5, unless the file has the extension.nix, in which case it is assumed the file only contains a string.

Type:function that evaluates to a(n) string

Default:<function>

hackage.versionFileUpdate

A function that returns a shell script fragment that updates the current version in a version file.The new version is stored in the environment variable$new_version in the surrounding shellscript.The default assumes hpack/cabal format, likeversion: 5, unless the file has the extension.nix, in which case it is assumed the file only contains a string.

Type:function that evaluates to a(n) string

Default:<function>

CTags

Hix exposes an app that runsthax to generate a CTags file covering all dependenciesand the local packages:

nix run .#tags

This will result in the creation of the file.tags.

Cross-compilation and static linking

All package outputs can be cross-compiled with the following syntax:

nix build .#parser.cross.musl64

In addition, the package may also be linked statically against its Haskell dependencies:

nix build .#parser.cross.musl64.static

Note that this does not result in a static binary – it will still be linked dynamically against libc.

For more elaborate cross-compilation setups, each GHC can be configured to use across pkgs set:

{  envs.dev.ghc.crossPkgs = config.envs.dev.ghc.pkgs.pkgsCross.musl64;}

Formusl, there are two native package sets in nixpkgs that are supported by Hix:

nix build .#parser.musl

This will result in a binary that’s similar to.#parser.cross.musl64.static.

For a fully static build, you can use thestatic attribute:

$ nix build .#parser.static$ file result/bin/parserresult/bin/parser: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

AppImage bundles

Hix can create more portable distributable bundles by usingnix-appimageto generate AppImage executables, exposed in the following flake apps:

nix run '.#appimage' # The default executable from the default packagenix run '.#<pkgname>.appimage' # The default executable from the specified packagenix run '.#<exename>.appimage' # The specified executable from whatever package defines itnix run '.#env.<envname>.[<pkg/exe>.]appimage' # The same as above, but from the specified env

This will print a store path:

>>> AppImage bundle for <exename> created at:/nix/store/bxbcp9mk9rf0sjg88hxsjqzpql5is280-<exename>-0.1.0.0-x86_64.AppImage

Automatic dependency management

Hix provides some functionality for adapting and verifying dependency bounds.

Note

This feature is new, so there are likely many edge cases that have not been tested.

Upper bounds and latest versions

If the optionmanaged.enable is enabled, the flake will expose an environment namedlatestand an app namedbump.

Running this app withnix run .#bump will fetch the newest versions of all dependencies and create a file in theproject at the path configured bymanaged.file, containing updated dependency version rangesthat include the new version.

For each dependency, this app will build all of the project’s packages and omit the version update if the build fails.When generating cabal files or derivations, the version ranges from this file will override those from the flake.

Additionally, the app will add overrides to the same file that select the newest version for the environmentlatest,so that thedev environment (and all others) will still use the default versions from nixpkgs (plus regularoverrides), making thelatest environment the testing ground for bleeding-edge dependency versions.

You can change this behavior to apply to other environments by settingmanaged totrue and runningnix run .#env.<name>.bump instead.

Ifmanaged.check istrue (the default), the derivations with latest versions will be addedto thechecks output, so your CI may depend on them.

Lower bounds

If the optionmanaged.lower.enable is enabled, the flake will expose an environment namedlower andan app namedlower.The app executes a subset of three stages based on the current state,init,optimize andstabilize, that arealso available as subcommands of this app (by running e.g.nix run .#lower optimize).The mode of operation is similar tobump, with the most crucial difference being that it manipulates the lowerbounds of dependencies.

The operational details of this app that are most important for a user are thatoptimize determines the lowestpossible bounds with which the project builds, and thatstabilize attempts to repair them after the code was changedin a way that invalidates them, e.g. by importing a name that wasn’t available in a dependency at the version in thelower bound.This allows the bounds validation to be integrated into the development process by runningnix run .#lower -- --stabilize as a pre-commit hook or a pre-release CI job.

Since older versions require boot packages from older GHCs, it is advisable to use the oldest GHC available.See the optionmanaged.lower.compiler for more information.The default is to use the first version inghcVersions.

Details

The first stage oflower is calledinit, and it attempts to determineinitial lower bounds, which consist of thelowest version in the highest major that builds successfully with the environment’s GHC.For example, if a dependency has the majors 1.4, 1.5 and 1.6, and all versions greater than 1.5.4 fail to build, theinitial lower bound will be 1.5.0.Repeatedly executing this stage will only compute initial bounds for dependencies that don’t have any yet (unless--reset is specified).

The purpose of this step is preparation for the other two apps: The initial bounds will be sufficiently recent thatthe project won’t break with these versions after changes with a high probability.The initial versions are stored in the managed state file along with the bounds.

Executing the app with a subcommand,nix run .#lower init, will only run this stage; otherwise, the next stage willdepend on the final state and the outcome ofinit.

If the specified dependencies have lower bounds configured in the flake, they will be ignored.

After the lower bounds have been initialized, or if the first stage is skipped due to all dependencies having bounds,the app will build the current state to decide what to do next.If the build succeeds, it will continue with theoptimize stage, otherwise it will runstabilize, although thisrequires the user to pass--stabilize in the command line, since it is an expensive operation with a high failurerate.

Theoptimize stage will iterate over the majors of all dependencies, building all versions in each of them until itfinds a working one, and terminating when an entire majors fails after at least one working version had been found.E.g. if a package has the majors 1.1, 1.2, 1.3, 1.4 and 1.5, and some versions in 1.3 and 1.4 build successfully, butnone in 1.3 do, then the lowest version in 1.4 will be the optimized lower bound.

Before thestabilize stage is executed, the app will first build the project with the versions that form the initiallower bounds.If that fails, it will refuse to continue, and require the user to fix the situation manually (the easiest first stepwould be to runnix run .#lower -- --reset).Otherwise, it will build the project with the initial lower versions and the optimized lower bound of the firstdependency, continuing upwards in the version sequence until a working version is found.Then the same process is performed with the next dependency, keeping the stabilized bounds of all previously processeddependencies.

Target sets

In the default mode, the managed-deps apps operate on the entirety of local packages, finding new bounds that work forall packages.This might be undesirable – some of the packages might have stricter version requirements than others, or they mightbe completely independent from each other.

For that purpose, the optionmanaged.sets may be used to specify multiple sets of packages that areprocessed independently.

The simplest variant, with the valuemanaged.sets = "each";, is to create one app and one env for each package, sothat you would runnix run .#bump.api to bump only the packageapi, with a flake check created asbump-api.

More granular sets can be specified like this:

{  main = ["core" "api" "app"];  other = ["docs" "compat"];}

Now the apps have paths likenix run .#lower.init.main, while the checks use the schemalower-main-api.

The default value forsets is"all", which processes all packages at once as described above.If your packages are dependent on each other, this might be more desirable, since it reduces the build time.

Fine tuning

This feature is incredibly complicated and suffers from vulnerabilities to myriad failure scenarios, some of whichmight be mitigated by configuration.

The app uses the Cabal solver to optimize the selection of overrides, which requires two package indexes:

  • A source package database of available packages with their versions and dependencies, which is read from Hackage.

  • An installed package index of concrete versions of packages that are considered to be bundled with the compiler,which is provided as the executable of a nixpkgsGHC with a selection of packages.

The latter of those is a very difficult mechanism that serves multiple independent purposes.

In order to make installed packages visible to Cabal, the Nix GHC has to be outfitted with a package DB, which is thesame structure used for development shells with GHCi, obtained by callingghcWithPackages and specifying thedependencies from the flake config as the working set.This results in a GHC executable that has those dependencies “built in”, meaning that you can just import them withoutany additional efforts, and the solver can query them by executingghc-pkg list.

Since we’re required to ultimately build the project with Nix, a major concern is to avoid rebuilding dependenciesthat are already available in the central cache.If a version selected by the solver matches the one present in the GHC set, we therefore don’t want to specify anoverride (to pull the version from Hackage and build it), since that version is very likely cached.

However, the GHC package set from nixpkgs always has the potential of containing broken packages, most often due toincompatible version bounds, or simply because a package fails to build with a bleeding-edge GHC (not to mention anyrequirements for custom build flags that your project might have).Even though this is a problem that the Cabal solver is intended to, uh, solve, the GHC set must be fully workingbefore starting the app, since that is how Nix works – we pass a store path to the GHC with packages to the app as aCLI option (roughly).

Now, the set of installed packages should resemble the “vanilla” Nixpkgs as closely as possible because of theaforementioned benefit of avoiding builds of cached versions, but if a broken package requires on override, parts ofthe package set will differ from the vanilla state!

To complicate matters even more, the same issue arises twice when building the project with the computed overrides –once when the app tests the build, and again when the final state has been written and the environment is built by aflake check or manual invocation.

At least for test builds and bound mismatches there’s a partial mitigation in place, since the app forces a jailbreak(removal of most bounds) of all overridden dependencies, but this only covers a tiny part of the problem space.

For more direct control, you can specify overrides in the flake config.However, since the solver is supposed to work on vanilla package sets, most of the usual override sources are ignored,so there is a special option for this purpose.In addition, a project might contain several managed environments with different requirements, so each must beconfigurable individually, but we also don’t want to be forced to specify a common override multiple times.

To achieve this, the modulesmanaged.envs,managed.latest.envs andmanaged.lower.envs allow you to specify configuration for all managed envs and all latest and lowerenvs, respectively.The optionmanaged.envs.solverOverrides is used only for the solver package set (with the usualthe section called “Override combinators” protocol).The actual build overrides can be defined atmanaged.envs.verbatim, which is equivalent tospecifying regular env-specific overrides for all managed (or latest/lower) environments individually.Note that at the moment, the config inlatest/lower completely overrides the more general one; they are notcombined.

For example, if your project depends ontype-errors, which has an insufficient upper bound on a dependency in thecurrent Nipxkgs set for the latest GHC that prevents the set from building, you might want to specify:

{  managed.latest.envs = {    solverOverrides = {jailbreak, ...}: { type-errors = jailbreak; };    verbatim.overrides = {jailbreak, ...}: { type-errors = jailbreak; };  };};

This will only use those overrides forlatest envs used by.#bump – if some overrides should be used for lowerbounds envs as well, you’d set this onmanaged.envs instead ofmanaged.latest.envs.The “verbatim” config is copied to all envs that are generated forthe section called “Target sets”, so with the setup from thatsection, this config would be equivalent to:

{  envs = {    latest-main.overrides = {jailbreak, ...}: { type-errors = jailbreak; };    latest-other.overrides = {jailbreak, ...}: { type-errors = jailbreak; };  };};

Managed dependencies options

managed.enable

Enable managed dependencies.

Type:boolean

Default:false

managed.check

Add builds with latest versions and lower bounds to the flake checks.

Type:boolean

Default:true

managed.debug

Print debug messages when managing dependencies.

Type:boolean

Default:false

managed.envs

Options for environments generated for managed dependencies.These apply to bothlatest andlower environments; the modulesmanaged.latest.envs andmanaged.lower.envs have precedence over them.

Type:submodule

Default:{ }

managed.envs.solverOverrides

Dependency overrides for the package set used only by the solver while finding newversions.Specifying these should only be necessary if the vanilla package set contains broken packages that would preventthe managed apps from starting.

Type:Haskell package override function specified in the Hix DSL

managed.envs.verbatim

Default config for environments generated for managed dependencies.These can be overriden per-environment by specifyingenvs.*.<attr> like for any other environment.

Type:unspecified value

managed.file

Relative path to the file in which dependency versions should be stored.

Type:string

Default:"ops/managed.nix"

managed.forceBounds

Concrete bounds that fully override those computed by the app when generating Cabal files.This is useful to relax the bounds of packages that cannot be managed, likebase, for example when the GHCused for the latest env isn’t the newest one because the dependencies are all broken right after release, butyou want it to build with that version anyway.

Type:attribute set of (submodule)

Default:{ }

Example:

{  base = { upper = "4.21"; };}
managed.forceBounds.<name>.lower

The lower bound, inclusive.

Type:null or string

Default:null

Example:"1.4.8"

managed.forceBounds.<name>.upper

The upper bound, exclusive.

Type:null or string

Default:null

Example:"1.7"

managed.generate

Whether to regenerate cabal files and override derivations after updating the project.

Type:boolean

Default:true

managed.gitAdd

Git-addthe managed deps after the first run.Since nix ignores untracked files in flakes, the state wouldn’t be loaded if you forgot to add the fileyourself.

Type:boolean

Default:true

managed.internal.localsInPackageDb

Whether to include local packages as source derivations in the package db used for the solver

Type:boolean

Default:false

managed.latest.compiler

The GHC version (as the attribute name inhaskell.packages) that should be used for latest versionsenvironments.The default is to use the last entry inghcVersions, orcompiler if theformer is empty.It is advisable to use the latest GHC version that you want to support, since boot libraries will fail tobuild with different GHCs.

Type:string

Default:"ghc910"

managed.latest.envs

Options for environments generated for latest versions.These default to the values inmanaged.envs.

Type:submodule

Default:{ }

managed.latest.envs.solverOverrides

Dependency overrides for the package set used only by the solver while finding newversions.Specifying these should only be necessary if the vanilla package set contains broken packages that would preventthe managed apps from starting.

Type:Haskell package override function specified in the Hix DSL

managed.latest.envs.verbatim

Default config for environments generated for managed dependencies.These can be overriden per-environment by specifyingenvs.*.<attr> like for any other environment.

Type:unspecified value

managed.latest.readFlakeBounds

Use the upper bounds from the flake for the first run.

Type:boolean

Default:false

managed.lower.enable

Enable an environment for testing lower bounds.

Type:boolean

Default:false

managed.lower.compiler

The GHC version (as the attribute name inhaskell.packages) that should be used for lower boundsenvironments.The default is to use the first entry inghcVersions, orcompiler if theformer is empty.It is advisable to use the lowest GHC version that you want to support, since boot libraries will fail tobuild with different GHCs.

Type:string

Default:"ghc94"

managed.lower.envs

Options for environments generated for lower bounds.These default to the values inmanaged.envs.

Type:submodule

Default:{ }

managed.lower.envs.solverOverrides

Dependency overrides for the package set used only by the solver while finding newversions.Specifying these should only be necessary if the vanilla package set contains broken packages that would preventthe managed apps from starting.

Type:Haskell package override function specified in the Hix DSL

managed.lower.envs.verbatim

Default config for environments generated for managed dependencies.These can be overriden per-environment by specifyingenvs.*.<attr> like for any other environment.

Type:unspecified value

managed.mergeBounds

Add the flake bounds to the managed bounds.Aside from going in the Cabal file, they are added to Cabal’s dependency solver when finding new bounds.This can be used to avoid problematic versions that have dependencies with a high tendency to break the build.The ranges defined here are intersected with the managed bounds.If you want to relax bounds, usemanaged.forceBounds.

Type:boolean

Default:false

managed.quiet

Suppress informational messages when managing dependencies.

Type:boolean

Default:false

managed.sets

Select how to group packages for processing by the managed deps tool.all for a single set,each for one set per package, and an attrset for custom grouping.

Type:one of “all”, “each” or attribute set of list of name of a package defined in config.packages

Default:"all"

Example:

{  main = ["core" "api" "app"];  other = ["docs" "compat"];}
managed.verbose

Print verbose messages when managing dependencies.

Type:boolean

Default:false

Miscellaneous tools

Show overrides

nix run .#show-overrides

Prints all environments’ overrides.

Show config

nix run .#show-config

Prints the project’s entire configuration.

Show dependency versions

nix run .#dep-versionsnix run .#env.ghc96.dep-versions

Prints all components’ dependencies and their actual versions in the dev environment, or the named environment in thesecond variant.

Access to intermediate outputs

Packages and environments are subjected to several stages of transformation in order to arrive at the final flakeoutputs from the initial configuration obtained from module options.In order to make this process more transparent and flexible, in particular for overriding outputs without having toreimplement substantial parts of Hix’s internals, intermediate data is exposed in the module argumentsproject,build, andoutputs.This means that the module that constitutes the Hix flake config can access these arguments by listing them in itsparameter set:

{  outputs = {hix, ...}: hix ({config, project, build, outputs, ...}: {    packages.core.src = ./core;    outputs.packages.custom-build = build.packages.dev.core.static.overrideAttrs (...);  });}

For now, these sets don’t have a stable API, but here is a brief overview:

  • project contains values that are closely related to the config options they are computed from, to be used by awide spectrum of consumers:

    • project.base contains the project’s base directory in the nix store, which is eitherbase ifthat’s non-null, or the directory inferred fromsrc if only the package config contains paths.

    • project.packages.*.path contains the relative path to a package, either given byrelativePathif that’s non-null, or the directory inferred fromproject.base.

  • build contains the full set of derivations for each package in each env.Its API looks roughly like this:

    {  packages = {    dev = {      core = {        package = <derivation>;        static = <derivation>;        musl = <derivation>;        cross = {          aarch64-android = <derivation>;          ...        };        release = <derivation>;        ghc = { <package set used for this package> };        cabal = { <cabal config for this package> };        expose = true;        executables = {          <name> = {            package = <derivation>;            static = <derivation>;            musl = <derivation>;            app = <flake app>;            appimage = <derivation>;          };          ...        };      };      api = {        ...      };      ...    };    ghc910 = {      ...    };    ...  };  envs = {    dev = {      # Like above, but only for the main package of the set      static = <derivation>;      musl = <derivation>;      cross = <attrs>;      release = <derivation>;      # Main executable of the main package      api = <derivation>;      # All executables of all packages, flattened      executables = <attrs>;    };    ...  };  commands = {    default = {      # All built-in and custom commands using their default env env      ghci = <derivation>;      hls = <derivation>;      ...    };    envs = {      dev = {        # All built-in and custom commands using this env        ghci = <derivation>;        hls = <derivation>;        ...      };    };  };}
  • outputs contains most of what will eventually end up in the flake outputs, keyed by output type.

All of this data is also exposed in the flake outputs, and can therefore be inspected fromnix repl:

Welcome to Nix 2.18.5. Type :? for help.nix-repl> :load-flake .Added 19 variables.nix-repl> legacyPackages.x86_64-linux.project{  # Values from `project`  base = /nix/store/ga9ifpvqzqgi6sqcfqhdvhj0qmfms8hk-source;  packages = { ... };  # The sets `build` and `outputs`, as attributes of `project` for simpler scoping  build = { ... };  outputs = { ... };  # Other internal data  config = <full config>;  ghc = <dev ghc set>;  ghc0 = <dev ghc set without overrides>;  pkgs = <dev nixpkgs>;  show-config = <see above>;}nix-repl> legacyPackages.x86_64-linux.project.build.packages.dev.hix.package«derivation /nix/store/qys3qc4kyvfx6wlsqkvjxk40dyq28gl3-hix-0.7.2.drv»nix-repl> packages.x86_64-linux.hix«derivation /nix/store/qys3qc4kyvfx6wlsqkvjxk40dyq28gl3-hix-0.7.2.drv»

[8]ページ先頭

©2009-2025 Movatter.jp