diff --git a/index.html b/index.html index 73e1d03..ada5dea 100644 --- a/index.html +++ b/index.html @@ -208,7 +208,7 @@ pre.pygments .tok-il { color: #666666 } /* Literal.Number.Integer.Long */
lib.genAttrslib.getExe and lib.getExe'lib.systems.flakeExposedpkgs.mkShellpkgs.mkShell and pkgs.mkShellNoCCstdenv.mkDerivationLast updated 2025-10-12 at 22:10:36 IST.
+Last updated 2025-10-13 at 19:56:23 IST.
lib.meta.getExe'.
pkgs.mkShellpkgs.mkShell and pkgs.mkShellNoCCThe function
pkgs.mkShell
@@ -2590,6 +2597,9 @@ along with all attributes of stdenv.mkDerivation.
If you don’t need a C compiler, you can use mkShellNoCC instead.
stdenv.mkDerivationunpackPhase
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
inputs section of the flake.
+I’ve mentioned the Nix standard environment before, but I didn’t explain
what it is. The standard environment, or stdenv, refers to the
@@ -3648,6 +3660,7 @@ manual.
This will be a very simple development project, so that we can focus on how to use Nix. @@ -3695,7 +3710,8 @@ cowsay "Hello from your fla
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.
We want to define the development environment we need for this project, so that we can recreate it at any time. @@ -3752,7 +3769,33 @@ the same development environment we used. This avoids complaints of "but it works on my machine!"
Create the file flake.nix as shown below.
+
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.
pkgs = import nixpkgs { system = "x86_64-linux"; };
+In Section 3.4, “pkgs.mkShell and pkgs.mkShellNoCC” we learned that
+Nix provides the function called mkShell, which defines a Bash environment.
+We need to specify that the environment should provide cowsay.
pkgs.mkShell {
+ packages = [ pkgs.cowsay ];
+};
+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.
{ +20 +21
{ description = "what does the cow say"; inputs = { @@ -3806,13 +3850,14 @@ run the following command. outputs = { self, nixpkgs }: { - devShells = { - x86_64-linux.default = - nixpkgs.legacyPackages.x86_64-linux.mkShell { - packages = [ - nixpkgs.legacyPackages.x86_64-linux.cowsay - ]; - }; # mkShell + devShells = { + x86_64-linux.default = + let + pkgs = import nixpkgs { system = "x86_64-linux"; }; + in + pkgs.mkShell { + packages = [ pkgs.cowsay ]; + }; }; # devShells }; # outputs @@ -3823,31 +3868,13 @@ run the following command.-The
-descriptionpart is just a short description of the package. Theinputssection should be familiar from Section 6.1, “Inputs”. -If we ignore parts of the long package names, theoutputssection looks like this:--+So far, the-devShells = { - x86_64-linux.default = - blahblah.mkShell { - packages = [ - blahblah.cowsay - ]; - }; # mkShell - }; # devShellsoutputssection only defines a development environment +(we’ll add to it in Section 9.1.3, “Defining the package”).--This says, in effect, that to create a
-defaultshell for thex86_64-linuxarchitecture, -call themkShellcommand and tell it you need thecowsaypackage.-The code is rather wordy, with all the this.that.the.other.thing stuff. -Also, the name
+legacyPackagessuggests that we’re not following current best practices. -(In fact, it could in some circumstances result in duplicate instances of nixpkgs.) -In Section 9.1.3, “Supporting multiple architectures” we will refactor the code to eliminate some duplication, make it more readable, -and eliminate the references tolegacyPackages. -For now, we will stick with the ugly, but straightforward, version.The repetition of
x86_64-linuxis undesirable. +In Section 9.1.5, “Supporting multiple architectures” 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.
@@ -3874,7 +3901,7 @@ $ nix develop warning: Git tree '/home/amy/codeberg/nix-book/source/new-flake/bash-flake/my-project' is dirty warning: creating lock file '"/home/amy/codeberg/nix-book/source/new-flake/bash-flake/my-project/flake.lock"': • Added input 'nixpkgs': - 'github:NixOS/nixpkgs/1b5c1881789eb8c86c655caeff3c918fb76fbfe6?narHash=sha256-5oeX7NvYHNslymyCmX9mLEmLp07a8ai522G8J4VrDrs%3D' (2025-10-12) + 'github:NixOS/nixpkgs/81a6b38af7d047072d87ff0f0bc75f2b563c5f36?narHash=sha256-9XWnHg2JYVLKB2wDobsLLKXZ/wpzgA6wlWm7HuAFato%3D' (2025-10-13) warning: Git tree '/home/amy/codeberg/nix-book/source/new-flake/bash-flake/my-project' is dirty
cowsay is now available, and our script runs.
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.
+Now we are ready to package it.
In Section 3.5, “stdenv.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 Section 8.1, “The Nix 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.
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.
x86_64-linux if needed to match your system architect
36
37
38
-39{ +39 +40 +41
{
description = "what does the cow say";
inputs = {
@@ -3958,30 +4018,32 @@ Again, change x86_64-linux if needed to match your system architect
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
@@ -4012,11 +4074,15 @@ we would have an error about the file being missing.
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.
devShells as well.
60
61
62
-63{ +63 +64 +65 +66 +67
{ description = "what does the cow say"; inputs = { @@ -4096,54 +4166,58 @@ so we’ll add an entry todevShellsas well. 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 @@ -4209,7 +4283,8 @@ For the second argument, we used a function that "pretended" to generate definitWhat 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.genAttrsand the list of system architecture names would give us +Applyinglib.genAttrsand the list of system architecture names +to those functions would give us all the definitions we need for the outputs section.
supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
-
-forAllSupportedSystems = nixpkgs.lib.genAttrs supportedSystems;
+forEachSystem = nixpkgs.lib.genAttrs supportedSystems;
Now let’s examine the definition of nixpkgsFor.${system}.
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}.
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.
1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
{
+ description = "what does the cow say";
+
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs";
+ };
+
+ outputs = { self, nixpkgs }:
+ let
+ supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
+ forEachSystem = nixpkgs.lib.genAttrs supportedSystems;
+ nixpkgsFor = forEachSystem (system: import nixpkgs { inherit system; });
+
+ in {
+
+ devShells = forEachSystem (system:
+ let pkgs = nixpkgsFor.${system}; in {
+ default = pkgs.mkShell {
+ packages = [ pkgs.cowsay ];
+ };
+ });
+
+ packages = forEachSystem (system:
+ let pkgs = nixpkgsFor.${system}; in {
+ default = 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
+}
+
+Let’s verify that it runs on our system.
+$ git commit -am "refactored the flake" +[master (root-commit) cdb1858] refactored the flake + 3 files changed, 70 insertions(+) + create mode 100755 cow-hello.sh + create mode 100644 flake.lock + create mode 100644 flake.nix +$ nix run + ________________________ +< Hello from your flake! > + ------------------------ + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || ||+
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.
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.
nixpkgsFor = forEachSystem (system: import nixpkgs {
+ inherit system;
+ config = { };
+ overlays = [ ];
+});
+The Nix manual has more information on +config options +and +overlays.
+Putting everything together, we have:
+ 1
2
@@ -4321,7 +4560,22 @@ in order to reassure yourself that the definitions are equivalent.
41
42
43
-44
{
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
{
description = "what does the cow say";
inputs = {
@@ -4333,23 +4587,27 @@ in order to reassure yourself that the definitions are equivalent.
supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
- forAllSupportedSystems = nixpkgs.lib.genAttrs supportedSystems;
+ forEachSystem = nixpkgs.lib.genAttrs supportedSystems;
- nixpkgsFor = forAllSupportedSystems (system: import nixpkgs { inherit system; });
-
- in {
-
- devShells = forAllSupportedSystems (system:
- let pkgs = nixpkgsFor.${system}; in {
- default = pkgs.mkShell {
- packages = [ pkgs.cowsay ];
- };
+ nixpkgsFor = forEachSystem (system: import nixpkgs {
+ inherit system;
+ config = { };
+ overlays = [ ];
});
- packages = forAllSupportedSystems (system:
+ in {
+
+ devShells = forEachSystem (system:
let pkgs = nixpkgsFor.${system}; in {
- default = pkgs.stdenv.mkDerivation {
- name = "cow-hello.sh";
+ default = pkgs.mkShell {
+ packages = [ pkgs.cowsay ];
+ };
+ });
+
+ packages = forEachSystem (system:
+ let pkgs = nixpkgsFor.${system}; in rec {
+ hello = pkgs.stdenv.mkDerivation {
+ name = "cow-hello.sh";
src = ./.;
unpackPhase = "true";
buildPhase = ":";
@@ -4361,11 +4619,22 @@ in order to reassure yourself that the definitions are equivalent.
'';
buildInputs = [ pkgs.cowsay ];
}; # mkDerivation
- }); # packages
- }; # outputs
-}
-
+ default = hello;
+ }); # packages
+
+ apps = forEachSystem (system:
+ let pkgs = nixpkgsFor.${system}; in rec {
+ hello = {
+ type = "app";
+ program = pkgs.lib.getExe self.packages.${system}.hello;
+ };
+
+ default = hello;
+ });
+ }; # outputs
+}
+$ git commit -am "refactored the flake" -[master (root-commit) a69e4f5] refactored the flake - 3 files changed, 73 insertions(+) - create mode 100755 cow-hello.sh - create mode 100644 flake.lock - create mode 100644 flake.nix +[master ba19983] refactored the flake + 1 file changed, 21 insertions(+), 3 deletions(-) $ nix run +evaluation warning: getExe: Package "cow-hello.sh" does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo "bar". ________________________ < Hello from your flake! > ------------------------ @@ -6850,7 +7117,7 @@ Hello from your flake!