Setting up a Project Skeleton
In a first iteration, we will set up a basic project that contains some Agda code, and add a Flake that...
- Describes how to install Agda and the Agda standard library.
- Has instructions for type-checking our code.
We'll call our project Playground. First, create a new directory that will contain the project, and enter it:
$ mkdir agda-playground
$ cd agda-playground
In the following, we call this directory the (project) root directory. If not stated otherwise, files are created and edited in the project root directory.
Next, we create the module structure for our project:
$ mkdir Playground
$ touch Playground/Hello.agda
Open the Agda module Playground/Hello.agda
in your preferred editor,
and enter the following code:
-- In Playground/Hello.Agda:
module Playground.Hello where
open import Agda.Builtin.String using (String)
hello : String
hello = "Hello, world"
You can re-use the shell from before and check whether Agda accepts the file:
$ nix-shell --packages 'agda' --run "agda --no-libraries Playground/Hello.agda"
Checking Playground.Hello (/tmp/agda-playground/Playground/Hello.agda).
We pass --no-libraries
to temporarily ensure that Agda treats Hello.agda
as a stand-alone module,
without any library dependencies.
In order to later specify dependencies we set up a proper Agda library,
defined in a library file.
Create playground.agda-lib
and enter the following:
-- In playground.agda-lib:
-- The name of our library:
name: playground
-- Search for modules in directory of this file
-- (i.e. the project root)
include: .
-- This library has a dependency on the Agda standard library:
depend: standard-library
-- No compiler flags (yet):
flags:
Now, create and open the file flake.nix
, and insert the following code
(explanation follows below):
# In flake.nix:
{
# 1.
description = "An Agda Library set up with Nix Flakes";
# 2.
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
# 3.
outputs = {
self,
nixpkgs,
flake-utils,
...
}: let
# 4.
inherit (flake-utils.lib) simpleFlake defaultSystems;
# 5.
name = "playground";
# 6.
overlay = final: prev: {
${name} = {
# 7.
defaultPackage = final.callPackage ./playground.nix {};
};
};
in
# 8.
simpleFlake {
inherit self nixpkgs name overlay;
systems = defaultSystems;
# 9.
shell = {pkgs}:
pkgs.mkShell {
inputsFrom = [pkgs.${name}.defaultPackage];
};
};
}
Every flake.nix
consists of three attributes:
a description, some inputs and some outputs.
Let's go over them in detail:
-
description
is a short human-readable string (duh). -
inputs
to the Flake are Nix expressions that are fetched from elsewhere. In our case, we fetch two inputs:- The
nixpkgs
package index from GitHub, checking out the branchnixpkgs-unstable
. - flake-utils, a self-contained library to help us write less boilerplate code.
- The
-
outputs
is a function from the inputs (as downloaded and stored in your system) to any number of build artifacts. We takenixpkgs
andflake-utils
as inputs (ignoreself
for now), and produce a package that contains our type-checked library and some other goodies. -
We use a
let ... in ...
-expression to give names to values that we'll use later. First, we bringsimpleFlake
anddefaultSystems
into scope, ... -
...assign a name for later use...
-
...and then create an Overlay that contains one attribute
${name}
(i.e."playground"
). Overlays arenixpkgs
's way of extending the package index with new packages or patching existing ones. -
We assign an attribute set to the key
${name}
which contains one package built from a Nix expression in the file./playground.nix
. This file contains the actual definition of our package, and we'll go over it later. -
To return the package in a format that
nix
expects, we use the library functionsimpleFlake
that puts our package together: We specify which snapshot ofnixpkgs
to use, what the name of our package is, which overlay to use (this contains our package), and which systems the package can be built for. In turn,simpleFlake
returns an output in the correct format. -
We set up a custom interactive shell that has access to all build dependencies of our package. We will discuss how to use it in a later chapter.
Now, we can run nix build
and attempt the first build of our project.
Of course that will fail, but we can learn some things from the error message:
$ nix build
warning: creating lock file '/[...]/agda-playground/flake.lock'
error: getting status of '/nix/store/[...]-source/agda-playground/playground.nix': No such file or directory
(use '--show-trace' to show detailed location information)
nix
created theflake.lock
containing snapshots of the inputs for us. It's a JSON file, so we can inspect it usingjq
:$ nix-shell -p 'jq' --run "jq '.nodes.nixpkgs.locked.rev' < flake.lock" "813836d64fa57285d108f0dbf2356457ccd304e3"
nix
cannot findplayground.nix
. This is where our package description goes.
Next, let us put together a Nix package for our Agda project in playground.nix
.