More complex configurations with Nickel

nitrile.ncl is an alternative to nitrile.yml that allows to prevent duplication within the definition of your package. This approach makes use of the programming language Nickel to generate the package configuration.

When using this approach, all that is necessary is to create a nitrile.ncl file. Nitrile will call Nickel under the hood, so a nitrile.yml file is not necessary (and will be ignored if it exists).

Setting up

To use the nitrile.ncl approach, install Nickel and include it in your system path. It is also possible to provide a custom executable, or use Docker, through a global setting. Nickel is also included within the cleanlang/nitrile Docker image.

The result of nitrile.ncl should be a valid package configuration. For details on Nickel syntax and features, please refer to the Nickel User Manual.

Info

To check what kind of configuration the nitrile.ncl file would produce, run nickel export nitrile.ncl --format yaml or use the Nickel Playground.

Example to avoid duplication

Consider the following test specification in nitrile.yml:

tests:
  unit:
    script:
      - clm: {src: [tests], main: test1, target: tests/test1}
      - clm: {src: [tests], main: test2, target: tests/test2}
      - clm: {src: [tests], main: test3, target: tests/test3}
      - test-runner:
          tests:
            - ./tests/test1
            - ./tests/test2
            - ./tests/test3

In a Nickel configuration, this could be simplified as:

let
  unit_tests = fun cases => {
    script =
      std.array.map (fun c => {clm = {src = ["tests"], main = c, target = "tests/" ++ c}}) cases @
      [{ test-runner.tests = std.array.map ((++) "tests/") cases }]
  }
in
{
  # rest of the package configuration...,
  tests.unit = unit_tests ["test1", "test2", "test3"]
}

Example with command line options

Nickel specifications can also be used to customize a project’s configuration using command line options. Suppose one wants to be able to build their application in a “develop” configuration with stack tracing enabled, and in a “production” configuration without stack tracing. Using a YAML specification, the only option is to define two separate build targets, and require the user to specify which is used with --only:

build:
  production:
    script:
      - clm:
          main: app
          target: bin/app
  develop:
    script:
      - clm:
          main: app
          target: bin/app
          profiling: StackTracing

With Nickel it is possible to define a single build target in which certain values can be customized on the command line using --define. Variables defined with --define=VARIABLE[:VALUE] can be accessed in Nickel as nitrile.options.VARIABLE:

{
  nitrile | not_exported = {
    options = {
      develop | Bool | default = false
    }
  },
  # rest of the package configuration...,
  build.app = {
    script = [
      { clm = {
          main = "app",
          target = "bin/app",
          profiling = if nitrile.options.develop then "StackTracing" else null
      } }
    ]
  }
}

Nitrile can now be called with --define=develop:true to override the default setting.

Info

The root field nitrile in the Nickel configuration needs to be marked as not_exported.

Multiple packages

If you need to define multiple packages, let your Nickel configuration return an array of configurations.