mirror of
https://codeberg.org/mhwombat/nix-book.git
synced 2026-05-13 17:11:13 +08:00
better flake style
This commit is contained in:
parent
f5d2bd2c97
commit
e9565b0878
9 changed files with 38769 additions and 30553 deletions
|
|
@ -104,6 +104,9 @@ install phases, the only commands available are those provided by the
|
|||
Nix standard environment and the external dependencies identified in the
|
||||
`inputs` section of the flake.
|
||||
|
||||
[#standard-environment]
|
||||
== The Nix standard environment
|
||||
|
||||
I've mentioned the Nix standard environment before, but I didn't explain
|
||||
what it is. The standard environment, or `stdenv`, refers to the
|
||||
functionality that is available during the build and install phases of a
|
||||
|
|
|
|||
|
|
@ -9,30 +9,32 @@
|
|||
|
||||
devShells = {
|
||||
x86_64-linux.default =
|
||||
nixpkgs.legacyPackages.x86_64-linux.mkShell {
|
||||
packages = [
|
||||
nixpkgs.legacyPackages.x86_64-linux.cowsay
|
||||
];
|
||||
}; # mkShell
|
||||
let
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = [ pkgs.cowsay ];
|
||||
};
|
||||
}; # devShells
|
||||
|
||||
packages = {
|
||||
x86_64-linux.default =
|
||||
nixpkgs.legacyPackages.x86_64-linux.stdenv.mkDerivation {
|
||||
name = "cow-hello.sh";
|
||||
src = ./.;
|
||||
unpackPhase = "true";
|
||||
buildPhase = ":";
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
cp $src/cow-hello.sh $out/bin
|
||||
chmod +x $out/bin/cow-hello.sh
|
||||
'';
|
||||
buildInputs = [
|
||||
nixpkgs.legacyPackages.x86_64-linux.cowsay
|
||||
];
|
||||
}; # mkDerivation
|
||||
let
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "cow-hello.sh";
|
||||
src = ./.;
|
||||
unpackPhase = "true";
|
||||
buildPhase = ":";
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
cp $src/cow-hello.sh $out/bin
|
||||
chmod +x $out/bin/cow-hello.sh
|
||||
'';
|
||||
buildInputs = [ pkgs.cowsay ];
|
||||
}; # mkDerivation
|
||||
}; # packages
|
||||
|
||||
}; # outputs
|
||||
|
|
|
|||
|
|
@ -9,54 +9,58 @@
|
|||
|
||||
devShells = {
|
||||
x86_64-linux.default =
|
||||
nixpkgs.legacyPackages.x86_64-linux.mkShell {
|
||||
packages = [
|
||||
nixpkgs.legacyPackages.x86_64-linux.cowsay
|
||||
];
|
||||
}; # mkShell
|
||||
let
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = [ pkgs.cowsay ];
|
||||
};
|
||||
|
||||
aarch64-linux.default =
|
||||
nixpkgs.legacyPackages.aarch64-linux.mkShell {
|
||||
packages = [
|
||||
nixpkgs.legacyPackages.aarch64-linux.cowsay
|
||||
];
|
||||
}; # mkShell
|
||||
let
|
||||
pkgs = import nixpkgs { system = "aarch64-linux"; };
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = [ pkgs.cowsay ];
|
||||
};
|
||||
}; # devShells
|
||||
|
||||
packages = {
|
||||
x86_64-linux.default =
|
||||
nixpkgs.legacyPackages.x86_64-linux.stdenv.mkDerivation {
|
||||
name = "cow-hello.sh";
|
||||
src = ./.;
|
||||
unpackPhase = "true";
|
||||
buildPhase = ":";
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
cp $src/cow-hello.sh $out/bin
|
||||
chmod +x $out/bin/cow-hello.sh
|
||||
'';
|
||||
buildInputs = [
|
||||
nixpkgs.legacyPackages.x86_64-linux.cowsay
|
||||
];
|
||||
}; # mkDerivation
|
||||
let
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "cow-hello.sh";
|
||||
src = ./.;
|
||||
unpackPhase = "true";
|
||||
buildPhase = ":";
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
cp $src/cow-hello.sh $out/bin
|
||||
chmod +x $out/bin/cow-hello.sh
|
||||
'';
|
||||
buildInputs = [ pkgs.cowsay ];
|
||||
}; # mkDerivation
|
||||
|
||||
aarch64-linux.default =
|
||||
nixpkgs.legacyPackages.aarch64-linux.stdenv.mkDerivation {
|
||||
name = "cow-hello.sh";
|
||||
src = ./.;
|
||||
unpackPhase = "true";
|
||||
buildPhase = ":";
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
cp $src/cow-hello.sh $out/bin
|
||||
chmod +x $out/bin/cow-hello.sh
|
||||
'';
|
||||
buildInputs = [
|
||||
nixpkgs.legacyPackages.aarch64-linux.cowsay
|
||||
];
|
||||
}; # mkDerivation
|
||||
let
|
||||
pkgs = import nixpkgs { system = "aarch64-linux"; };
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "cow-hello.sh";
|
||||
src = ./.;
|
||||
unpackPhase = "true";
|
||||
buildPhase = ":";
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
cp $src/cow-hello.sh $out/bin
|
||||
chmod +x $out/bin/cow-hello.sh
|
||||
'';
|
||||
buildInputs = [ pkgs.cowsay ];
|
||||
}; # mkDerivation
|
||||
}; # packages
|
||||
|
||||
}; # outputs
|
||||
|
|
|
|||
|
|
@ -7,23 +7,20 @@
|
|||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
|
||||
supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
|
||||
forAllSupportedSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
|
||||
nixpkgsFor = forAllSupportedSystems (system: import nixpkgs { inherit system; });
|
||||
forEachSystem = nixpkgs.lib.genAttrs supportedSystems;
|
||||
nixpkgsFor = forEachSystem (system: import nixpkgs { inherit system; });
|
||||
|
||||
in {
|
||||
|
||||
devShells = forAllSupportedSystems (system:
|
||||
devShells = forEachSystem (system:
|
||||
let pkgs = nixpkgsFor.${system}; in {
|
||||
default = pkgs.mkShell {
|
||||
packages = [ pkgs.cowsay ];
|
||||
};
|
||||
});
|
||||
|
||||
packages = forAllSupportedSystems (system:
|
||||
packages = forEachSystem (system:
|
||||
let pkgs = nixpkgsFor.${system}; in {
|
||||
default = pkgs.stdenv.mkDerivation {
|
||||
name = "cow-hello.sh";
|
||||
|
|
|
|||
|
|
@ -9,11 +9,12 @@
|
|||
|
||||
devShells = {
|
||||
x86_64-linux.default =
|
||||
nixpkgs.legacyPackages.x86_64-linux.mkShell {
|
||||
packages = [
|
||||
nixpkgs.legacyPackages.x86_64-linux.cowsay
|
||||
];
|
||||
}; # mkShell
|
||||
let
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
in
|
||||
pkgs.mkShell {
|
||||
packages = [ pkgs.cowsay ];
|
||||
};
|
||||
}; # devShells
|
||||
|
||||
}; # outputs
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ $ cd my-project
|
|||
$ git init
|
||||
....
|
||||
|
||||
== A simple Bash script
|
||||
|
||||
This will be a very simple development project,
|
||||
so that we can focus on how to use Nix.
|
||||
We want to package the following script.
|
||||
|
|
@ -24,7 +26,8 @@ $ cp ../cow-hello.sh .
|
|||
$# cat cow-hello.sh
|
||||
....
|
||||
|
||||
Add the script to your directory and we will test it.
|
||||
Add the script to your directory and make it executable.
|
||||
Let's test it.
|
||||
|
||||
....
|
||||
$ chmod +x cow-hello.sh
|
||||
|
|
@ -63,10 +66,35 @@ Furthermore, anyone else who wants to work on our project will be able to recrea
|
|||
the same development environment we used.
|
||||
This avoids complaints of "but it works on my machine!"
|
||||
|
||||
We can use a function from Nixpkgs to create the environment,
|
||||
but we have to configure the package set for the system architecture.
|
||||
The function `nixpkgs` takes an attribute set as input,
|
||||
and returns a configured Nix package set that we can use.
|
||||
The only attribute we need to specify at this time is the system architecture name.
|
||||
For example, the following code creates a package set for the `x86_64-linux` architecture.
|
||||
|
||||
[source,nix]
|
||||
....
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
....
|
||||
|
||||
In <<#mkShell>> we learned that
|
||||
Nix provides the function called `mkShell`, which defines a Bash environment.
|
||||
We need to specify that the environment should provide `cowsay`.
|
||||
|
||||
[source,nix]
|
||||
....
|
||||
pkgs.mkShell {
|
||||
packages = [ pkgs.cowsay ];
|
||||
};
|
||||
....
|
||||
|
||||
|
||||
////
|
||||
$ cp ../flake.nix .
|
||||
////
|
||||
|
||||
Now we're ready to write the flake.
|
||||
Create the file `flake.nix` as shown below.
|
||||
If you're not on an `x86_64-linux` system, modify the highlighted lines accordingly.
|
||||
|
||||
|
|
@ -78,7 +106,7 @@ run the following command.
|
|||
nix eval --impure --raw --expr 'builtins.currentSystem'
|
||||
====
|
||||
|
||||
[source,nix,linenums,highlight="10,12,14"]
|
||||
[source,nix,linenums,highlight="11,13"]
|
||||
.flake.nix (version 1)
|
||||
....
|
||||
$# cat flake.nix
|
||||
|
|
@ -86,29 +114,12 @@ $# cat flake.nix
|
|||
|
||||
The `description` part is just a short description of the package.
|
||||
The `inputs` section should be familiar from <<#flake-inputs>>.
|
||||
If we ignore parts of the long package names, the `outputs` section looks like this:
|
||||
So far, the `outputs` section only defines a development environment
|
||||
(we'll add to it in <<#define-package>>).
|
||||
|
||||
[source,subs=quotes]
|
||||
....
|
||||
devShells = {
|
||||
x86_64-linux.default =
|
||||
_blahblah_.mkShell {
|
||||
packages = [
|
||||
_blahblah_.cowsay
|
||||
];
|
||||
}; # mkShell
|
||||
}; # devShells
|
||||
....
|
||||
|
||||
This says, in effect, that to create a `default` shell for the `x86_64-linux` architecture,
|
||||
call the `mkShell` command and tell it you need the `cowsay` package.
|
||||
|
||||
The code is rather wordy, with all the _this.that.the.other.thing_ stuff.
|
||||
Also, the name `legacyPackages` suggests that we're not following current best practices.
|
||||
(In fact, it could in some circumstances result in duplicate instances of nixpkgs.)
|
||||
In <<#multi-arch>> we will refactor the code to eliminate some duplication, make it more readable,
|
||||
and eliminate the references to `legacyPackages`.
|
||||
For now, we will stick with the ugly, but straightforward, version.
|
||||
The repetition of `x86_64-linux` is undesirable.
|
||||
In <<#multi-arch>> we will refactor the code to eliminate some duplication and make it more readable.
|
||||
For now, we will stick with the straightforward version.
|
||||
|
||||
Let's enter the shell.
|
||||
|
||||
|
|
@ -136,18 +147,48 @@ We can see that `cowsay` is now available, and our script runs.
|
|||
$ ./cow-hello.sh # Succeeds
|
||||
....
|
||||
|
||||
[#define-package]
|
||||
== Defining the package
|
||||
|
||||
We created an appropriate development environment, and tested our script.
|
||||
Now we are ready to package it.
|
||||
Add the new lines below to `flake.nix`.
|
||||
|
||||
In <<#mkDerivation>> we learned that the function `pkgs.std.mkDerivation`
|
||||
provides a way to create a derivation by specifying the steps that need to be performed in each phase
|
||||
We specify the location of the source files.
|
||||
and use the standard unpack phase, which makes our source files available in `$src`
|
||||
during the build and installation.
|
||||
(We described the environment available during build and installation in <<#standard-environment>>.)
|
||||
We don't need to do anything in the build phase.
|
||||
In the install phase, we copy the script from `$src` to the output directory, `$out`,
|
||||
and make it executable.
|
||||
As with the development environment, we specify that `cowsay` is required.
|
||||
|
||||
[source,nix]
|
||||
....
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "cow-hello.sh";
|
||||
src = ./.;
|
||||
unpackPhase = "true";
|
||||
buildPhase = ":";
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
cp $src/cow-hello.sh $out/bin
|
||||
chmod +x $out/bin/cow-hello.sh
|
||||
'';
|
||||
buildInputs = [ pkgs.cowsay ];
|
||||
}; # mkDerivation
|
||||
....
|
||||
|
||||
To update the flake, add the new lines below to `flake.nix`.
|
||||
Again, change `x86_64-linux` if needed to match your system architecture.
|
||||
|
||||
////
|
||||
$ cp ../flake-2.nix flake.nix
|
||||
////
|
||||
|
||||
[source,nix,linenums,highlight="19..36"]
|
||||
[source,nix,linenums,highlight="20..38"]
|
||||
.flake.nix (version 2)
|
||||
....
|
||||
$# cat flake.nix
|
||||
|
|
@ -162,13 +203,15 @@ $ nix run
|
|||
If we hadn't added `cow-hello.sh` to the git repository,
|
||||
we would have an error about the file being missing.
|
||||
|
||||
[#config-nixpkgs]
|
||||
== Configuring Nixpkgs
|
||||
|
||||
[#multi-arch]
|
||||
== Supporting multiple architectures
|
||||
|
||||
Congratulations!
|
||||
Our package is popular, and people want to run it on `aarch64-linux` systems.
|
||||
So now we need to add an entry for that to `packages`.
|
||||
So now we need to add an entry to `packages`.
|
||||
Of course we want to test it on the new architecture,
|
||||
so we'll add an entry to `devShells` as well.
|
||||
|
||||
|
|
@ -176,7 +219,7 @@ so we'll add an entry to `devShells` as well.
|
|||
$ cp ../flake-3.nix flake.nix
|
||||
////
|
||||
|
||||
[source,nix,linenums,highlight="18..23,44..59"]
|
||||
[source,nix,linenums,highlight="19..25,47..63"]
|
||||
.flake.nix (version 3)
|
||||
....
|
||||
$# cat flake.nix
|
||||
|
|
@ -223,7 +266,8 @@ For the second argument, we used a function that "pretended" to generate definit
|
|||
What if we wrote a function which, given the name of the system architecture,
|
||||
would generate the development shell definition for us,
|
||||
and another function that would do the same for the package definition?
|
||||
Applying `lib.genAttrs` and the list of system architecture names would give us
|
||||
Applying `lib.genAttrs` and the list of system architecture names
|
||||
to those functions would give us
|
||||
all the definitions we need for the outputs section.
|
||||
|
||||
The following function will generate a development shell definition.
|
||||
|
|
@ -269,17 +313,21 @@ We can make the flake more readable with the following definitions.
|
|||
[source,nix]
|
||||
....
|
||||
supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
|
||||
forAllSupportedSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
forEachSystem = nixpkgs.lib.genAttrs supportedSystems;
|
||||
....
|
||||
|
||||
So `forEachSystem` is a function which takes one argument.
|
||||
That argument should be a function that, given the system name,
|
||||
generates the appropriate definition for that system,
|
||||
Now let's examine the definition of `nixpkgsFor.${system}`.
|
||||
|
||||
[source,nix]
|
||||
....
|
||||
nixpkgsFor = forAllSupportedSystems (system: import nixpkgs { inherit system; });
|
||||
nixpkgsFor = forEachSystem (system: import nixpkgs { inherit system; });
|
||||
....
|
||||
|
||||
Here we're using `forEachSystem` to access the appropriate nixpkgs for a system.
|
||||
|
||||
Putting everything together, we have a shiny new flake.
|
||||
You may want to compare it carefully to the original version,
|
||||
in order to reassure yourself that the definitions are equivalent.
|
||||
|
|
@ -288,8 +336,8 @@ in order to reassure yourself that the definitions are equivalent.
|
|||
$ cp ../flake-4.nix flake.nix
|
||||
////
|
||||
|
||||
[source,nix,linenums,highlight="18..23,44..59"]
|
||||
.flake.nix (version 3)
|
||||
[source,nix,linenums,highlight="10..12,16,17,23,24"]
|
||||
.flake.nix (version 4)
|
||||
....
|
||||
$# cat flake.nix
|
||||
....
|
||||
|
|
@ -301,9 +349,65 @@ $ git commit -am "refactored the flake"
|
|||
$ nix run
|
||||
....
|
||||
|
||||
// TODO In packages, add cow-hello and set default = cow-hello. Explain why.
|
||||
== A few more improvements
|
||||
|
||||
// TODO Add an apps section and explain why.
|
||||
I took some shortcuts in the flake definitions up to this point, just to keep it simple.
|
||||
Normally a flake has both a `packages` section and an `apps` section.
|
||||
The `apps` section is where we specify executable programs.
|
||||
If there is no `apps` section, then `nix run` will default to using the package, but that's not ideal.
|
||||
|
||||
A typical flake might have multiple packages and multiple apps.
|
||||
(We could even have multiple development environments.)
|
||||
Normally we would specify a default package and a default app.
|
||||
The command `nix run ` _flakeurl_`#`_appname_ will run the app named _appname_ from the `apps` section of _flakeurl_.
|
||||
If we don't specify _appname_, the default app is run.
|
||||
|
||||
To define an app, we specify the type and the path to the executable.
|
||||
We can re-use the definition from the `packages` section as shown below.
|
||||
|
||||
[source,nix]
|
||||
....
|
||||
hello = {
|
||||
type = "app";
|
||||
program = pkgs.lib.getExe self.packages.${system}.hello;
|
||||
};
|
||||
....
|
||||
|
||||
Later we might want to add overlays or some configuration options to `nixpkgs` in our flake.
|
||||
We can include the scaffolding for it with the following change.
|
||||
|
||||
[source,nix]
|
||||
....
|
||||
nixpkgsFor = forEachSystem (system: import nixpkgs {
|
||||
inherit system;
|
||||
config = { };
|
||||
overlays = [ ];
|
||||
});
|
||||
....
|
||||
|
||||
The Nix manual has more information on
|
||||
https://nixos.org/manual/nixpkgs/stable/#sec-config-options-reference[config options]
|
||||
and
|
||||
https://nixos.org/manual/nixpkgs/stable/#chap-overlays[overlays].
|
||||
|
||||
Putting everything together, we have:
|
||||
|
||||
////
|
||||
$ cp ../flake-5.nix flake.nix
|
||||
////
|
||||
|
||||
[source,nix,linenums,highlight="17,18,31,32,46,49..57"]
|
||||
.flake.nix (version 5)
|
||||
....
|
||||
$# cat flake.nix
|
||||
....
|
||||
|
||||
Let's verify that it runs on our system.
|
||||
|
||||
....
|
||||
$ git commit -am "refactored the flake"
|
||||
$ nix run
|
||||
....
|
||||
|
||||
////
|
||||
Good adoc0 scripts clean up after themselves.
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ This attribute is considered experimental and is subject to change.
|
|||
====
|
||||
|
||||
[#mkShell]
|
||||
== `pkgs.mkShell`
|
||||
== `pkgs.mkShell` and `pkgs.mkShellNoCC`
|
||||
|
||||
The function
|
||||
https://nixos.org/manual/nixpkgs/stable/#sec-pkgs-mkShell[`pkgs.mkShell`]
|
||||
|
|
@ -167,6 +167,7 @@ along with all attributes of `stdenv.mkDerivation`.
|
|||
|`""`
|
||||
|===
|
||||
|
||||
If you don't need a C compiler, you can use `mkShellNoCC` instead.
|
||||
|
||||
[#mkDerivation]
|
||||
== `stdenv.mkDerivation`
|
||||
|
|
@ -190,7 +191,7 @@ Some of the commonly used attributes are listed below.
|
|||
|location of the source files
|
||||
|
||||
|`unpackPhase`
|
||||
|Bash command to copy/unpack the source files. If set to `"true"`
|
||||
|Bash command to copy/unpack the source files. If set to `"true"`, copies/unpacks all files in `src`, including subdirectories.
|
||||
|
||||
|`buildPhase`
|
||||
|Bash commands to build the package. If no action is required, use the no-op `":"` command.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue