# Packaging This note covers `02-package/`, which builds a shell script into a Nix store path using `stdenv.mkDerivation`. --- ## 1. What `nix build` Does When you run `nix build` inside `02-package/`: 1. Nix evaluates `flake.nix` and produces a `.drv` file (the build recipe). 2. Nix realises the `.drv`: it runs the install phase inside a sandbox with no network and no access to files outside the declared inputs. 3. The output lands in `/nix/store/-greet-0.1.0/`. 4. A `./result` symlink is created pointing to that store path. You can then run `./result/bin/greet` directly, or use `nix run` which does step 1 through 3 and then executes the `apps..default.program` path. --- ## 2. `stdenv.mkDerivation` in Detail `stdenv.mkDerivation` is the main builder in nixpkgs. It wraps the low-level `derivation` builtin with a phased build system. The default phases, in order: 1. `unpackPhase`: extracts `src` (tarball, git checkout, or copied path). 2. `patchPhase`: applies patches from the `patches` list. 3. `configurePhase`: runs `./configure` or cmake or similar. 4. `buildPhase`: runs `make` or equivalent. 5. `installPhase`: copies outputs into `$out`. 6. `fixupPhase`: strips binaries, patches RPATHs, shrink-wraps references. For a simple script, most of these are irrelevant. The `02-package` example disables unpack and build with `dontUnpack = true` and `dontBuild = true`, then writes a custom `installPhase`. --- ## 3. Key Concepts in the Example `$out`: every derivation has at least one output. `$out` is the default output path. The build must place files under `$out` or the derivation produces nothing. `substituteInPlace`: a nixpkgs helper that does in-place string replacement. The example uses it to rewrite the shebang from `#!/usr/bin/env bash` to a store-path bash. This matters because `/usr/bin/env` may not exist on a pure NixOS system, and even where it does, it would pick up whichever `bash` happens to be on `$PATH` at runtime rather than the pinned one. `src = ./.`: copies the entire flake directory into the store before the build starts. Changes to any file in the directory change the hash, which triggers a rebuild. For real projects you'd use `lib.fileset` or `lib.cleanSource` to exclude irrelevant files (editor backups, `.git/`, etc.). `apps..default`: the `nix run` command looks for this attribute. Its `program` field must be an absolute store path to an executable. --- ## 4. The Build Sandbox By default, Nix builds run inside a sandbox: - No network access (except fixed-output derivations). - No access to the host filesystem beyond explicitly declared inputs. - No access to environment variables from the host. - A restricted `/tmp` for scratch space. This is what guarantees reproducibility: the build can only use what it declared. If a build needs to download something (a source tarball, a Go module cache), it must be a fixed-output derivation. You provide the expected hash, and Nix verifies the output matches. See `builtins.fetchurl`, `pkgs.fetchFromGitHub`, and similar. --- ## 5. Commands to Try ```bash cd 02-package nix build # build and create ./result symlink ./result/bin/greet # run directly ./result/bin/greet "nix learner" # pass an argument nix run # build + run via apps.default nix run . -- "nix learner" # pass arguments after -- nix path-info ./result # show the store path nix path-info -rS ./result # show closure size (all transitive deps) nix derivation show ./result # inspect the .drv (inputs, env, builder) ``` --- ## 6. Beyond Shell Scripts The same `stdenv.mkDerivation` pattern scales to compiled languages. nixpkgs provides wrappers: - `rustPlatform.buildRustPackage` for Rust (Cargo). - `buildGoModule` for Go. - `python3Packages.buildPythonPackage` for Python. - `mkDerivation` directly for C/C++ (autotools, cmake, meson). Each wrapper pre-configures the phases for its ecosystem. You supply `src`, a lock file hash, and metadata; the wrapper handles configure/build/install.