= Flake structure

The basic structure of a flake is shown below.

[source,nix,subs=quotes]
....
{
  description = _package description_
  inputs = _dependencies_
  outputs = _what the flake produces_
  nixConfig = _advanced configuration options_
}
....

== Description

The `description` part is self-explanatory; it's just a string. You
probably won't need `nixConfig` unless you're doing something fancy. I'm
going to focus on what goes into the `inputs` and `outputs` sections,
and highlight some of the things I found confusing when I began using flakes.

== Inputs

This section specifies the dependencies of a flake. It's an _attribute
set_; it maps keys to values.

To ensure that a build is reproducible, the build step runs in a _pure_
environment with no network access. Therefore, any external dependencies
must be specified in the "`inputs`" section so they can be fetched in
advance (before we enter the pure environment).

Each entry in this section maps an input name to a _flake reference_.
This commonly takes the following form.

....
NAME.url = URL-LIKE-EXPRESSION
....

As a first example of a flake reference, all (almost all?) flakes depend on "`nixpkgs`",
which is a large Git repository of programs and libraries that are
pre-packaged for Nix. We can write that as

[source,nix,subs=quotes]
....
nixpkgs.url = "github:NixOS/nixpkgs/nixos-__version__";
....

where _version_ is replaced with the version number that you used to build
the package, e.g. `22.11`. Information about the latest nixpkgs releases
is available at https://status.nixos.org/. You can also write the entry
without the version number

[source,nix]
....
nixpkgs.url = "github:NixOS/nixpkgs/nixos";
....

or more simply,

[source,nix]
....
nixpkgs.url = "nixpkgs";
....

You might be concerned that omitting the version number would make the
build non-reproducible. If someone else builds the flake, could they end
up with a different version of nixpkgs? No! remember that the lockfile
(`flake.lock`) _uniquely_ specifies all flake inputs.

Git and Mercurial repositories are the most common type of flake
reference, as in the examples below.

A Git repository::
  `git+https://github.com/NixOS/patchelf`
A specific branch of a Git repository::
  `git+https://github.com/NixOS/patchelf?ref=master`
A specific revision of a Git repository::
   +
  `git+https://github.com/NixOS/patchelf?ref=master&rev=f34751b88bd07d7f44f5cd3200fb4122bf916c7e`
A tarball::
  `https://github.com/NixOS/patchelf/archive/master.tar.gz`

You can find more examples of flake references in the
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#examples[Nix
Reference Manual].

[NOTE]
====
Although you probably won't need to use it, there is another syntax for
flake references that you might encounter. This example

[source,nix]
....
inputs.import-cargo = {
  type = "github";
  owner = "edolstra";
  repo = "import-cargo";
};
....

is equivalent to

[source,nix]
....
inputs.import-cargo.url = "github:edolstra/import-cargo";
....
====

Each of the `inputs` is fetched, evaluated and passed to the `outputs`
function as a set of attributes with the same name as the corresponding
input.

== Outputs

This section is a function that essentially returns the recipe for
building the flake.

We said above that `inputs` are passed to the `outputs`, so we need to
list them as parameters. This example references the `import-cargo`
dependency defined in the previous example.

[source,nix,subs=quotes]
....
outputs = { self, nixpkgs, import-cargo }: {
  #_definitions for outputs_#
};
....

So what actually goes in the highlighted section?
That depends on the programming languages your software is written in,
the build system you use, and more. There are Nix functions and tools
that can simplify much of this, and new, easier-to-use ones are released
regularly. We'll look at some of these in the next section.
