diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/asserts.nix | 22 | ||||
-rw-r--r-- | lib/attrsets.nix | 2 | ||||
-rw-r--r-- | lib/default.nix | 9 | ||||
-rw-r--r-- | lib/licenses.nix | 15 | ||||
-rw-r--r-- | lib/meta.nix | 2 | ||||
-rw-r--r-- | lib/modules.nix | 12 | ||||
-rw-r--r-- | lib/options.nix | 56 | ||||
-rw-r--r-- | lib/sources.nix | 115 | ||||
-rw-r--r-- | lib/trivial.nix | 10 | ||||
-rw-r--r-- | lib/types.nix | 28 |
10 files changed, 189 insertions, 82 deletions
diff --git a/lib/asserts.nix b/lib/asserts.nix index 8a5f1fb3feb..9ae357cbc93 100644 --- a/lib/asserts.nix +++ b/lib/asserts.nix @@ -2,35 +2,33 @@ rec { - /* Print a trace message if pred is false. + /* Throw if pred is false, else return pred. Intended to be used to augment asserts with helpful error messages. Example: assertMsg false "nope" - => false - stderr> trace: nope + stderr> error: nope - assert (assertMsg ("foo" == "bar") "foo is not bar, silly"); "" - stderr> trace: foo is not bar, silly - stderr> assert failed at … + assert assertMsg ("foo" == "bar") "foo is not bar, silly"; "" + stderr> error: foo is not bar, silly Type: assertMsg :: Bool -> String -> Bool */ # TODO(Profpatsch): add tests that check stderr assertMsg = pred: msg: - if pred - then true - else builtins.trace msg false; + pred || builtins.throw msg; /* Specialized `assertMsg` for checking if val is one of the elements of a list. Useful for checking enums. Example: - let sslLibrary = "libressl" + let sslLibrary = "libressl"; in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ] - => false - stderr> trace: sslLibrary must be one of "openssl", "bearssl", but is: "libressl" + stderr> error: sslLibrary must be one of [ + stderr> "openssl" + stderr> "bearssl" + stderr> ], but is: "libressl" Type: assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool diff --git a/lib/attrsets.nix b/lib/attrsets.nix index dafdd98f498..a88947b4585 100644 --- a/lib/attrsets.nix +++ b/lib/attrsets.nix @@ -276,7 +276,7 @@ rec { /* Like `mapAttrsRecursive', but it takes an additional predicate - function that tells it whether to recursive into an attribute + function that tells it whether to recurse into an attribute set. If it returns false, `mapAttrsRecursiveCond' does not recurse, but does apply the map function. If it returns true, it does recurse, and does not apply the map function. diff --git a/lib/default.nix b/lib/default.nix index 2dfe62e82a8..3e43733ad20 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -111,8 +111,8 @@ let cleanSource sourceByRegex sourceFilesBySuffices commitIdFromGitRepo cleanSourceWith pathHasContext canCleanSource pathIsRegularFile pathIsGitRepo; - inherit (self.modules) evalModules unifyModuleSyntax - applyIfFunction mergeModules + inherit (self.modules) evalModules setDefaultModuleLocation + unifyModuleSyntax applyIfFunction mergeModules mergeModules' mergeOptionDecls evalOptionValue mergeDefinitions pushDownProperties dischargeProperties filterOverrides sortProperties fixupOptionType mkIf mkAssert mkMerge mkOverride @@ -122,8 +122,9 @@ let mkRenamedOptionModule mkMergedOptionModule mkChangedOptionModule mkAliasOptionModule mkDerivedConfig doRename; inherit (self.options) isOption mkEnableOption mkSinkUndeclaredOptions - mergeDefaultOption mergeOneOption mergeEqualOption getValues - getFiles optionAttrSetToDocList optionAttrSetToDocList' + mergeDefaultOption mergeOneOption mergeEqualOption mergeUniqueOption + getValues getFiles + optionAttrSetToDocList optionAttrSetToDocList' scrubOptionValue literalExpression literalExample literalDocBook showOption showFiles unknownModule mkOption; inherit (self.types) isType setType defaultTypeMerge defaultFunctor diff --git a/lib/licenses.nix b/lib/licenses.nix index ddd5f8daac4..b9310ef6c5b 100644 --- a/lib/licenses.nix +++ b/lib/licenses.nix @@ -67,6 +67,11 @@ in mkLicense lset) ({ free = false; }; + aom = { + fullName = "Alliance for Open Media Patent License 1.0"; + url = "https://aomedia.org/license/patent-license/"; + }; + apsl20 = { spdxId = "APSL-2.0"; fullName = "Apple Public Source License 2.0"; @@ -460,6 +465,11 @@ in mkLicense lset) ({ spdxId = "imagemagick"; }; + imlib2 = { + spdxId = "Imlib2"; + fullName = "Imlib2 License"; + }; + inria-compcert = { fullName = "INRIA Non-Commercial License Agreement for the CompCert verified compiler"; url = "https://compcert.org/doc/LICENSE.txt"; @@ -592,6 +602,11 @@ in mkLicense lset) ({ fullName = "feh License"; }; + mitAdvertising = { + spdxId = "MIT-advertising"; + fullName = "Enlightenment License (e16)"; + }; + mpl10 = { spdxId = "MPL-1.0"; fullName = "Mozilla Public License 1.0"; diff --git a/lib/meta.nix b/lib/meta.nix index bc3387646f2..5b1f7ee5ff2 100644 --- a/lib/meta.nix +++ b/lib/meta.nix @@ -78,7 +78,7 @@ rec { 2. (modern) a pattern for the platform `parsed` field. - We can inject these into a patten for the whole of a structured platform, + We can inject these into a pattern for the whole of a structured platform, and then match that. */ platformMatch = platform: elem: let diff --git a/lib/modules.nix b/lib/modules.nix index c68bbfcaa3e..f1125aca0a3 100644 --- a/lib/modules.nix +++ b/lib/modules.nix @@ -334,6 +334,10 @@ rec { in modulesPath: initialModules: args: filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args); + /* Wrap a module with a default location for reporting errors. */ + setDefaultModuleLocation = file: m: + { _file = file; imports = [ m ]; }; + /* Massage a module into canonical form, that is, a set consisting of ‘options’, ‘config’ and ‘imports’ attributes. */ unifyModuleSyntax = file: key: m: @@ -534,11 +538,9 @@ rec { correspond to the definition of 'loc' in 'opt.file'. */ mergeOptionDecls = let - packSubmodule = file: m: - { _file = file; imports = [ m ]; }; coerceOption = file: opt: - if isFunction opt then packSubmodule file opt - else packSubmodule file { options = opt; }; + if isFunction opt then setDefaultModuleLocation file opt + else setDefaultModuleLocation file { options = opt; }; in loc: opts: foldl' (res: opt: let t = res.type; @@ -568,7 +570,7 @@ rec { getSubModules = opt.options.type.getSubModules or null; submodules = - if getSubModules != null then map (packSubmodule opt._file) getSubModules ++ res.options + if getSubModules != null then map (setDefaultModuleLocation opt._file) getSubModules ++ res.options else if opt.options ? options then map (coerceOption opt._file) options' ++ res.options else res.options; in opt.options // res // diff --git a/lib/options.nix b/lib/options.nix index 53001a3113f..627aac24d2f 100644 --- a/lib/options.nix +++ b/lib/options.nix @@ -26,6 +26,7 @@ let take ; inherit (lib.attrsets) + attrByPath optionalAttrs ; inherit (lib.strings) @@ -99,6 +100,49 @@ rec { type = lib.types.bool; }; + /* Creates an Option attribute set for an option that specifies the + package a module should use for some purpose. + + Type: mkPackageOption :: pkgs -> string -> { default :: [string], example :: null | string | [string] } -> option + + The package is specified as a list of strings representing its attribute path in nixpkgs. + + Because of this, you need to pass nixpkgs itself as the first argument. + + The second argument is the name of the option, used in the description "The <name> package to use.". + + You can also pass an example value, either a literal string or a package's attribute path. + + You can omit the default path if the name of the option is also attribute path in nixpkgs. + + Example: + mkPackageOption pkgs "hello" { } + => { _type = "option"; default = «derivation /nix/store/3r2vg51hlxj3cx5vscp0vkv60bqxkaq0-hello-2.10.drv»; defaultText = { ... }; description = "The hello package to use."; type = { ... }; } + + Example: + mkPackageOption pkgs "GHC" { + default = [ "ghc" ]; + example = "pkgs.haskell.package.ghc921.ghc.withPackages (hkgs: [ hkgs.primes ])"; + } + => { _type = "option"; default = «derivation /nix/store/jxx55cxsjrf8kyh3fp2ya17q99w7541r-ghc-8.10.7.drv»; defaultText = { ... }; description = "The GHC package to use."; example = { ... }; type = { ... }; } + */ + mkPackageOption = + # Package set (a specific version of nixpkgs) + pkgs: + # Name for the package, shown in option description + name: + { default ? [ name ], example ? null }: + let default' = if !isList default then [ default ] else default; + in mkOption { + type = lib.types.package; + description = "The ${name} package to use."; + default = attrByPath default' + (throw "${concatStringsSep "." default'} cannot be found in pkgs") pkgs; + defaultText = literalExpression ("pkgs." + concatStringsSep "." default'); + ${if example != null then "example" else null} = literalExpression + (if isList example then "pkgs." + concatStringsSep "." example else example); + }; + /* This option accepts anything, but it does not produce any result. This is useful for sharing a module across different module sets @@ -128,11 +172,13 @@ rec { else if all isInt list && all (x: x == head list) list then head list else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}"; - mergeOneOption = loc: defs: - if defs == [] then abort "This case should never happen." - else if length defs != 1 then - throw "The unique option `${showOption loc}' is defined multiple times. Definition values:${showDefs defs}" - else (head defs).value; + mergeOneOption = mergeUniqueOption { message = ""; }; + + mergeUniqueOption = { message }: loc: defs: + if length defs == 1 + then (head defs).value + else assert length defs > 1; + throw "The option `${showOption loc}' is defined multiple times.\n${message}\nDefinition values:${showDefs defs}"; /* "Merge" option definitions by checking that they all have the same value. */ mergeEqualOption = loc: defs: diff --git a/lib/sources.nix b/lib/sources.nix index ae2df723521..343449d9a60 100644 --- a/lib/sources.nix +++ b/lib/sources.nix @@ -20,17 +20,26 @@ let readFile ; - # Returns the type of a path: regular (for file), symlink, or directory - pathType = p: getAttr (baseNameOf p) (readDir (dirOf p)); + /* + Returns the type of a path: regular (for file), symlink, or directory. + */ + pathType = path: getAttr (baseNameOf path) (readDir (dirOf path)); - # Returns true if the path exists and is a directory, false otherwise - pathIsDirectory = p: if pathExists p then (pathType p) == "directory" else false; + /* + Returns true if the path exists and is a directory, false otherwise. + */ + pathIsDirectory = path: if pathExists path then (pathType path) == "directory" else false; - # Returns true if the path exists and is a regular file, false otherwise - pathIsRegularFile = p: if pathExists p then (pathType p) == "regular" else false; + /* + Returns true if the path exists and is a regular file, false otherwise. + */ + pathIsRegularFile = path: if pathExists path then (pathType path) == "regular" else false; - # Bring in a path as a source, filtering out all Subversion and CVS - # directories, as well as backup files (*~). + /* + A basic filter for `cleanSourceWith` that removes + directories of version control system, backup files (*~) + and some generated files. + */ cleanSourceFilter = name: type: let baseName = baseNameOf (toString name); in ! ( # Filter out version control software files/directories (baseName == ".git" || type == "directory" && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg")) || @@ -48,43 +57,48 @@ let (type == "unknown") ); - # Filters a source tree removing version control files and directories using cleanSourceWith - # - # Example: - # cleanSource ./. + /* + Filters a source tree removing version control files and directories using cleanSourceFilter. + + Example: + cleanSource ./. + */ cleanSource = src: cleanSourceWith { filter = cleanSourceFilter; inherit src; }; - # Like `builtins.filterSource`, except it will compose with itself, - # allowing you to chain multiple calls together without any - # intermediate copies being put in the nix store. - # - # lib.cleanSourceWith { - # filter = f; - # src = lib.cleanSourceWith { - # filter = g; - # src = ./.; - # }; - # } - # # Succeeds! - # - # builtins.filterSource f (builtins.filterSource g ./.) - # # Fails! - # - # Parameters: - # - # src: A path or cleanSourceWith result to filter and/or rename. - # - # filter: A function (path -> type -> bool) - # Optional with default value: constant true (include everything) - # The function will be combined with the && operator such - # that src.filter is called lazily. - # For implementing a filter, see - # https://nixos.org/nix/manual/#builtin-filterSource - # - # name: Optional name to use as part of the store path. - # This defaults to `src.name` or otherwise `"source"`. - # - cleanSourceWith = { filter ? _path: _type: true, src, name ? null }: + /* + Like `builtins.filterSource`, except it will compose with itself, + allowing you to chain multiple calls together without any + intermediate copies being put in the nix store. + + Example: + lib.cleanSourceWith { + filter = f; + src = lib.cleanSourceWith { + filter = g; + src = ./.; + }; + } + # Succeeds! + + builtins.filterSource f (builtins.filterSource g ./.) + # Fails! + + */ + cleanSourceWith = + { + # A path or cleanSourceWith result to filter and/or rename. + src, + # Optional with default value: constant true (include everything) + # The function will be combined with the && operator such + # that src.filter is called lazily. + # For implementing a filter, see + # https://nixos.org/nix/manual/#builtin-filterSource + # Type: A function (path -> type -> bool) + filter ? _path: _type: true, + # Optional name to use as part of the store path. + # This defaults to `src.name` or otherwise `"source"`. + name ? null + }: let orig = toSourceAttributes src; in fromSourceAttributes { @@ -116,9 +130,11 @@ let satisfiesSubpathInvariant = src ? satisfiesSubpathInvariant && src.satisfiesSubpathInvariant; }; - # Filter sources by a list of regular expressions. - # - # E.g. `src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"]` + /* + Filter sources by a list of regular expressions. + + Example: src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"] + */ sourceByRegex = src: regexes: let isFiltered = src ? _isLibCleanSourceWith; @@ -153,8 +169,11 @@ let pathIsGitRepo = path: (tryEval (commitIdFromGitRepo path)).success; - # Get the commit id of a git repo - # Example: commitIdFromGitRepo <nixpkgs/.git> + /* + Get the commit id of a git repo. + + Example: commitIdFromGitRepo <nixpkgs/.git> + */ commitIdFromGitRepo = let readCommitFromFile = file: path: let fileName = toString path + "/" + file; diff --git a/lib/trivial.nix b/lib/trivial.nix index 575aaf6a7ad..c68bac902e9 100644 --- a/lib/trivial.nix +++ b/lib/trivial.nix @@ -61,11 +61,11 @@ rec { pipe = val: functions: let reverseApply = x: f: f x; in builtins.foldl' reverseApply val functions; - /* note please don’t add a function like `compose = flip pipe`. - This would confuse users, because the order of the functions - in the list is not clear. With pipe, it’s obvious that it - goes first-to-last. With `compose`, not so much. - */ + + # note please don’t add a function like `compose = flip pipe`. + # This would confuse users, because the order of the functions + # in the list is not clear. With pipe, it’s obvious that it + # goes first-to-last. With `compose`, not so much. ## Named versions corresponding to some builtin operators. diff --git a/lib/types.nix b/lib/types.nix index 244cbb6b535..f2f9b2bca98 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -32,7 +32,6 @@ let last length tail - unique ; inherit (lib.attrsets) attrNames @@ -48,6 +47,7 @@ let mergeDefaultOption mergeEqualOption mergeOneOption + mergeUniqueOption showFiles showOption ; @@ -300,6 +300,19 @@ rec { inherit (str) merge; }; + # Allow a newline character at the end and trim it in the merge function. + singleLineStr = + let + inherit (strMatching "[^\n\r]*\n?") check merge; + in + mkOptionType { + name = "singleLineStr"; + description = "(optionally newline-terminated) single-line string"; + inherit check; + merge = loc: defs: + lib.removeSuffix "\n" (merge loc defs); + }; + strMatching = pattern: mkOptionType { name = "strMatching ${escapeNixString pattern}"; description = "string matching the pattern ${pattern}"; @@ -457,6 +470,18 @@ rec { nestedTypes.elemType = elemType; }; + unique = { message }: type: mkOptionType rec { + name = "unique"; + inherit (type) description check; + merge = mergeUniqueOption { inherit message; }; + emptyValue = type.emptyValue; + getSubOptions = type.getSubOptions; + getSubModules = type.getSubModules; + substSubModules = m: uniq (type.substSubModules m); + functor = (defaultFunctor name) // { wrapped = type; }; + nestedTypes.elemType = type; + }; + # Null or value of ... nullOr = elemType: mkOptionType rec { name = "nullOr"; @@ -586,6 +611,7 @@ rec { # A value from a set of allowed ones. enum = values: let + inherit (lib.lists) unique; show = v: if builtins.isString v then ''"${v}"'' else if builtins.isInt v then builtins.toString v |