From 547da9ef41f40e7814b2056d477ea43384d846a7 Mon Sep 17 00:00:00 2001 From: Matt Schreiber Date: Fri, 15 Apr 2022 09:18:24 -0400 Subject: [PATCH] Support additional format search paths via the NIXOS_GENERATORS_FORMAT_SEARCH_PATH environment variable and --format-search-path CLI option. Additionally, introduce the --show-format-search-path option, which causes nixos-generate to print the list of paths it will search for format files. --- README.md | 41 ++++++++++++++++++- nixos-generate | 107 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 137 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 45716190..bc4425bf 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ you can use the `--system` option to create images for other architectures. `nixos-generators` can be included as a `Flake` input and provides a `nixos-generate` function for building images as `Flake` outputs. This approach pins all dependencies and allows for conveniently defining multiple -output types based on one config. +output types based on one config. An example `flake.nix` demonstrating this approach is below. `vmware` or `virtualbox` images can be built from the same `configuration.nix` by running @@ -145,6 +145,45 @@ An example `flake.nix` demonstrating this approach is below. `vmware` or } ``` +## Using custom formats + +You can choose a format by telling `nixos-generate` its full path: + +```console +nixos-generate --format-path ./path/to/my-format.nix +``` + +Additionally, you can tell `nixos-generate` where to search for format files by + +* Adding `:`-separated paths to the `NIXOS_GENERATORS_FORMAT_SEARCH_PATH` + environment variable, or +* Calling `nixos-generate` with one or more `--format-search-path ` + options. + +Example: + +```console +NIXOS_GENERATORS_FORMAT_SEARCH_PATH=/path/a:/path/b nixos-generate --format-search-path /path/c --format-search-path /path/d -f my-format +``` + +The above command searches for the file `my-format.nix` in the order: + +1. `/path/d` +2. `/path/c` +3. `/path/a` +4. `/path/b` +5. The builtin `nixos-generate` format directory + +Note that: + +* `nixos-generate` does not recognize a mechanism for escaping `:` characters + in paths specified in `NIXOS_GENERATORS_FORMAT_SEARCH_PATH`; if you have + custom formats that live in a path that contains `:`, specify the path with + `--format-search-path ./path/that/contains/a:or/two:`. +* `nixos-generate` ignores empty strings in the list of format search paths + (`nixos-generate --format-search-path ''`). +* Format names cannot be empty and cannot contain `/` elements. + ### License This project is licensed under the [MIT License](LICENSE). diff --git a/nixos-generate b/nixos-generate index 40a0151c..4d499555 100755 --- a/nixos-generate +++ b/nixos-generate @@ -4,21 +4,27 @@ set -euo pipefail ## Configuration readonly libexec_dir="${0%/*}" -readonly format_dir=$libexec_dir/formats configuration=${NIXOS_CONFIG:-$libexec_dir/configuration.nix} flake_uri= flake_attr= +format= format_path= target_system= cores= run= +list_formats=false +show_format_search_path=false nix_args=( "$libexec_dir/nixos-generate.nix" ) has_outlink=false nix_build_args=() +# `printf' rather than `<<<' to avoid introducing a spurious trailing newline +mapfile -t -d : format_dirs < <(printf -- '%s' "${NIXOS_GENERATORS_FORMAT_SEARCH_PATH:-}") +format_dirs+=("$libexec_dir/formats") + ## Functions showUsage() { @@ -34,7 +40,11 @@ Options: selects the nixos configuration to build, using flake uri like "~/dotfiles#my-config" * -f, --format NAME: select one of the pre-determined formats * --format-path PATH: pass a custom format +* --format-search-path DIR: + prepend a directory to the list of directories ${0##*/} searches for format definitions * --list: list the available built-in formats +* --show-format-search-path: + list the directories ${0##*/} searches for format files * --run: runs the configuration in a VM only works for the "vm" and "vm-nogui" formats * --show-trace: show more detailed nix evaluation location information @@ -47,9 +57,68 @@ USAGE } listFormats() { - for format in "$format_dir"/*.nix; do - basename "$format" ".nix" + local -A formats + local format_dir format_file format + + for format_dir in "${format_dirs[@]}"; do + if [[ -n $format_dir ]]; then + for format_file in "$format_dir"/*.nix; do + if [[ -f "$format_file" ]]; then + format=$(basename "$format_file" ".nix") + formats["$format"]=1 + fi + done + fi + done + + for format in "${!formats[@]}"; do + printf -- '%s\n' "$format" + done | sort +} + +showFormatSearchPath() { + local -i pos=1 + local format_dir + + for format_dir in "${format_dirs[@]}"; do + if [[ -n $format_dir ]]; then + printf -- '%d. "%q"\n' "$(( pos++ ))" "$format_dir" + fi + done +} + +validateFormat() { + case "${1:-}" in + */* | '') + abort "not a valid format name: ${1:-}" + return 1 + ;; + esac +} + +findFormat() { + local format="${1?}" + shift + + validateFormat "$format" || return + + local -n ref_var="${1:-format_file}" + shift + + local format_dir maybe_format_file + + for format_dir in "${format_dirs[@]}"; do + if [[ -n $format_dir ]]; then + maybe_format_file="${format_dir}/${format}.nix" + + if [[ -f "$maybe_format_file" ]]; then + ref_var="$maybe_format_file" + return + fi + fi done + + abort "unable to locate file for format: $format" } abort() { @@ -84,27 +153,33 @@ while [[ $# -gt 0 ]]; do shift 2 ;; -f | --format) - format_path=$format_dir/$2.nix + format="$2" shift ;; --format-path) format_path=$2 shift ;; + --format-search-path) + format_dirs=("$2" "${format_dirs[@]}") + shift + ;; --help) showUsage exit ;; --list) - listFormats - exit + list_formats=true + show_format_search_path=false + ;; + --show-format-search-path) + list_formats=false + show_format_search_path=true ;; --run) run=1 # default to the VM format - if [[ -z $format_path ]]; then - format_path=$format_dir/vm.nix - fi + format="${format:-vm}" ;; --show-trace) nix_args+=(--show-trace) @@ -129,12 +204,24 @@ while [[ $# -gt 0 ]]; do shift done +if $list_formats; then + listFormats + exit +elif $show_format_search_path; then + showFormatSearchPath + exit +fi + if ! $has_outlink; then nix_build_args+=(--no-out-link) fi if [[ -z $format_path ]]; then - abort "missing format. use --help for more details" + if [[ -n $format ]] ;then + findFormat "$format" format_path + else + abort "missing format. use --help for more details" + fi fi if [[ ! -f $format_path ]]; then