From ee3220440da59e39154b520353a3d03a1abf3405 Mon Sep 17 00:00:00 2001 From: Jan Malakhovski Date: Thu, 7 Dec 2017 21:26:30 +0000 Subject: [PATCH 1/7] lib: implement `compare`, `splitByAndCompare`, and `compareLists` --- lib/default.nix | 7 ++++--- lib/lists.nix | 24 ++++++++++++++++++++++++ lib/trivial.nix | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/lib/default.nix b/lib/default.nix index 97d7c10192a7..77cfa712557c 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -56,7 +56,8 @@ let replaceStrings seq stringLength sub substring tail; inherit (trivial) id const concat or and boolToString mergeAttrs flip mapNullable inNixShell min max importJSON warn info - nixpkgsVersion mod functionArgs setFunctionArgs isFunction; + nixpkgsVersion mod compare splitByAndCompare + functionArgs setFunctionArgs isFunction; inherit (fixedPoints) fix fix' extends composeExtensions makeExtensible makeExtensibleWithCustomName; @@ -71,8 +72,8 @@ let inherit (lists) singleton foldr fold foldl foldl' imap0 imap1 concatMap flatten remove findSingle findFirst any all count optional optionals toList range partition zipListsWith zipLists - reverseList listDfs toposort sort take drop sublist last init - crossLists unique intersectLists subtractLists + reverseList listDfs toposort sort compareLists take drop sublist + last init crossLists unique intersectLists subtractLists mutuallyExclusive; inherit (strings) concatStrings concatMapStrings concatImapStrings intersperse concatStringsSep concatMapStringsSep diff --git a/lib/lists.nix b/lib/lists.nix index 8f67c6bb0ca3..f7e09040a5aa 100644 --- a/lib/lists.nix +++ b/lib/lists.nix @@ -385,6 +385,30 @@ rec { if len < 2 then list else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right)); + /* Compare two lists element-by-element. + + Example: + compareLists compare [] [] + => 0 + compareLists compare [] [ "a" ] + => -1 + compareLists compare [ "a" ] [] + => 1 + compareLists compare [ "a" "b" ] [ "a" "c" ] + => 1 + */ + compareLists = cmp: a: b: + if a == [] + then if b == [] + then 0 + else -1 + else if b == [] + then 1 + else let rel = cmp (head a) (head b); in + if rel == 0 + then compareLists cmp (tail a) (tail b) + else rel; + /* Return the first (at most) N elements of a list. Example: diff --git a/lib/trivial.nix b/lib/trivial.nix index d8d51298143e..a928e1dbca98 100644 --- a/lib/trivial.nix +++ b/lib/trivial.nix @@ -81,6 +81,42 @@ rec { */ mod = base: int: base - (int * (builtins.div base int)); + /* C-style comparisons + + a < b, compare a b => -1 + a == b, compare a b => 0 + a > b, compare a b => 1 + */ + compare = a: b: + if a < b + then -1 + else if a > b + then 1 + else 0; + + /* Split type into two subtypes by predicate `p`, take all elements + of the first subtype to be less than all the elements of the + second subtype, compare elements of a single subtype with `yes` + and `no` respectively. + + Example: + + let cmp = splitByAndCompare (hasPrefix "foo") compare compare; in + + cmp "a" "z" => -1 + cmp "fooa" "fooz" => -1 + + cmp "f" "a" => 1 + cmp "fooa" "a" => -1 + # while + compare "fooa" "a" => 1 + + */ + splitByAndCompare = p: yes: no: a: b: + if p a + then if p b then yes a b else -1 + else if p b then 1 else no a b; + /* Reads a JSON file. */ importJSON = path: builtins.fromJSON (builtins.readFile path); From a7d75ab6489dc5834e8402db374b4d0f1e774d53 Mon Sep 17 00:00:00 2001 From: Jan Malakhovski Date: Wed, 20 Apr 2016 21:46:02 +0000 Subject: [PATCH 2/7] nixos/doc: push all the `enable*' and `package*` options to the top of their option group Why? Because this way configuration.nix(5) can be read linearly. Before: > virtualisation.xen.bootParams > ... > virtualisation.xen.enable > ... > virtualisation.xen.package > ... After: > virtualisation.xen.enable > virtualisation.xen.package > virtualisation.xen.bootParams > ... --- nixos/doc/manual/default.nix | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix index 8079a2feb29f..9ee70ba59b79 100644 --- a/nixos/doc/manual/default.nix +++ b/nixos/doc/manual/default.nix @@ -6,7 +6,7 @@ let lib = pkgs.lib; # Remove invisible and internal options. - optionsList = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options); + optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options); # Replace functions by the string substFunction = x: @@ -16,7 +16,7 @@ let else x; # Clean up declaration sites to not refer to the NixOS source tree. - optionsList' = lib.flip map optionsList (opt: opt // { + optionsListDesc = lib.flip map optionsListVisible (opt: opt // { declarations = map stripAnyPrefixes opt.declarations; } // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; } @@ -32,8 +32,22 @@ let prefixesToStrip = map (p: "${toString p}/") ([ ../../.. ] ++ extraSources); stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip; + # Custom "less" that pushes up all the things ending in ".enable*" + # and ".package" + optionListLess = a: b: + let + splt = lib.splitString "."; + ise = lib.hasPrefix "enable"; + isp = lib.hasPrefix "package"; + cmp = lib.splitByAndCompare ise lib.compare + (lib.splitByAndCompare isp lib.compare lib.compare); + in lib.compareLists cmp (splt a) (splt b) < 0; + + # Customly sort option list for the man page. + optionsList = lib.sort (a: b: optionListLess a.name b.name) optionsListDesc; + # Convert the list of options into an XML file. - optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList'); + optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList); optionsDocBook = runCommand "options-db.xml" {} '' optionsXML=${optionsXML} @@ -191,7 +205,7 @@ in rec { mkdir -p $dst cp ${builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON - (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList')))) + (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList)))) } $dst/options.json mkdir -p $out/nix-support From 660806066abc4a64ac44b53b2b1a20f5ab4d920b Mon Sep 17 00:00:00 2001 From: Jan Malakhovski Date: Wed, 20 Apr 2016 21:57:33 +0000 Subject: [PATCH 3/7] nixos, lib: implement relatedPackages option This allows one to specify "related packages" in NixOS that get rendered into the configuration.nix(5) man page. The interface philosophy is pretty much stolen from TeX bibliography. See the next several commits for examples. --- lib/options.nix | 9 ++++--- nixos/doc/manual/default.nix | 34 +++++++++++++++++++++++-- nixos/doc/manual/options-to-docbook.xsl | 9 +++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/lib/options.nix b/lib/options.nix index 769d3cc55723..e10c86dd5064 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -14,6 +14,7 @@ rec { , defaultText ? null # Textual representation of the default, for in the manual. , example ? null # Example value used in the manual. , description ? null # String describing the option. + , relatedPackages ? null # Related packages used in the manual (see `genRelatedPackages` in ../nixos/doc/manual/default.nix). , type ? null # Option type, providing type-checking and value merging. , apply ? null # Function that converts the option value to something else. , internal ? null # Whether the option is for NixOS developers only. @@ -76,7 +77,6 @@ rec { getValues = map (x: x.value); getFiles = map (x: x.file); - # Generate documentation template from the list of option declaration like # the set generated with filterOptionSets. optionAttrSetToDocList = optionAttrSetToDocList' []; @@ -93,9 +93,10 @@ rec { readOnly = opt.readOnly or false; type = opt.type.description or null; } - // (if opt ? example then { example = scrubOptionValue opt.example; } else {}) - // (if opt ? default then { default = scrubOptionValue opt.default; } else {}) - // (if opt ? defaultText then { default = opt.defaultText; } else {}); + // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; } + // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; } + // optionalAttrs (opt ? defaultText) { default = opt.defaultText; } + // optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; }; subOptions = let ss = opt.type.getSubOptions opt.loc; diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix index 9ee70ba59b79..66fa4f0ba43c 100644 --- a/nixos/doc/manual/default.nix +++ b/nixos/doc/manual/default.nix @@ -15,13 +15,43 @@ let else if lib.isFunction x then "" else x; - # Clean up declaration sites to not refer to the NixOS source tree. + # Generate DocBook documentation for a list of packages. This is + # what `relatedPackages` option of `mkOption` from + # ../../../lib/options.nix influences. + # + # Each element of `relatedPackages` can be either + # - a string: that will be interpreted as an attribute name from `pkgs`, + # - a list: that will be interpreted as an attribute path from `pkgs`, + # - an attrset: that can specify `name`, `path`, `package`, `comment` + # (either of `name`, `path` is required, the rest are optional). + genRelatedPackages = packages: + let + unpack = p: if lib.isString p then { name = p; } + else if lib.isList p then { path = p; } + else p; + describe = args: + let + name = args.name or (lib.concatStringsSep "." args.path); + path = args.path or [ args.name ]; + package = args.package or (lib.attrByPath path (throw "Invalid package attribute path `${toString path}'") pkgs); + in "" + + "pkgs.${name} (${package.meta.name})" + + lib.optionalString (!package.meta.evaluates) " [UNAVAILABLE]" + + ": ${package.meta.description or "???"}." + + lib.optionalString (args ? comment) "\n${args.comment}" + # Lots of `longDescription's break DocBook, so we just wrap them into + + lib.optionalString (package.meta ? longDescription) "\n${package.meta.longDescription}" + + ""; + in "${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}"; + optionsListDesc = lib.flip map optionsListVisible (opt: opt // { + # Clean up declaration sites to not refer to the NixOS source tree. declarations = map stripAnyPrefixes opt.declarations; } // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; } // lib.optionalAttrs (opt ? default) { default = substFunction opt.default; } - // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; }); + // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; } + // lib.optionalAttrs (opt ? relatedPackages) { relatedPackages = genRelatedPackages opt.relatedPackages; }); # We need to strip references to /nix/store/* from options, # including any `extraSources` if some modules came from elsewhere, diff --git a/nixos/doc/manual/options-to-docbook.xsl b/nixos/doc/manual/options-to-docbook.xsl index 5387546b5982..7b45b233ab2a 100644 --- a/nixos/doc/manual/options-to-docbook.xsl +++ b/nixos/doc/manual/options-to-docbook.xsl @@ -70,6 +70,15 @@ + + + Related packages: + + + + + Declared by: From eb38b8676a3185141c49478f1a2c6374c55f27a9 Mon Sep 17 00:00:00 2001 From: Jan Malakhovski Date: Thu, 7 Dec 2017 21:26:36 +0000 Subject: [PATCH 4/7] nixos/tmux: add related package This is a trivial example of `relatedPackages` option usage. --- nixos/modules/programs/tmux.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nixos/modules/programs/tmux.nix b/nixos/modules/programs/tmux.nix index 1eb6fa6bf2fa..4a60403a2827 100644 --- a/nixos/modules/programs/tmux.nix +++ b/nixos/modules/programs/tmux.nix @@ -61,7 +61,12 @@ in { options = { programs.tmux = { - enable = mkEnableOption "tmux - a screen replacement."; + enable = mkOption { + type = types.bool; + default = false; + description = "Whenever to configure tmux system-wide."; + relatedPackages = [ "tmux" ]; + }; aggressiveResize = mkOption { default = false; From e5268344feedd21ad1f59c43fea28be49d27e347 Mon Sep 17 00:00:00 2001 From: Jan Malakhovski Date: Mon, 5 Feb 2018 15:24:46 +0000 Subject: [PATCH 5/7] nixos/adb: add related package This is an attribute path example of `relatedPackages` option usage. --- nixos/modules/programs/adb.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/modules/programs/adb.nix b/nixos/modules/programs/adb.nix index 18290555b79d..f648d70bd9fa 100644 --- a/nixos/modules/programs/adb.nix +++ b/nixos/modules/programs/adb.nix @@ -16,6 +16,7 @@ with lib; To grant access to a user, it must be part of adbusers group: users.extraUsers.alice.extraGroups = ["adbusers"]; ''; + relatedPackages = [ ["androidenv" "platformTools"] ]; }; }; }; From 06adc17455257324ae555bb1544643ee3a42d313 Mon Sep 17 00:00:00 2001 From: Jan Malakhovski Date: Thu, 7 Dec 2017 21:26:42 +0000 Subject: [PATCH 6/7] xen, qemu: passthru the path to qemu-system-i386 --- pkgs/applications/virtualization/qemu/default.nix | 4 ++++ pkgs/applications/virtualization/xen/4.5.nix | 6 ++++++ pkgs/applications/virtualization/xen/4.8.nix | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/pkgs/applications/virtualization/qemu/default.nix b/pkgs/applications/virtualization/qemu/default.nix index 91b02f7ad1f0..68ab979ecfbe 100644 --- a/pkgs/applications/virtualization/qemu/default.nix +++ b/pkgs/applications/virtualization/qemu/default.nix @@ -101,6 +101,10 @@ stdenv.mkDerivation rec { else if stdenv.isAarch64 then ''makeWrapper $out/bin/qemu-system-aarch64 $out/bin/qemu-kvm --add-flags "\$([ -e /dev/kvm ] && echo -enable-kvm)"'' else ""; + passthru = { + qemu-system-i386 = "bin/qemu-system-i386"; + }; + meta = with stdenv.lib; { homepage = http://www.qemu.org/; description = "A generic and open source machine emulator and virtualizer"; diff --git a/pkgs/applications/virtualization/xen/4.5.nix b/pkgs/applications/virtualization/xen/4.5.nix index ec3fe9ccf221..22799a7af8e7 100644 --- a/pkgs/applications/virtualization/xen/4.5.nix +++ b/pkgs/applications/virtualization/xen/4.5.nix @@ -248,4 +248,10 @@ callPackage (import ./generic.nix (rec { -i tools/libxl/libxl_device.c ''; + passthru = { + qemu-system-i386 = if withInternalQemu + then "lib/xen/bin/qemu-system-i386" + else throw "this xen has no qemu builtin"; + }; + })) ({ ocamlPackages = ocamlPackages_4_02; } // args) diff --git a/pkgs/applications/virtualization/xen/4.8.nix b/pkgs/applications/virtualization/xen/4.8.nix index 6eedca18960b..44a52a1026af 100644 --- a/pkgs/applications/virtualization/xen/4.8.nix +++ b/pkgs/applications/virtualization/xen/4.8.nix @@ -176,4 +176,10 @@ callPackage (import ./generic.nix (rec { -i tools/libxl/libxl_device.c ''; + passthru = { + qemu-system-i386 = if withInternalQemu + then "lib/xen/bin/qemu-system-i386" + else throw "this xen has no qemu builtin"; + }; + })) args From 0d1a6432100e10b833fdb5557329f9e0818ecbfe Mon Sep 17 00:00:00 2001 From: Jan Malakhovski Date: Thu, 7 Dec 2017 21:26:49 +0000 Subject: [PATCH 7/7] nixos/xen-dom0: add related packages, make it play well with them This is a custom attribute set example of `relatedPackages` option usage. --- nixos/modules/rename.nix | 4 ++++ nixos/modules/virtualisation/xen-dom0.nix | 25 +++++++++-------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 562be13a3f64..7351482f957f 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -210,6 +210,7 @@ with lib; "Set the option `services.xserver.displayManager.sddm.package' instead.") (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "") (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "") + (mkRemovedOptionModule [ "virtualisation" "xen" "qemu" ] "You don't need this option anymore, it will work without it.") # ZSH (mkRenamedOptionModule [ "programs" "zsh" "enableSyntaxHighlighting" ] [ "programs" "zsh" "syntaxHighlighting" "enable" ]) @@ -220,5 +221,8 @@ with lib; (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "theme" ] [ "programs" "zsh" "ohMyZsh" "theme" ]) (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "custom" ] [ "programs" "zsh" "ohMyZsh" "custom" ]) (mkRenamedOptionModule [ "programs" "zsh" "oh-my-zsh" "plugins" ] [ "programs" "zsh" "ohMyZsh" "plugins" ]) + + # Xen + (mkRenamedOptionModule [ "virtualisation" "xen" "qemu-package" ] [ "virtualisation" "xen" "package-qemu" ]) ]; } diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix index c7656bc309c0..afc5a42f8b4e 100644 --- a/nixos/modules/virtualisation/xen-dom0.nix +++ b/nixos/modules/virtualisation/xen-dom0.nix @@ -35,24 +35,19 @@ in description = '' The package used for Xen binary. ''; + relatedPackages = [ "xen" "xen-light" ]; }; - virtualisation.xen.qemu = mkOption { - type = types.path; - defaultText = "\${pkgs.xen}/lib/xen/bin/qemu-system-i386"; - example = literalExample "''${pkgs.qemu_xen-light}/bin/qemu-system-i386"; - description = '' - The qemu binary to use for Dom-0 backend. - ''; - }; - - virtualisation.xen.qemu-package = mkOption { + virtualisation.xen.package-qemu = mkOption { type = types.package; defaultText = "pkgs.xen"; example = literalExample "pkgs.qemu_xen-light"; description = '' - The package with qemu binaries for xendomains. + The package with qemu binaries for dom0 qemu and xendomains. ''; + relatedPackages = [ "xen" + { name = "qemu_xen-light"; comment = "For use with pkgs.xen-light."; } + ]; }; virtualisation.xen.bootParams = @@ -158,8 +153,7 @@ in } ]; virtualisation.xen.package = mkDefault pkgs.xen; - virtualisation.xen.qemu = mkDefault "${pkgs.xen}/lib/xen/bin/qemu-system-i386"; - virtualisation.xen.qemu-package = mkDefault pkgs.xen; + virtualisation.xen.package-qemu = mkDefault pkgs.xen; virtualisation.xen.stored = mkDefault "${cfg.package}/bin/oxenstored"; environment.systemPackages = [ cfg.package ]; @@ -339,7 +333,8 @@ in after = [ "xen-console.service" ]; requires = [ "xen-store.service" ]; serviceConfig.ExecStart = '' - ${cfg.qemu} -xen-attach -xen-domid 0 -name dom0 -M xenpv \ + ${cfg.package-qemu}/${cfg.package-qemu.qemu-system-i386} \ + -xen-attach -xen-domid 0 -name dom0 -M xenpv \ -nographic -monitor /dev/null -serial /dev/null -parallel /dev/null ''; }; @@ -448,7 +443,7 @@ in before = [ "dhcpd.service" ]; restartIfChanged = false; serviceConfig.RemainAfterExit = "yes"; - path = [ cfg.package cfg.qemu-package ]; + path = [ cfg.package cfg.package-qemu ]; environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains"; preStart = "mkdir -p /var/lock/subsys -m 755"; serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start";