diff --git a/src/alejandra/tests/cases/idioms_lib_2/in.nix b/src/alejandra/tests/cases/idioms_lib_2/in.nix new file mode 100644 index 0000000..c68bac9 --- /dev/null +++ b/src/alejandra/tests/cases/idioms_lib_2/in.nix @@ -0,0 +1,456 @@ +{ lib }: + +rec { + + ## Simple (higher order) functions + + /* The identity function + For when you need a function that does “nothing”. + + Type: id :: a -> a + */ + id = + # The value to return + x: x; + + /* The constant function + + Ignores the second argument. If called with only one argument, + constructs a function that always returns a static value. + + Type: const :: a -> b -> a + Example: + let f = const 5; in f 10 + => 5 + */ + const = + # Value to return + x: + # Value to ignore + y: x; + + /* Pipes a value through a list of functions, left to right. + + Type: pipe :: a -> [] -> + Example: + pipe 2 [ + (x: x + 2) # 2 + 2 = 4 + (x: x * 2) # 4 * 2 = 8 + ] + => 8 + + # ideal to do text transformations + pipe [ "a/b" "a/c" ] [ + + # create the cp command + (map (file: ''cp "${src}/${file}" $out\n'')) + + # concatenate all commands into one string + lib.concatStrings + + # make that string into a nix derivation + (pkgs.runCommand "copy-to-out" {}) + + ] + => + + The output type of each function has to be the input type + of the next function, and the last function returns the + final value. + */ + 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. + + ## Named versions corresponding to some builtin operators. + + /* Concatenate two lists + + Type: concat :: [a] -> [a] -> [a] + + Example: + concat [ 1 2 ] [ 3 4 ] + => [ 1 2 3 4 ] + */ + concat = x: y: x ++ y; + + /* boolean “or” */ + or = x: y: x || y; + + /* boolean “and” */ + and = x: y: x && y; + + /* bitwise “and” */ + bitAnd = builtins.bitAnd + or (import ./zip-int-bits.nix + (a: b: if a==1 && b==1 then 1 else 0)); + + /* bitwise “or” */ + bitOr = builtins.bitOr + or (import ./zip-int-bits.nix + (a: b: if a==1 || b==1 then 1 else 0)); + + /* bitwise “xor” */ + bitXor = builtins.bitXor + or (import ./zip-int-bits.nix + (a: b: if a!=b then 1 else 0)); + + /* bitwise “not” */ + bitNot = builtins.sub (-1); + + /* Convert a boolean to a string. + + This function uses the strings "true" and "false" to represent + boolean values. Calling `toString` on a bool instead returns "1" + and "" (sic!). + + Type: boolToString :: bool -> string + */ + boolToString = b: if b then "true" else "false"; + + /* Merge two attribute sets shallowly, right side trumps left + + mergeAttrs :: attrs -> attrs -> attrs + + Example: + mergeAttrs { a = 1; b = 2; } { b = 3; c = 4; } + => { a = 1; b = 3; c = 4; } + */ + mergeAttrs = + # Left attribute set + x: + # Right attribute set (higher precedence for equal keys) + y: x // y; + + /* Flip the order of the arguments of a binary function. + + Type: flip :: (a -> b -> c) -> (b -> a -> c) + + Example: + flip concat [1] [2] + => [ 2 1 ] + */ + flip = f: a: b: f b a; + + /* Apply function if the supplied argument is non-null. + + Example: + mapNullable (x: x+1) null + => null + mapNullable (x: x+1) 22 + => 23 + */ + mapNullable = + # Function to call + f: + # Argument to check for null before passing it to `f` + a: if a == null then a else f a; + + # Pull in some builtins not included elsewhere. + inherit (builtins) + pathExists readFile isBool + isInt isFloat add sub lessThan + seq deepSeq genericClosure; + + + ## nixpkgs version strings + + /* Returns the current full nixpkgs version number. */ + version = release + versionSuffix; + + /* Returns the current nixpkgs release number as string. */ + release = lib.strings.fileContents ../.version; + + /* Returns the current nixpkgs release code name. + + On each release the first letter is bumped and a new animal is chosen + starting with that new letter. + */ + codeName = "Quokka"; + + /* Returns the current nixpkgs version suffix as string. */ + versionSuffix = + let suffixFile = ../.version-suffix; + in if pathExists suffixFile + then lib.strings.fileContents suffixFile + else "pre-git"; + + /* Attempts to return the the current revision of nixpkgs and + returns the supplied default value otherwise. + + Type: revisionWithDefault :: string -> string + */ + revisionWithDefault = + # Default value to return if revision can not be determined + default: + let + revisionFile = "${toString ./..}/.git-revision"; + gitRepo = "${toString ./..}/.git"; + in if lib.pathIsGitRepo gitRepo + then lib.commitIdFromGitRepo gitRepo + else if lib.pathExists revisionFile then lib.fileContents revisionFile + else default; + + nixpkgsVersion = builtins.trace "`lib.nixpkgsVersion` is deprecated, use `lib.version` instead!" version; + + /* Determine whether the function is being called from inside a Nix + shell. + + Type: inNixShell :: bool + */ + inNixShell = builtins.getEnv "IN_NIX_SHELL" != ""; + + + ## Integer operations + + /* Return minimum of two numbers. */ + min = x: y: if x < y then x else y; + + /* Return maximum of two numbers. */ + max = x: y: if x > y then x else y; + + /* Integer modulus + + Example: + mod 11 10 + => 1 + mod 1 10 + => 1 + */ + mod = base: int: base - (int * (builtins.div base int)); + + + ## Comparisons + + /* 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. + + Type: (a -> bool) -> (a -> a -> int) -> (a -> a -> int) -> (a -> a -> int) + + 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 = + # Predicate + p: + # Comparison function if predicate holds for both values + yes: + # Comparison function if predicate holds for neither value + no: + # First value to compare + a: + # Second value to compare + 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. + + Type :: path -> any + */ + importJSON = path: + builtins.fromJSON (builtins.readFile path); + + /* Reads a TOML file. + + Type :: path -> any + */ + importTOML = path: + builtins.fromTOML (builtins.readFile path); + + ## Warnings + + # See https://github.com/NixOS/nix/issues/749. Eventually we'd like these + # to expand to Nix builtins that carry metadata so that Nix can filter out + # the INFO messages without parsing the message string. + # + # Usage: + # { + # foo = lib.warn "foo is deprecated" oldFoo; + # bar = lib.warnIf (bar == "") "Empty bar is deprecated" bar; + # } + # + # TODO: figure out a clever way to integrate location information from + # something like __unsafeGetAttrPos. + + /* + Print a warning before returning the second argument. This function behaves + like `builtins.trace`, but requires a string message and formats it as a + warning, including the `warning: ` prefix. + + To get a call stack trace and abort evaluation, set the environment variable + `NIX_ABORT_ON_WARN=true` and set the Nix options `--option pure-eval false --show-trace` + + Type: string -> a -> a + */ + warn = + if lib.elem (builtins.getEnv "NIX_ABORT_ON_WARN") ["1" "true" "yes"] + then msg: builtins.trace "warning: ${msg}" (abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors.") + else msg: builtins.trace "warning: ${msg}"; + + /* + Like warn, but only warn when the first argument is `true`. + + Type: bool -> string -> a -> a + */ + warnIf = cond: msg: if cond then warn msg else id; + + /* + Like the `assert b; e` expression, but with a custom error message and + without the semicolon. + + If true, return the identity function, `r: r`. + + If false, throw the error message. + + Calls can be juxtaposed using function application, as `(r: r) a = a`, so + `(r: r) (r: r) a = a`, and so forth. + + Type: bool -> string -> a -> a + + Example: + + throwIfNot (lib.isList overlays) "The overlays argument to nixpkgs must be a list." + lib.foldr (x: throwIfNot (lib.isFunction x) "All overlays passed to nixpkgs must be functions.") (r: r) overlays + pkgs + + */ + throwIfNot = cond: msg: if cond then x: x else throw msg; + + /* Check if the elements in a list are valid values from a enum, returning the identity function, or throwing an error message otherwise. + + Example: + let colorVariants = ["bright" "dark" "black"] + in checkListOfEnum "color variants" [ "standard" "light" "dark" ] colorVariants; + => + error: color variants: bright, black unexpected; valid ones: standard, light, dark + + Type: String -> List ComparableVal -> List ComparableVal -> a -> a + */ + checkListOfEnum = msg: valid: given: + let + unexpected = lib.subtractLists valid given; + in + lib.throwIfNot (unexpected == []) + "${msg}: ${builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected)} unexpected; valid ones: ${builtins.concatStringsSep ", " (builtins.map builtins.toString valid)}"; + + info = msg: builtins.trace "INFO: ${msg}"; + + showWarnings = warnings: res: lib.foldr (w: x: warn w x) res warnings; + + ## Function annotations + + /* Add metadata about expected function arguments to a function. + The metadata should match the format given by + builtins.functionArgs, i.e. a set from expected argument to a bool + representing whether that argument has a default or not. + setFunctionArgs : (a → b) → Map String Bool → (a → b) + + This function is necessary because you can't dynamically create a + function of the { a, b ? foo, ... }: format, but some facilities + like callPackage expect to be able to query expected arguments. + */ + setFunctionArgs = f: args: + { # TODO: Should we add call-time "type" checking like built in? + __functor = self: f; + __functionArgs = args; + }; + + /* Extract the expected function arguments from a function. + This works both with nix-native { a, b ? foo, ... }: style + functions and functions with args set with 'setFunctionArgs'. It + has the same return type and semantics as builtins.functionArgs. + setFunctionArgs : (a → b) → Map String Bool. + */ + functionArgs = f: + if f ? __functor + then f.__functionArgs or (lib.functionArgs (f.__functor f)) + else builtins.functionArgs f; + + /* Check whether something is a function or something + annotated with function args. + */ + isFunction = f: builtins.isFunction f || + (f ? __functor && isFunction (f.__functor f)); + + /* Convert the given positive integer to a string of its hexadecimal + representation. For example: + + toHexString 0 => "0" + + toHexString 16 => "10" + + toHexString 250 => "FA" + */ + toHexString = i: + let + toHexDigit = d: + if d < 10 + then toString d + else + { + "10" = "A"; + "11" = "B"; + "12" = "C"; + "13" = "D"; + "14" = "E"; + "15" = "F"; + }.${toString d}; + in + lib.concatMapStrings toHexDigit (toBaseDigits 16 i); + + /* `toBaseDigits base i` converts the positive integer i to a list of its + digits in the given base. For example: + + toBaseDigits 10 123 => [ 1 2 3 ] + + toBaseDigits 2 6 => [ 1 1 0 ] + + toBaseDigits 16 250 => [ 15 10 ] + */ + toBaseDigits = base: i: + let + go = i: + if i < base + then [i] + else + let + r = i - ((i / base) * base); + q = (i - r) / base; + in + [r] ++ go q; + in + assert (base >= 2); + assert (i >= 0); + lib.reverseList (go i); +} diff --git a/src/alejandra/tests/cases/idioms_lib_2/out.nix b/src/alejandra/tests/cases/idioms_lib_2/out.nix new file mode 100644 index 0000000..9911edf --- /dev/null +++ b/src/alejandra/tests/cases/idioms_lib_2/out.nix @@ -0,0 +1,539 @@ +{lib}: rec { + ## Simple (higher order) functions + + /* + The identity function + For when you need a function that does “nothing”. + + Type: id :: a -> a + */ + id = + # The value to return + x: x; + + /* + The constant function + + Ignores the second argument. If called with only one argument, + constructs a function that always returns a static value. + + Type: const :: a -> b -> a + Example: + let f = const 5; in f 10 + => 5 + */ + const = + # Value to return + x: + # Value to ignore + y: x; + + /* + Pipes a value through a list of functions, left to right. + + Type: pipe :: a -> [] -> + Example: + pipe 2 [ + (x: x + 2) # 2 + 2 = 4 + (x: x * 2) # 4 * 2 = 8 + ] + => 8 + + # ideal to do text transformations + pipe [ "a/b" "a/c" ] [ + + # create the cp command + (map (file: ''cp "${src}/${file}" $out\n'')) + + # concatenate all commands into one string + lib.concatStrings + + # make that string into a nix derivation + (pkgs.runCommand "copy-to-out" {}) + + ] + => + + The output type of each function has to be the input type + of the next function, and the last function returns the + final value. + */ + 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. + + ## Named versions corresponding to some builtin operators. + + /* + Concatenate two lists + + Type: concat :: [a] -> [a] -> [a] + + Example: + concat [ 1 2 ] [ 3 4 ] + => [ 1 2 3 4 ] + */ + concat = x: y: x ++ y; + + /* + boolean “or” + */ + or = x: y: x || y; + + /* + boolean “and” + */ + and = x: y: x && y; + + /* + bitwise “and” + */ + bitAnd = + builtins.bitAnd + or (import ./zip-int-bits.nix + (a: b: + if a == 1 && b == 1 + then 1 + else 0)); + + /* + bitwise “or” + */ + bitOr = + builtins.bitOr + or (import ./zip-int-bits.nix + (a: b: + if a == 1 || b == 1 + then 1 + else 0)); + + /* + bitwise “xor” + */ + bitXor = + builtins.bitXor + or (import ./zip-int-bits.nix + (a: b: + if a != b + then 1 + else 0)); + + /* + bitwise “not” + */ + bitNot = builtins.sub (-1); + + /* + Convert a boolean to a string. + + This function uses the strings "true" and "false" to represent + boolean values. Calling `toString` on a bool instead returns "1" + and "" (sic!). + + Type: boolToString :: bool -> string + */ + boolToString = b: + if b + then "true" + else "false"; + + /* + Merge two attribute sets shallowly, right side trumps left + + mergeAttrs :: attrs -> attrs -> attrs + + Example: + mergeAttrs { a = 1; b = 2; } { b = 3; c = 4; } + => { a = 1; b = 3; c = 4; } + */ + mergeAttrs = + # Left attribute set + x: + # Right attribute set (higher precedence for equal keys) + y: x // y; + + /* + Flip the order of the arguments of a binary function. + + Type: flip :: (a -> b -> c) -> (b -> a -> c) + + Example: + flip concat [1] [2] + => [ 2 1 ] + */ + flip = f: a: b: f b a; + + /* + Apply function if the supplied argument is non-null. + + Example: + mapNullable (x: x+1) null + => null + mapNullable (x: x+1) 22 + => 23 + */ + mapNullable = + # Function to call + f: + # Argument to check for null before passing it to `f` + a: + if a == null + then a + else f a; + + # Pull in some builtins not included elsewhere. + inherit + (builtins) + pathExists + readFile + isBool + isInt + isFloat + add + sub + lessThan + seq + deepSeq + genericClosure + ; + + ## nixpkgs version strings + + /* + Returns the current full nixpkgs version number. + */ + version = release + versionSuffix; + + /* + Returns the current nixpkgs release number as string. + */ + release = lib.strings.fileContents ../.version; + + /* + Returns the current nixpkgs release code name. + + On each release the first letter is bumped and a new animal is chosen + starting with that new letter. + */ + codeName = "Quokka"; + + /* + Returns the current nixpkgs version suffix as string. + */ + versionSuffix = let + suffixFile = ../.version-suffix; + in + if pathExists suffixFile + then lib.strings.fileContents suffixFile + else "pre-git"; + + /* + Attempts to return the the current revision of nixpkgs and + returns the supplied default value otherwise. + + Type: revisionWithDefault :: string -> string + */ + revisionWithDefault = + # Default value to return if revision can not be determined + default: let + revisionFile = "${toString ./..}/.git-revision"; + gitRepo = "${toString ./..}/.git"; + in + if lib.pathIsGitRepo gitRepo + then lib.commitIdFromGitRepo gitRepo + else if lib.pathExists revisionFile + then lib.fileContents revisionFile + else default; + + nixpkgsVersion = builtins.trace "`lib.nixpkgsVersion` is deprecated, use `lib.version` instead!" version; + + /* + Determine whether the function is being called from inside a Nix + shell. + + Type: inNixShell :: bool + */ + inNixShell = builtins.getEnv "IN_NIX_SHELL" != ""; + + ## Integer operations + + /* + Return minimum of two numbers. + */ + min = x: y: + if x < y + then x + else y; + + /* + Return maximum of two numbers. + */ + max = x: y: + if x > y + then x + else y; + + /* + Integer modulus + + Example: + mod 11 10 + => 1 + mod 1 10 + => 1 + */ + mod = base: int: base - (int * (builtins.div base int)); + + ## Comparisons + + /* + 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. + + Type: (a -> bool) -> (a -> a -> int) -> (a -> a -> int) -> (a -> a -> int) + + 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 = + # Predicate + p: + # Comparison function if predicate holds for both values + yes: + # Comparison function if predicate holds for neither value + no: + # First value to compare + a: + # Second value to compare + 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. + + Type :: path -> any + */ + importJSON = path: + builtins.fromJSON (builtins.readFile path); + + /* + Reads a TOML file. + + Type :: path -> any + */ + importTOML = path: + builtins.fromTOML (builtins.readFile path); + + ## Warnings + + # See https://github.com/NixOS/nix/issues/749. Eventually we'd like these + # to expand to Nix builtins that carry metadata so that Nix can filter out + # the INFO messages without parsing the message string. + # + # Usage: + # { + # foo = lib.warn "foo is deprecated" oldFoo; + # bar = lib.warnIf (bar == "") "Empty bar is deprecated" bar; + # } + # + # TODO: figure out a clever way to integrate location information from + # something like __unsafeGetAttrPos. + + /* + Print a warning before returning the second argument. This function behaves + like `builtins.trace`, but requires a string message and formats it as a + warning, including the `warning: ` prefix. + + To get a call stack trace and abort evaluation, set the environment variable + `NIX_ABORT_ON_WARN=true` and set the Nix options `--option pure-eval false --show-trace` + + Type: string -> a -> a + */ + warn = + if lib.elem (builtins.getEnv "NIX_ABORT_ON_WARN") ["1" "true" "yes"] + then msg: builtins.trace "warning: ${msg}" (abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors.") + else msg: builtins.trace "warning: ${msg}"; + + /* + Like warn, but only warn when the first argument is `true`. + + Type: bool -> string -> a -> a + */ + warnIf = cond: msg: + if cond + then warn msg + else id; + + /* + Like the `assert b; e` expression, but with a custom error message and + without the semicolon. + + If true, return the identity function, `r: r`. + + If false, throw the error message. + + Calls can be juxtaposed using function application, as `(r: r) a = a`, so + `(r: r) (r: r) a = a`, and so forth. + + Type: bool -> string -> a -> a + + Example: + + throwIfNot (lib.isList overlays) "The overlays argument to nixpkgs must be a list." + lib.foldr (x: throwIfNot (lib.isFunction x) "All overlays passed to nixpkgs must be functions.") (r: r) overlays + pkgs + + */ + throwIfNot = cond: msg: + if cond + then x: x + else throw msg; + + /* + Check if the elements in a list are valid values from a enum, returning the identity function, or throwing an error message otherwise. + + Example: + let colorVariants = ["bright" "dark" "black"] + in checkListOfEnum "color variants" [ "standard" "light" "dark" ] colorVariants; + => + error: color variants: bright, black unexpected; valid ones: standard, light, dark + + Type: String -> List ComparableVal -> List ComparableVal -> a -> a + */ + checkListOfEnum = msg: valid: given: let + unexpected = lib.subtractLists valid given; + in + lib.throwIfNot (unexpected == []) + "${msg}: ${builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected)} unexpected; valid ones: ${builtins.concatStringsSep ", " (builtins.map builtins.toString valid)}"; + + info = msg: builtins.trace "INFO: ${msg}"; + + showWarnings = warnings: res: lib.foldr (w: x: warn w x) res warnings; + + ## Function annotations + + /* + Add metadata about expected function arguments to a function. + The metadata should match the format given by + builtins.functionArgs, i.e. a set from expected argument to a bool + representing whether that argument has a default or not. + setFunctionArgs : (a → b) → Map String Bool → (a → b) + + This function is necessary because you can't dynamically create a + function of the { a, b ? foo, ... }: format, but some facilities + like callPackage expect to be able to query expected arguments. + */ + setFunctionArgs = f: args: { + # TODO: Should we add call-time "type" checking like built in? + __functor = self: f; + __functionArgs = args; + }; + + /* + Extract the expected function arguments from a function. + This works both with nix-native { a, b ? foo, ... }: style + functions and functions with args set with 'setFunctionArgs'. It + has the same return type and semantics as builtins.functionArgs. + setFunctionArgs : (a → b) → Map String Bool. + */ + functionArgs = f: + if f ? __functor + then f.__functionArgs or (lib.functionArgs (f.__functor f)) + else builtins.functionArgs f; + + /* + Check whether something is a function or something + annotated with function args. + */ + isFunction = f: + builtins.isFunction f + || (f ? __functor && isFunction (f.__functor f)); + + /* + Convert the given positive integer to a string of its hexadecimal + representation. For example: + + toHexString 0 => "0" + + toHexString 16 => "10" + + toHexString 250 => "FA" + */ + toHexString = i: let + toHexDigit = d: + if d < 10 + then toString d + else + { + "10" = "A"; + "11" = "B"; + "12" = "C"; + "13" = "D"; + "14" = "E"; + "15" = "F"; + } + .${toString d}; + in + lib.concatMapStrings toHexDigit (toBaseDigits 16 i); + + /* + `toBaseDigits base i` converts the positive integer i to a list of its + digits in the given base. For example: + + toBaseDigits 10 123 => [ 1 2 3 ] + + toBaseDigits 2 6 => [ 1 1 0 ] + + toBaseDigits 16 250 => [ 15 10 ] + */ + toBaseDigits = base: i: let + go = i: + if i < base + then [i] + else let + r = i - ((i / base) * base); + q = (i - r) / base; + in + [r] ++ go q; + in + assert (base >= 2); + assert (i >= 0); + lib.reverseList (go i); +} diff --git a/src/alejandra/tests/cases/idioms_nixos_1/in.nix b/src/alejandra/tests/cases/idioms_nixos_1/in.nix new file mode 100644 index 0000000..d147155 --- /dev/null +++ b/src/alejandra/tests/cases/idioms_nixos_1/in.nix @@ -0,0 +1,350 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (config.boot) kernelPatches; + inherit (config.boot.kernel) features randstructSeed; + inherit (config.boot.kernelPackages) kernel; + + kernelModulesConf = pkgs.writeText "nixos.conf" + '' + ${concatStringsSep "\n" config.boot.kernelModules} + ''; + +in + +{ + + ###### interface + + options = { + + boot.kernel.features = mkOption { + default = {}; + example = literalExpression "{ debug = true; }"; + internal = true; + description = '' + This option allows to enable or disable certain kernel features. + It's not API, because it's about kernel feature sets, that + make sense for specific use cases. Mostly along with programs, + which would have separate nixos options. + `grep features pkgs/os-specific/linux/kernel/common-config.nix` + ''; + }; + + boot.kernelPackages = mkOption { + default = pkgs.linuxPackages; + type = types.unspecified // { merge = mergeEqualOption; }; + apply = kernelPackages: kernelPackages.extend (self: super: { + kernel = super.kernel.override (originalArgs: { + inherit randstructSeed; + kernelPatches = (originalArgs.kernelPatches or []) ++ kernelPatches; + features = lib.recursiveUpdate super.kernel.features features; + }); + }); + # We don't want to evaluate all of linuxPackages for the manual + # - some of it might not even evaluate correctly. + defaultText = literalExpression "pkgs.linuxPackages"; + example = literalExpression "pkgs.linuxKernel.packages.linux_5_10"; + description = '' + This option allows you to override the Linux kernel used by + NixOS. Since things like external kernel module packages are + tied to the kernel you're using, it also overrides those. + This option is a function that takes Nixpkgs as an argument + (as a convenience), and returns an attribute set containing at + the very least an attribute kernel. + Additional attributes may be needed depending on your + configuration. For instance, if you use the NVIDIA X driver, + then it also needs to contain an attribute + nvidia_x11. + ''; + }; + + boot.kernelPatches = mkOption { + type = types.listOf types.attrs; + default = []; + example = literalExpression "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]"; + description = "A list of additional patches to apply to the kernel."; + }; + + boot.kernel.randstructSeed = mkOption { + type = types.str; + default = ""; + example = "my secret seed"; + description = '' + Provides a custom seed for the RANDSTRUCT security + option of the Linux kernel. Note that RANDSTRUCT is + only enabled in NixOS hardened kernels. Using a custom seed requires + building the kernel and dependent packages locally, since this + customization happens at build time. + ''; + }; + + boot.kernelParams = mkOption { + type = types.listOf (types.strMatching ''([^"[:space:]]|"[^"]*")+'' // { + name = "kernelParam"; + description = "string, with spaces inside double quotes"; + }); + default = [ ]; + description = "Parameters added to the kernel command line."; + }; + + boot.consoleLogLevel = mkOption { + type = types.int; + default = 4; + description = '' + The kernel console loglevel. All Kernel Messages with a log level smaller + than this setting will be printed to the console. + ''; + }; + + boot.vesa = mkOption { + type = types.bool; + default = false; + description = '' + (Deprecated) This option, if set, activates the VESA 800x600 video + mode on boot and disables kernel modesetting. It is equivalent to + specifying [ "vga=0x317" "nomodeset" ] in the + option. This option is + deprecated as of 2020: Xorg now works better with modesetting, and + you might want a different VESA vga setting, anyway. + ''; + }; + + boot.extraModulePackages = mkOption { + type = types.listOf types.package; + default = []; + example = literalExpression "[ config.boot.kernelPackages.nvidia_x11 ]"; + description = "A list of additional packages supplying kernel modules."; + }; + + boot.kernelModules = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The set of kernel modules to be loaded in the second stage of + the boot process. Note that modules that are needed to + mount the root file system should be added to + or + . + ''; + }; + + boot.initrd.availableKernelModules = mkOption { + type = types.listOf types.str; + default = []; + example = [ "sata_nv" "ext3" ]; + description = '' + The set of kernel modules in the initial ramdisk used during the + boot process. This set must include all modules necessary for + mounting the root device. That is, it should include modules + for the physical device (e.g., SCSI drivers) and for the file + system (e.g., ext3). The set specified here is automatically + closed under the module dependency relation, i.e., all + dependencies of the modules list here are included + automatically. The modules listed here are available in the + initrd, but are only loaded on demand (e.g., the ext3 module is + loaded automatically when an ext3 filesystem is mounted, and + modules for PCI devices are loaded when they match the PCI ID + of a device in your system). To force a module to be loaded, + include it in . + ''; + }; + + boot.initrd.kernelModules = mkOption { + type = types.listOf types.str; + default = []; + description = "List of modules that are always loaded by the initrd."; + }; + + boot.initrd.includeDefaultModules = mkOption { + type = types.bool; + default = true; + description = '' + This option, if set, adds a collection of default kernel modules + to and + . + ''; + }; + + system.modulesTree = mkOption { + type = types.listOf types.path; + internal = true; + default = []; + description = '' + Tree of kernel modules. This includes the kernel, plus modules + built outside of the kernel. Combine these into a single tree of + symlinks because modprobe only supports one directory. + ''; + # Convert the list of path to only one path. + apply = pkgs.aggregateModules; + }; + + system.requiredKernelConfig = mkOption { + default = []; + example = literalExpression '' + with config.lib.kernelConfig; [ + (isYes "MODULES") + (isEnabled "FB_CON_DECOR") + (isEnabled "BLK_DEV_INITRD") + ] + ''; + internal = true; + type = types.listOf types.attrs; + description = '' + This option allows modules to specify the kernel config options that + must be set (or unset) for the module to work. Please use the + lib.kernelConfig functions to build list elements. + ''; + }; + + }; + + + ###### implementation + + config = mkMerge + [ (mkIf config.boot.initrd.enable { + boot.initrd.availableKernelModules = + optionals config.boot.initrd.includeDefaultModules ([ + # Note: most of these (especially the SATA/PATA modules) + # shouldn't be included by default since nixos-generate-config + # detects them, but I'm keeping them for now for backwards + # compatibility. + + # Some SATA/PATA stuff. + "ahci" + "sata_nv" + "sata_via" + "sata_sis" + "sata_uli" + "ata_piix" + "pata_marvell" + + # Standard SCSI stuff. + "sd_mod" + "sr_mod" + + # SD cards and internal eMMC drives. + "mmc_block" + + # Support USB keyboards, in case the boot fails and we only have + # a USB keyboard, or for LUKS passphrase prompt. + "uhci_hcd" + "ehci_hcd" + "ehci_pci" + "ohci_hcd" + "ohci_pci" + "xhci_hcd" + "xhci_pci" + "usbhid" + "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" + "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft" + + ] ++ optionals pkgs.stdenv.hostPlatform.isx86 [ + # Misc. x86 keyboard stuff. + "pcips2" "atkbd" "i8042" + + # x86 RTC needed by the stage 2 init script. + "rtc_cmos" + ]); + + boot.initrd.kernelModules = + optionals config.boot.initrd.includeDefaultModules [ + # For LVM. + "dm_mod" + ]; + }) + + (mkIf (!config.boot.isContainer) { + system.build = { inherit kernel; }; + + system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages; + + # Implement consoleLogLevel both in early boot and using sysctl + # (so you don't need to reboot to have changes take effect). + boot.kernelParams = + [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++ + optionals config.boot.vesa [ "vga=0x317" "nomodeset" ]; + + boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel; + + boot.kernelModules = [ "loop" "atkbd" ]; + + # The Linux kernel >= 2.6.27 provides firmware. + hardware.firmware = [ kernel ]; + + # Create /etc/modules-load.d/nixos.conf, which is read by + # systemd-modules-load.service to load required kernel modules. + environment.etc = + { "modules-load.d/nixos.conf".source = kernelModulesConf; + }; + + systemd.services.systemd-modules-load = + { wantedBy = [ "multi-user.target" ]; + restartTriggers = [ kernelModulesConf ]; + serviceConfig = + { # Ignore failed module loads. Typically some of the + # modules in ‘boot.kernelModules’ are "nice to have but + # not required" (e.g. acpi-cpufreq), so we don't want to + # barf on those. + SuccessExitStatus = "0 1"; + }; + }; + + lib.kernelConfig = { + isYes = option: { + assertion = config: config.isYes option; + message = "CONFIG_${option} is not yes!"; + configLine = "CONFIG_${option}=y"; + }; + + isNo = option: { + assertion = config: config.isNo option; + message = "CONFIG_${option} is not no!"; + configLine = "CONFIG_${option}=n"; + }; + + isModule = option: { + assertion = config: config.isModule option; + message = "CONFIG_${option} is not built as a module!"; + configLine = "CONFIG_${option}=m"; + }; + + ### Usually you will just want to use these two + # True if yes or module + isEnabled = option: { + assertion = config: config.isEnabled option; + message = "CONFIG_${option} is not enabled!"; + configLine = "CONFIG_${option}=y"; + }; + + # True if no or omitted + isDisabled = option: { + assertion = config: config.isDisabled option; + message = "CONFIG_${option} is not disabled!"; + configLine = "CONFIG_${option}=n"; + }; + }; + + # The config options that all modules can depend upon + system.requiredKernelConfig = with config.lib.kernelConfig; + [ + # !!! Should this really be needed? + (isYes "MODULES") + (isYes "BINFMT_ELF") + ] ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT")); + + # nixpkgs kernels are assumed to have all required features + assertions = if config.boot.kernelPackages.kernel ? features then [] else + let cfg = config.boot.kernelPackages.kernel.config; in map (attrs: + { assertion = attrs.assertion cfg; inherit (attrs) message; } + ) config.system.requiredKernelConfig; + + }) + + ]; + +} diff --git a/src/alejandra/tests/cases/idioms_nixos_1/out.nix b/src/alejandra/tests/cases/idioms_nixos_1/out.nix new file mode 100644 index 0000000..ae087c2 --- /dev/null +++ b/src/alejandra/tests/cases/idioms_nixos_1/out.nix @@ -0,0 +1,361 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + inherit (config.boot) kernelPatches; + inherit (config.boot.kernel) features randstructSeed; + inherit (config.boot.kernelPackages) kernel; + + kernelModulesConf = + pkgs.writeText "nixos.conf" + '' + ${concatStringsSep "\n" config.boot.kernelModules} + ''; +in { + ###### interface + + options = { + boot.kernel.features = mkOption { + default = {}; + example = literalExpression "{ debug = true; }"; + internal = true; + description = '' + This option allows to enable or disable certain kernel features. + It's not API, because it's about kernel feature sets, that + make sense for specific use cases. Mostly along with programs, + which would have separate nixos options. + `grep features pkgs/os-specific/linux/kernel/common-config.nix` + ''; + }; + + boot.kernelPackages = mkOption { + default = pkgs.linuxPackages; + type = types.unspecified // {merge = mergeEqualOption;}; + apply = kernelPackages: + kernelPackages.extend (self: super: { + kernel = super.kernel.override (originalArgs: { + inherit randstructSeed; + kernelPatches = (originalArgs.kernelPatches or []) ++ kernelPatches; + features = lib.recursiveUpdate super.kernel.features features; + }); + }); + # We don't want to evaluate all of linuxPackages for the manual + # - some of it might not even evaluate correctly. + defaultText = literalExpression "pkgs.linuxPackages"; + example = literalExpression "pkgs.linuxKernel.packages.linux_5_10"; + description = '' + This option allows you to override the Linux kernel used by + NixOS. Since things like external kernel module packages are + tied to the kernel you're using, it also overrides those. + This option is a function that takes Nixpkgs as an argument + (as a convenience), and returns an attribute set containing at + the very least an attribute kernel. + Additional attributes may be needed depending on your + configuration. For instance, if you use the NVIDIA X driver, + then it also needs to contain an attribute + nvidia_x11. + ''; + }; + + boot.kernelPatches = mkOption { + type = types.listOf types.attrs; + default = []; + example = literalExpression "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]"; + description = "A list of additional patches to apply to the kernel."; + }; + + boot.kernel.randstructSeed = mkOption { + type = types.str; + default = ""; + example = "my secret seed"; + description = '' + Provides a custom seed for the RANDSTRUCT security + option of the Linux kernel. Note that RANDSTRUCT is + only enabled in NixOS hardened kernels. Using a custom seed requires + building the kernel and dependent packages locally, since this + customization happens at build time. + ''; + }; + + boot.kernelParams = mkOption { + type = types.listOf (types.strMatching ''([^"[:space:]]|"[^"]*")+'' + // { + name = "kernelParam"; + description = "string, with spaces inside double quotes"; + }); + default = []; + description = "Parameters added to the kernel command line."; + }; + + boot.consoleLogLevel = mkOption { + type = types.int; + default = 4; + description = '' + The kernel console loglevel. All Kernel Messages with a log level smaller + than this setting will be printed to the console. + ''; + }; + + boot.vesa = mkOption { + type = types.bool; + default = false; + description = '' + (Deprecated) This option, if set, activates the VESA 800x600 video + mode on boot and disables kernel modesetting. It is equivalent to + specifying [ "vga=0x317" "nomodeset" ] in the + option. This option is + deprecated as of 2020: Xorg now works better with modesetting, and + you might want a different VESA vga setting, anyway. + ''; + }; + + boot.extraModulePackages = mkOption { + type = types.listOf types.package; + default = []; + example = literalExpression "[ config.boot.kernelPackages.nvidia_x11 ]"; + description = "A list of additional packages supplying kernel modules."; + }; + + boot.kernelModules = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The set of kernel modules to be loaded in the second stage of + the boot process. Note that modules that are needed to + mount the root file system should be added to + or + . + ''; + }; + + boot.initrd.availableKernelModules = mkOption { + type = types.listOf types.str; + default = []; + example = ["sata_nv" "ext3"]; + description = '' + The set of kernel modules in the initial ramdisk used during the + boot process. This set must include all modules necessary for + mounting the root device. That is, it should include modules + for the physical device (e.g., SCSI drivers) and for the file + system (e.g., ext3). The set specified here is automatically + closed under the module dependency relation, i.e., all + dependencies of the modules list here are included + automatically. The modules listed here are available in the + initrd, but are only loaded on demand (e.g., the ext3 module is + loaded automatically when an ext3 filesystem is mounted, and + modules for PCI devices are loaded when they match the PCI ID + of a device in your system). To force a module to be loaded, + include it in . + ''; + }; + + boot.initrd.kernelModules = mkOption { + type = types.listOf types.str; + default = []; + description = "List of modules that are always loaded by the initrd."; + }; + + boot.initrd.includeDefaultModules = mkOption { + type = types.bool; + default = true; + description = '' + This option, if set, adds a collection of default kernel modules + to and + . + ''; + }; + + system.modulesTree = mkOption { + type = types.listOf types.path; + internal = true; + default = []; + description = '' + Tree of kernel modules. This includes the kernel, plus modules + built outside of the kernel. Combine these into a single tree of + symlinks because modprobe only supports one directory. + ''; + # Convert the list of path to only one path. + apply = pkgs.aggregateModules; + }; + + system.requiredKernelConfig = mkOption { + default = []; + example = literalExpression '' + with config.lib.kernelConfig; [ + (isYes "MODULES") + (isEnabled "FB_CON_DECOR") + (isEnabled "BLK_DEV_INITRD") + ] + ''; + internal = true; + type = types.listOf types.attrs; + description = '' + This option allows modules to specify the kernel config options that + must be set (or unset) for the module to work. Please use the + lib.kernelConfig functions to build list elements. + ''; + }; + }; + + ###### implementation + + config = + mkMerge + [ + (mkIf config.boot.initrd.enable { + boot.initrd.availableKernelModules = optionals config.boot.initrd.includeDefaultModules ([ + # Note: most of these (especially the SATA/PATA modules) + # shouldn't be included by default since nixos-generate-config + # detects them, but I'm keeping them for now for backwards + # compatibility. + + # Some SATA/PATA stuff. + "ahci" + "sata_nv" + "sata_via" + "sata_sis" + "sata_uli" + "ata_piix" + "pata_marvell" + + # Standard SCSI stuff. + "sd_mod" + "sr_mod" + + # SD cards and internal eMMC drives. + "mmc_block" + + # Support USB keyboards, in case the boot fails and we only have + # a USB keyboard, or for LUKS passphrase prompt. + "uhci_hcd" + "ehci_hcd" + "ehci_pci" + "ohci_hcd" + "ohci_pci" + "xhci_hcd" + "xhci_pci" + "usbhid" + "hid_generic" + "hid_lenovo" + "hid_apple" + "hid_roccat" + "hid_logitech_hidpp" + "hid_logitech_dj" + "hid_microsoft" + ] + ++ optionals pkgs.stdenv.hostPlatform.isx86 [ + # Misc. x86 keyboard stuff. + "pcips2" + "atkbd" + "i8042" + + # x86 RTC needed by the stage 2 init script. + "rtc_cmos" + ]); + + boot.initrd.kernelModules = optionals config.boot.initrd.includeDefaultModules [ + # For LVM. + "dm_mod" + ]; + }) + + (mkIf (!config.boot.isContainer) { + system.build = {inherit kernel;}; + + system.modulesTree = [kernel] ++ config.boot.extraModulePackages; + + # Implement consoleLogLevel both in early boot and using sysctl + # (so you don't need to reboot to have changes take effect). + boot.kernelParams = + ["loglevel=${toString config.boot.consoleLogLevel}"] + ++ optionals config.boot.vesa ["vga=0x317" "nomodeset"]; + + boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel; + + boot.kernelModules = ["loop" "atkbd"]; + + # The Linux kernel >= 2.6.27 provides firmware. + hardware.firmware = [kernel]; + + # Create /etc/modules-load.d/nixos.conf, which is read by + # systemd-modules-load.service to load required kernel modules. + environment.etc = { + "modules-load.d/nixos.conf".source = kernelModulesConf; + }; + + systemd.services.systemd-modules-load = { + wantedBy = ["multi-user.target"]; + restartTriggers = [kernelModulesConf]; + serviceConfig = { + # Ignore failed module loads. Typically some of the + # modules in ‘boot.kernelModules’ are "nice to have but + # not required" (e.g. acpi-cpufreq), so we don't want to + # barf on those. + SuccessExitStatus = "0 1"; + }; + }; + + lib.kernelConfig = { + isYes = option: { + assertion = config: config.isYes option; + message = "CONFIG_${option} is not yes!"; + configLine = "CONFIG_${option}=y"; + }; + + isNo = option: { + assertion = config: config.isNo option; + message = "CONFIG_${option} is not no!"; + configLine = "CONFIG_${option}=n"; + }; + + isModule = option: { + assertion = config: config.isModule option; + message = "CONFIG_${option} is not built as a module!"; + configLine = "CONFIG_${option}=m"; + }; + + ### Usually you will just want to use these two + # True if yes or module + isEnabled = option: { + assertion = config: config.isEnabled option; + message = "CONFIG_${option} is not enabled!"; + configLine = "CONFIG_${option}=y"; + }; + + # True if no or omitted + isDisabled = option: { + assertion = config: config.isDisabled option; + message = "CONFIG_${option} is not disabled!"; + configLine = "CONFIG_${option}=n"; + }; + }; + + # The config options that all modules can depend upon + system.requiredKernelConfig = with config.lib.kernelConfig; + [ + # !!! Should this really be needed? + (isYes "MODULES") + (isYes "BINFMT_ELF") + ] + ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT")); + + # nixpkgs kernels are assumed to have all required features + assertions = + if config.boot.kernelPackages.kernel ? features + then [] + else let + cfg = config.boot.kernelPackages.kernel.config; + in + map ( + attrs: { + assertion = attrs.assertion cfg; + inherit (attrs) message; + } + ) + config.system.requiredKernelConfig; + }) + ]; +} diff --git a/src/alejandra/tests/cases/idioms_pkgs_2/in.nix b/src/alejandra/tests/cases/idioms_pkgs_2/in.nix new file mode 100644 index 0000000..ce49cfd --- /dev/null +++ b/src/alejandra/tests/cases/idioms_pkgs_2/in.nix @@ -0,0 +1,43 @@ +{ lib +, stdenv +, fetchurl +, nixos +, testVersion +, testEqualDerivation +, hello +}: + +stdenv.mkDerivation rec { + pname = "hello"; + version = "2.12"; + + src = fetchurl { + url = "mirror://gnu/hello/${pname}-${version}.tar.gz"; + sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g"; + }; + + doCheck = true; + + passthru.tests = { + version = testVersion { package = hello; }; + + invariant-under-noXlibs = + testEqualDerivation + "hello must not be rebuilt when environment.noXlibs is set." + hello + (nixos { environment.noXlibs = true; }).pkgs.hello; + }; + + meta = with lib; { + description = "A program that produces a familiar, friendly greeting"; + longDescription = '' + GNU Hello is a program that prints "Hello, world!" when you run it. + It is fully customizable. + ''; + homepage = "https://www.gnu.org/software/hello/manual/"; + changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${version}"; + license = licenses.gpl3Plus; + maintainers = [ maintainers.eelco ]; + platforms = platforms.all; + }; +} diff --git a/src/alejandra/tests/cases/idioms_pkgs_2/out.nix b/src/alejandra/tests/cases/idioms_pkgs_2/out.nix new file mode 100644 index 0000000..dd0a2b5 --- /dev/null +++ b/src/alejandra/tests/cases/idioms_pkgs_2/out.nix @@ -0,0 +1,43 @@ +{ + lib, + stdenv, + fetchurl, + nixos, + testVersion, + testEqualDerivation, + hello, +}: +stdenv.mkDerivation rec { + pname = "hello"; + version = "2.12"; + + src = fetchurl { + url = "mirror://gnu/hello/${pname}-${version}.tar.gz"; + sha256 = "1ayhp9v4m4rdhjmnl2bq3cibrbqqkgjbl3s7yk2nhlh8vj3ay16g"; + }; + + doCheck = true; + + passthru.tests = { + version = testVersion {package = hello;}; + + invariant-under-noXlibs = + testEqualDerivation + "hello must not be rebuilt when environment.noXlibs is set." + hello + (nixos {environment.noXlibs = true;}).pkgs.hello; + }; + + meta = with lib; { + description = "A program that produces a familiar, friendly greeting"; + longDescription = '' + GNU Hello is a program that prints "Hello, world!" when you run it. + It is fully customizable. + ''; + homepage = "https://www.gnu.org/software/hello/manual/"; + changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${version}"; + license = licenses.gpl3Plus; + maintainers = [maintainers.eelco]; + platforms = platforms.all; + }; +} diff --git a/src/alejandra/tests/cases/idioms_pkgs_3/in.nix b/src/alejandra/tests/cases/idioms_pkgs_3/in.nix new file mode 100644 index 0000000..d147155 --- /dev/null +++ b/src/alejandra/tests/cases/idioms_pkgs_3/in.nix @@ -0,0 +1,350 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (config.boot) kernelPatches; + inherit (config.boot.kernel) features randstructSeed; + inherit (config.boot.kernelPackages) kernel; + + kernelModulesConf = pkgs.writeText "nixos.conf" + '' + ${concatStringsSep "\n" config.boot.kernelModules} + ''; + +in + +{ + + ###### interface + + options = { + + boot.kernel.features = mkOption { + default = {}; + example = literalExpression "{ debug = true; }"; + internal = true; + description = '' + This option allows to enable or disable certain kernel features. + It's not API, because it's about kernel feature sets, that + make sense for specific use cases. Mostly along with programs, + which would have separate nixos options. + `grep features pkgs/os-specific/linux/kernel/common-config.nix` + ''; + }; + + boot.kernelPackages = mkOption { + default = pkgs.linuxPackages; + type = types.unspecified // { merge = mergeEqualOption; }; + apply = kernelPackages: kernelPackages.extend (self: super: { + kernel = super.kernel.override (originalArgs: { + inherit randstructSeed; + kernelPatches = (originalArgs.kernelPatches or []) ++ kernelPatches; + features = lib.recursiveUpdate super.kernel.features features; + }); + }); + # We don't want to evaluate all of linuxPackages for the manual + # - some of it might not even evaluate correctly. + defaultText = literalExpression "pkgs.linuxPackages"; + example = literalExpression "pkgs.linuxKernel.packages.linux_5_10"; + description = '' + This option allows you to override the Linux kernel used by + NixOS. Since things like external kernel module packages are + tied to the kernel you're using, it also overrides those. + This option is a function that takes Nixpkgs as an argument + (as a convenience), and returns an attribute set containing at + the very least an attribute kernel. + Additional attributes may be needed depending on your + configuration. For instance, if you use the NVIDIA X driver, + then it also needs to contain an attribute + nvidia_x11. + ''; + }; + + boot.kernelPatches = mkOption { + type = types.listOf types.attrs; + default = []; + example = literalExpression "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]"; + description = "A list of additional patches to apply to the kernel."; + }; + + boot.kernel.randstructSeed = mkOption { + type = types.str; + default = ""; + example = "my secret seed"; + description = '' + Provides a custom seed for the RANDSTRUCT security + option of the Linux kernel. Note that RANDSTRUCT is + only enabled in NixOS hardened kernels. Using a custom seed requires + building the kernel and dependent packages locally, since this + customization happens at build time. + ''; + }; + + boot.kernelParams = mkOption { + type = types.listOf (types.strMatching ''([^"[:space:]]|"[^"]*")+'' // { + name = "kernelParam"; + description = "string, with spaces inside double quotes"; + }); + default = [ ]; + description = "Parameters added to the kernel command line."; + }; + + boot.consoleLogLevel = mkOption { + type = types.int; + default = 4; + description = '' + The kernel console loglevel. All Kernel Messages with a log level smaller + than this setting will be printed to the console. + ''; + }; + + boot.vesa = mkOption { + type = types.bool; + default = false; + description = '' + (Deprecated) This option, if set, activates the VESA 800x600 video + mode on boot and disables kernel modesetting. It is equivalent to + specifying [ "vga=0x317" "nomodeset" ] in the + option. This option is + deprecated as of 2020: Xorg now works better with modesetting, and + you might want a different VESA vga setting, anyway. + ''; + }; + + boot.extraModulePackages = mkOption { + type = types.listOf types.package; + default = []; + example = literalExpression "[ config.boot.kernelPackages.nvidia_x11 ]"; + description = "A list of additional packages supplying kernel modules."; + }; + + boot.kernelModules = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The set of kernel modules to be loaded in the second stage of + the boot process. Note that modules that are needed to + mount the root file system should be added to + or + . + ''; + }; + + boot.initrd.availableKernelModules = mkOption { + type = types.listOf types.str; + default = []; + example = [ "sata_nv" "ext3" ]; + description = '' + The set of kernel modules in the initial ramdisk used during the + boot process. This set must include all modules necessary for + mounting the root device. That is, it should include modules + for the physical device (e.g., SCSI drivers) and for the file + system (e.g., ext3). The set specified here is automatically + closed under the module dependency relation, i.e., all + dependencies of the modules list here are included + automatically. The modules listed here are available in the + initrd, but are only loaded on demand (e.g., the ext3 module is + loaded automatically when an ext3 filesystem is mounted, and + modules for PCI devices are loaded when they match the PCI ID + of a device in your system). To force a module to be loaded, + include it in . + ''; + }; + + boot.initrd.kernelModules = mkOption { + type = types.listOf types.str; + default = []; + description = "List of modules that are always loaded by the initrd."; + }; + + boot.initrd.includeDefaultModules = mkOption { + type = types.bool; + default = true; + description = '' + This option, if set, adds a collection of default kernel modules + to and + . + ''; + }; + + system.modulesTree = mkOption { + type = types.listOf types.path; + internal = true; + default = []; + description = '' + Tree of kernel modules. This includes the kernel, plus modules + built outside of the kernel. Combine these into a single tree of + symlinks because modprobe only supports one directory. + ''; + # Convert the list of path to only one path. + apply = pkgs.aggregateModules; + }; + + system.requiredKernelConfig = mkOption { + default = []; + example = literalExpression '' + with config.lib.kernelConfig; [ + (isYes "MODULES") + (isEnabled "FB_CON_DECOR") + (isEnabled "BLK_DEV_INITRD") + ] + ''; + internal = true; + type = types.listOf types.attrs; + description = '' + This option allows modules to specify the kernel config options that + must be set (or unset) for the module to work. Please use the + lib.kernelConfig functions to build list elements. + ''; + }; + + }; + + + ###### implementation + + config = mkMerge + [ (mkIf config.boot.initrd.enable { + boot.initrd.availableKernelModules = + optionals config.boot.initrd.includeDefaultModules ([ + # Note: most of these (especially the SATA/PATA modules) + # shouldn't be included by default since nixos-generate-config + # detects them, but I'm keeping them for now for backwards + # compatibility. + + # Some SATA/PATA stuff. + "ahci" + "sata_nv" + "sata_via" + "sata_sis" + "sata_uli" + "ata_piix" + "pata_marvell" + + # Standard SCSI stuff. + "sd_mod" + "sr_mod" + + # SD cards and internal eMMC drives. + "mmc_block" + + # Support USB keyboards, in case the boot fails and we only have + # a USB keyboard, or for LUKS passphrase prompt. + "uhci_hcd" + "ehci_hcd" + "ehci_pci" + "ohci_hcd" + "ohci_pci" + "xhci_hcd" + "xhci_pci" + "usbhid" + "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat" + "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft" + + ] ++ optionals pkgs.stdenv.hostPlatform.isx86 [ + # Misc. x86 keyboard stuff. + "pcips2" "atkbd" "i8042" + + # x86 RTC needed by the stage 2 init script. + "rtc_cmos" + ]); + + boot.initrd.kernelModules = + optionals config.boot.initrd.includeDefaultModules [ + # For LVM. + "dm_mod" + ]; + }) + + (mkIf (!config.boot.isContainer) { + system.build = { inherit kernel; }; + + system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages; + + # Implement consoleLogLevel both in early boot and using sysctl + # (so you don't need to reboot to have changes take effect). + boot.kernelParams = + [ "loglevel=${toString config.boot.consoleLogLevel}" ] ++ + optionals config.boot.vesa [ "vga=0x317" "nomodeset" ]; + + boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel; + + boot.kernelModules = [ "loop" "atkbd" ]; + + # The Linux kernel >= 2.6.27 provides firmware. + hardware.firmware = [ kernel ]; + + # Create /etc/modules-load.d/nixos.conf, which is read by + # systemd-modules-load.service to load required kernel modules. + environment.etc = + { "modules-load.d/nixos.conf".source = kernelModulesConf; + }; + + systemd.services.systemd-modules-load = + { wantedBy = [ "multi-user.target" ]; + restartTriggers = [ kernelModulesConf ]; + serviceConfig = + { # Ignore failed module loads. Typically some of the + # modules in ‘boot.kernelModules’ are "nice to have but + # not required" (e.g. acpi-cpufreq), so we don't want to + # barf on those. + SuccessExitStatus = "0 1"; + }; + }; + + lib.kernelConfig = { + isYes = option: { + assertion = config: config.isYes option; + message = "CONFIG_${option} is not yes!"; + configLine = "CONFIG_${option}=y"; + }; + + isNo = option: { + assertion = config: config.isNo option; + message = "CONFIG_${option} is not no!"; + configLine = "CONFIG_${option}=n"; + }; + + isModule = option: { + assertion = config: config.isModule option; + message = "CONFIG_${option} is not built as a module!"; + configLine = "CONFIG_${option}=m"; + }; + + ### Usually you will just want to use these two + # True if yes or module + isEnabled = option: { + assertion = config: config.isEnabled option; + message = "CONFIG_${option} is not enabled!"; + configLine = "CONFIG_${option}=y"; + }; + + # True if no or omitted + isDisabled = option: { + assertion = config: config.isDisabled option; + message = "CONFIG_${option} is not disabled!"; + configLine = "CONFIG_${option}=n"; + }; + }; + + # The config options that all modules can depend upon + system.requiredKernelConfig = with config.lib.kernelConfig; + [ + # !!! Should this really be needed? + (isYes "MODULES") + (isYes "BINFMT_ELF") + ] ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT")); + + # nixpkgs kernels are assumed to have all required features + assertions = if config.boot.kernelPackages.kernel ? features then [] else + let cfg = config.boot.kernelPackages.kernel.config; in map (attrs: + { assertion = attrs.assertion cfg; inherit (attrs) message; } + ) config.system.requiredKernelConfig; + + }) + + ]; + +} diff --git a/src/alejandra/tests/cases/idioms_pkgs_3/out.nix b/src/alejandra/tests/cases/idioms_pkgs_3/out.nix new file mode 100644 index 0000000..ae087c2 --- /dev/null +++ b/src/alejandra/tests/cases/idioms_pkgs_3/out.nix @@ -0,0 +1,361 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + inherit (config.boot) kernelPatches; + inherit (config.boot.kernel) features randstructSeed; + inherit (config.boot.kernelPackages) kernel; + + kernelModulesConf = + pkgs.writeText "nixos.conf" + '' + ${concatStringsSep "\n" config.boot.kernelModules} + ''; +in { + ###### interface + + options = { + boot.kernel.features = mkOption { + default = {}; + example = literalExpression "{ debug = true; }"; + internal = true; + description = '' + This option allows to enable or disable certain kernel features. + It's not API, because it's about kernel feature sets, that + make sense for specific use cases. Mostly along with programs, + which would have separate nixos options. + `grep features pkgs/os-specific/linux/kernel/common-config.nix` + ''; + }; + + boot.kernelPackages = mkOption { + default = pkgs.linuxPackages; + type = types.unspecified // {merge = mergeEqualOption;}; + apply = kernelPackages: + kernelPackages.extend (self: super: { + kernel = super.kernel.override (originalArgs: { + inherit randstructSeed; + kernelPatches = (originalArgs.kernelPatches or []) ++ kernelPatches; + features = lib.recursiveUpdate super.kernel.features features; + }); + }); + # We don't want to evaluate all of linuxPackages for the manual + # - some of it might not even evaluate correctly. + defaultText = literalExpression "pkgs.linuxPackages"; + example = literalExpression "pkgs.linuxKernel.packages.linux_5_10"; + description = '' + This option allows you to override the Linux kernel used by + NixOS. Since things like external kernel module packages are + tied to the kernel you're using, it also overrides those. + This option is a function that takes Nixpkgs as an argument + (as a convenience), and returns an attribute set containing at + the very least an attribute kernel. + Additional attributes may be needed depending on your + configuration. For instance, if you use the NVIDIA X driver, + then it also needs to contain an attribute + nvidia_x11. + ''; + }; + + boot.kernelPatches = mkOption { + type = types.listOf types.attrs; + default = []; + example = literalExpression "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]"; + description = "A list of additional patches to apply to the kernel."; + }; + + boot.kernel.randstructSeed = mkOption { + type = types.str; + default = ""; + example = "my secret seed"; + description = '' + Provides a custom seed for the RANDSTRUCT security + option of the Linux kernel. Note that RANDSTRUCT is + only enabled in NixOS hardened kernels. Using a custom seed requires + building the kernel and dependent packages locally, since this + customization happens at build time. + ''; + }; + + boot.kernelParams = mkOption { + type = types.listOf (types.strMatching ''([^"[:space:]]|"[^"]*")+'' + // { + name = "kernelParam"; + description = "string, with spaces inside double quotes"; + }); + default = []; + description = "Parameters added to the kernel command line."; + }; + + boot.consoleLogLevel = mkOption { + type = types.int; + default = 4; + description = '' + The kernel console loglevel. All Kernel Messages with a log level smaller + than this setting will be printed to the console. + ''; + }; + + boot.vesa = mkOption { + type = types.bool; + default = false; + description = '' + (Deprecated) This option, if set, activates the VESA 800x600 video + mode on boot and disables kernel modesetting. It is equivalent to + specifying [ "vga=0x317" "nomodeset" ] in the + option. This option is + deprecated as of 2020: Xorg now works better with modesetting, and + you might want a different VESA vga setting, anyway. + ''; + }; + + boot.extraModulePackages = mkOption { + type = types.listOf types.package; + default = []; + example = literalExpression "[ config.boot.kernelPackages.nvidia_x11 ]"; + description = "A list of additional packages supplying kernel modules."; + }; + + boot.kernelModules = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The set of kernel modules to be loaded in the second stage of + the boot process. Note that modules that are needed to + mount the root file system should be added to + or + . + ''; + }; + + boot.initrd.availableKernelModules = mkOption { + type = types.listOf types.str; + default = []; + example = ["sata_nv" "ext3"]; + description = '' + The set of kernel modules in the initial ramdisk used during the + boot process. This set must include all modules necessary for + mounting the root device. That is, it should include modules + for the physical device (e.g., SCSI drivers) and for the file + system (e.g., ext3). The set specified here is automatically + closed under the module dependency relation, i.e., all + dependencies of the modules list here are included + automatically. The modules listed here are available in the + initrd, but are only loaded on demand (e.g., the ext3 module is + loaded automatically when an ext3 filesystem is mounted, and + modules for PCI devices are loaded when they match the PCI ID + of a device in your system). To force a module to be loaded, + include it in . + ''; + }; + + boot.initrd.kernelModules = mkOption { + type = types.listOf types.str; + default = []; + description = "List of modules that are always loaded by the initrd."; + }; + + boot.initrd.includeDefaultModules = mkOption { + type = types.bool; + default = true; + description = '' + This option, if set, adds a collection of default kernel modules + to and + . + ''; + }; + + system.modulesTree = mkOption { + type = types.listOf types.path; + internal = true; + default = []; + description = '' + Tree of kernel modules. This includes the kernel, plus modules + built outside of the kernel. Combine these into a single tree of + symlinks because modprobe only supports one directory. + ''; + # Convert the list of path to only one path. + apply = pkgs.aggregateModules; + }; + + system.requiredKernelConfig = mkOption { + default = []; + example = literalExpression '' + with config.lib.kernelConfig; [ + (isYes "MODULES") + (isEnabled "FB_CON_DECOR") + (isEnabled "BLK_DEV_INITRD") + ] + ''; + internal = true; + type = types.listOf types.attrs; + description = '' + This option allows modules to specify the kernel config options that + must be set (or unset) for the module to work. Please use the + lib.kernelConfig functions to build list elements. + ''; + }; + }; + + ###### implementation + + config = + mkMerge + [ + (mkIf config.boot.initrd.enable { + boot.initrd.availableKernelModules = optionals config.boot.initrd.includeDefaultModules ([ + # Note: most of these (especially the SATA/PATA modules) + # shouldn't be included by default since nixos-generate-config + # detects them, but I'm keeping them for now for backwards + # compatibility. + + # Some SATA/PATA stuff. + "ahci" + "sata_nv" + "sata_via" + "sata_sis" + "sata_uli" + "ata_piix" + "pata_marvell" + + # Standard SCSI stuff. + "sd_mod" + "sr_mod" + + # SD cards and internal eMMC drives. + "mmc_block" + + # Support USB keyboards, in case the boot fails and we only have + # a USB keyboard, or for LUKS passphrase prompt. + "uhci_hcd" + "ehci_hcd" + "ehci_pci" + "ohci_hcd" + "ohci_pci" + "xhci_hcd" + "xhci_pci" + "usbhid" + "hid_generic" + "hid_lenovo" + "hid_apple" + "hid_roccat" + "hid_logitech_hidpp" + "hid_logitech_dj" + "hid_microsoft" + ] + ++ optionals pkgs.stdenv.hostPlatform.isx86 [ + # Misc. x86 keyboard stuff. + "pcips2" + "atkbd" + "i8042" + + # x86 RTC needed by the stage 2 init script. + "rtc_cmos" + ]); + + boot.initrd.kernelModules = optionals config.boot.initrd.includeDefaultModules [ + # For LVM. + "dm_mod" + ]; + }) + + (mkIf (!config.boot.isContainer) { + system.build = {inherit kernel;}; + + system.modulesTree = [kernel] ++ config.boot.extraModulePackages; + + # Implement consoleLogLevel both in early boot and using sysctl + # (so you don't need to reboot to have changes take effect). + boot.kernelParams = + ["loglevel=${toString config.boot.consoleLogLevel}"] + ++ optionals config.boot.vesa ["vga=0x317" "nomodeset"]; + + boot.kernel.sysctl."kernel.printk" = mkDefault config.boot.consoleLogLevel; + + boot.kernelModules = ["loop" "atkbd"]; + + # The Linux kernel >= 2.6.27 provides firmware. + hardware.firmware = [kernel]; + + # Create /etc/modules-load.d/nixos.conf, which is read by + # systemd-modules-load.service to load required kernel modules. + environment.etc = { + "modules-load.d/nixos.conf".source = kernelModulesConf; + }; + + systemd.services.systemd-modules-load = { + wantedBy = ["multi-user.target"]; + restartTriggers = [kernelModulesConf]; + serviceConfig = { + # Ignore failed module loads. Typically some of the + # modules in ‘boot.kernelModules’ are "nice to have but + # not required" (e.g. acpi-cpufreq), so we don't want to + # barf on those. + SuccessExitStatus = "0 1"; + }; + }; + + lib.kernelConfig = { + isYes = option: { + assertion = config: config.isYes option; + message = "CONFIG_${option} is not yes!"; + configLine = "CONFIG_${option}=y"; + }; + + isNo = option: { + assertion = config: config.isNo option; + message = "CONFIG_${option} is not no!"; + configLine = "CONFIG_${option}=n"; + }; + + isModule = option: { + assertion = config: config.isModule option; + message = "CONFIG_${option} is not built as a module!"; + configLine = "CONFIG_${option}=m"; + }; + + ### Usually you will just want to use these two + # True if yes or module + isEnabled = option: { + assertion = config: config.isEnabled option; + message = "CONFIG_${option} is not enabled!"; + configLine = "CONFIG_${option}=y"; + }; + + # True if no or omitted + isDisabled = option: { + assertion = config: config.isDisabled option; + message = "CONFIG_${option} is not disabled!"; + configLine = "CONFIG_${option}=n"; + }; + }; + + # The config options that all modules can depend upon + system.requiredKernelConfig = with config.lib.kernelConfig; + [ + # !!! Should this really be needed? + (isYes "MODULES") + (isYes "BINFMT_ELF") + ] + ++ (optional (randstructSeed != "") (isYes "GCC_PLUGIN_RANDSTRUCT")); + + # nixpkgs kernels are assumed to have all required features + assertions = + if config.boot.kernelPackages.kernel ? features + then [] + else let + cfg = config.boot.kernelPackages.kernel.config; + in + map ( + attrs: { + assertion = attrs.assertion cfg; + inherit (attrs) message; + } + ) + config.system.requiredKernelConfig; + }) + ]; +} diff --git a/src/alejandra/tests/cases/monsters_1/in.nix b/src/alejandra/tests/cases/monsters_1/in.nix new file mode 100644 index 0000000..2759c4b --- /dev/null +++ b/src/alejandra/tests/cases/monsters_1/in.nix @@ -0,0 +1,269 @@ +{ +# foo +stdenv +# foo +, +# foo +lib +# foo +, +# foo +fetchFromGitLab +# foo +, +# foo +cairo +# foo +, +# foo +desktop-file-utils +# foo +, +# foo +gettext +# foo +, +# foo +glib +# foo +, +# foo +gtk4 +# foo +, +# foo +libadwaita +# foo +, +# foo +meson +# foo +, +# foo +ninja +# foo +, +# foo +pango +# foo +, +# foo +pkg-config +# foo +, +# foo +python3 +# foo +, +# foo +rustPlatform +# foo +, +# foo +wrapGAppsHook4 +# foo +}: +# foo +stdenv.mkDerivation +# foo +rec +# foo +{ +# foo +pname +# foo += +# foo +"contrast"; +# foo +version +# foo += +# foo +"0.0.5"; +# foo +src +# foo += +# foo +fetchFromGitLab +# foo +{ +# foo +domain +# foo += +# foo +"gitlab.gnome.org"; +# foo +group +# foo += +# foo +"World"; +# foo +owner +# foo += +# foo +"design"; +# foo +repo +# foo += +# foo +"contrast"; +# foo +rev +# foo += +# foo +version; +# foo +sha256 +# foo += +# foo +"cypSbqLwSmauOoWOuppWpF3hvrxiqmkLspxAWzvlUC0="; +# foo +}; +# foo +cargoDeps +# foo += +# foo +rustPlatform.fetchCargoTarball +# foo +{ +# foo +inherit +# foo +src; +# foo +name +# foo += +# foo +"${pname}-${version}"; +# foo +hash +# foo += +# foo +"sha256-W4FyqwJpimf0isQRCq9TegpTQPQfsumx40AFQCFG5VQ="; +# foo +}; +# foo +nativeBuildInputs +# foo += +# foo +[ +# foo +desktop-file-utils +# foo +gettext +# foo +meson +# foo +ninja +# foo +pkg-config +# foo +python3 +# foo +rustPlatform.rust.cargo +# foo +rustPlatform.cargoSetupHook +# foo +rustPlatform.rust.rustc +# foo +wrapGAppsHook4 +# foo +glib +# foo +# for glib-compile-resources + +# foo +]; +# foo +buildInputs +# foo += +# foo +[ +# foo +cairo +# foo +glib +# foo +gtk4 +# foo +libadwaita +# foo +pango +# foo +]; +# foo +postPatch +# foo += +# foo +'' + patchShebangs build-aux/meson_post_install.py + # https://gitlab.gnome.org/World/design/contrast/-/merge_requests/23 + substituteInPlace build-aux/meson_post_install.py \ + --replace "gtk-update-icon-cache" "gtk4-update-icon-cache" + ''; +# foo +meta +# foo += +# foo +with +# foo +lib; +# foo +{ +# foo +description +# foo += +# foo +"Checks whether the contrast between two colors meet the WCAG requirements"; +# foo +homepage +# foo += +# foo +"https://gitlab.gnome.org/World/design/contrast"; +# foo +license +# foo += +# foo +licenses.gpl3Plus; +# foo +maintainers +# foo += +# foo +with +# foo +maintainers; +# foo +[ +# foo +jtojnar +# foo +]; +# foo +platforms +# foo += +# foo +platforms.unix; +# foo +}; +# foo +} diff --git a/src/alejandra/tests/cases/monsters_1/out.nix b/src/alejandra/tests/cases/monsters_1/out.nix new file mode 100644 index 0000000..9a87685 --- /dev/null +++ b/src/alejandra/tests/cases/monsters_1/out.nix @@ -0,0 +1,255 @@ +{ + # foo + stdenv, + # foo + # foo + lib, + # foo + # foo + fetchFromGitLab, + # foo + # foo + cairo, + # foo + # foo + desktop-file-utils, + # foo + # foo + gettext, + # foo + # foo + glib, + # foo + # foo + gtk4, + # foo + # foo + libadwaita, + # foo + # foo + meson, + # foo + # foo + ninja, + # foo + # foo + pango, + # foo + # foo + pkg-config, + # foo + # foo + python3, + # foo + # foo + rustPlatform, + # foo + # foo + wrapGAppsHook4, + # foo +}: +# foo +stdenv.mkDerivation +# foo +rec +# foo +{ + # foo + pname + # foo + = + # foo + "contrast"; + # foo + version + # foo + = + # foo + "0.0.5"; + # foo + src + # foo + = + # foo + fetchFromGitLab + # foo + { + # foo + domain + # foo + = + # foo + "gitlab.gnome.org"; + # foo + group + # foo + = + # foo + "World"; + # foo + owner + # foo + = + # foo + "design"; + # foo + repo + # foo + = + # foo + "contrast"; + # foo + rev + # foo + = + # foo + version; + # foo + sha256 + # foo + = + # foo + "cypSbqLwSmauOoWOuppWpF3hvrxiqmkLspxAWzvlUC0="; + # foo + }; + # foo + cargoDeps + # foo + = + # foo + rustPlatform.fetchCargoTarball + # foo + { + # foo + inherit + # foo + src + ; + # foo + name + # foo + = + # foo + "${pname}-${version}"; + # foo + hash + # foo + = + # foo + "sha256-W4FyqwJpimf0isQRCq9TegpTQPQfsumx40AFQCFG5VQ="; + # foo + }; + # foo + nativeBuildInputs + # foo + = + # foo + [ + # foo + desktop-file-utils + # foo + gettext + # foo + meson + # foo + ninja + # foo + pkg-config + # foo + python3 + # foo + rustPlatform.rust.cargo + # foo + rustPlatform.cargoSetupHook + # foo + rustPlatform.rust.rustc + # foo + wrapGAppsHook4 + # foo + glib + # foo + # for glib-compile-resources + + # foo + ]; + # foo + buildInputs + # foo + = + # foo + [ + # foo + cairo + # foo + glib + # foo + gtk4 + # foo + libadwaita + # foo + pango + # foo + ]; + # foo + postPatch + # foo + = + # foo + '' + patchShebangs build-aux/meson_post_install.py + # https://gitlab.gnome.org/World/design/contrast/-/merge_requests/23 + substituteInPlace build-aux/meson_post_install.py \ + --replace "gtk-update-icon-cache" "gtk4-update-icon-cache" + ''; + # foo + meta + # foo + = + # foo + with + # foo + lib; + # foo + { + # foo + description + # foo + = + # foo + "Checks whether the contrast between two colors meet the WCAG requirements"; + # foo + homepage + # foo + = + # foo + "https://gitlab.gnome.org/World/design/contrast"; + # foo + license + # foo + = + # foo + licenses.gpl3Plus; + # foo + maintainers + # foo + = + # foo + with + # foo + maintainers; + # foo + [ + # foo + jtojnar + # foo + ]; + # foo + platforms + # foo + = + # foo + platforms.unix; + # foo + }; + # foo +} diff --git a/src/alejandra/tests/cases/monsters_2/in.nix b/src/alejandra/tests/cases/monsters_2/in.nix new file mode 100644 index 0000000..c108b1a --- /dev/null +++ b/src/alejandra/tests/cases/monsters_2/in.nix @@ -0,0 +1,31 @@ + +{ + lib = { + + /* Concatenate two lists + + Type: concat :: [a] -> [a] -> [a] + + Example: + concat [ 1 2 ] [ 3 4 ] + => [ 1 2 3 4 ] + */ + concat = x: y: x ++ y; + }; + + options = { + + boot.kernel.features = mkOption { + default = {}; + example = literalExpression "{ debug = true; }"; + internal = true; + description = '' + This option allows to enable or disable certain kernel features. + It's not API, because it's about kernel feature sets, that + make sense for specific use cases. Mostly along with programs, + which would have separate nixos options. + `grep features pkgs/os-specific/linux/kernel/common-config.nix` + ''; + }; + }; +} diff --git a/src/alejandra/tests/cases/monsters_2/out.nix b/src/alejandra/tests/cases/monsters_2/out.nix new file mode 100644 index 0000000..ae70fdb --- /dev/null +++ b/src/alejandra/tests/cases/monsters_2/out.nix @@ -0,0 +1,29 @@ +{ + lib = { + /* + Concatenate two lists + + Type: concat :: [a] -> [a] -> [a] + + Example: + concat [ 1 2 ] [ 3 4 ] + => [ 1 2 3 4 ] + */ + concat = x: y: x ++ y; + }; + + options = { + boot.kernel.features = mkOption { + default = {}; + example = literalExpression "{ debug = true; }"; + internal = true; + description = '' + This option allows to enable or disable certain kernel features. + It's not API, because it's about kernel feature sets, that + make sense for specific use cases. Mostly along with programs, + which would have separate nixos options. + `grep features pkgs/os-specific/linux/kernel/common-config.nix` + ''; + }; + }; +} diff --git a/src/alejandra/tests/cases/monsters_3/in.nix b/src/alejandra/tests/cases/monsters_3/in.nix new file mode 100644 index 0000000..5c09b5a --- /dev/null +++ b/src/alejandra/tests/cases/monsters_3/in.nix @@ -0,0 +1,7 @@ +{ stdenv , lib , fetchFromGitLab , cairo , desktop-file-utils , gettext , glib , gtk4 , libadwaita , meson , ninja , pango , pkg-config , python3 , rustPlatform , wrapGAppsHook4 }: stdenv.mkDerivation rec { pname = "contrast"; version = "0.0.5"; src = fetchFromGitLab { domain = "gitlab.gnome.org"; group = "World"; owner = "design"; repo = "contrast"; rev = version; sha256 = "cypSbqLwSmauOoWOuppWpF3hvrxiqmkLspxAWzvlUC0="; }; cargoDeps = rustPlatform.fetchCargoTarball { inherit src; name = "${pname}-${version}"; hash = "sha256-W4FyqwJpimf0isQRCq9TegpTQPQfsumx40AFQCFG5VQ="; }; nativeBuildInputs = [ desktop-file-utils gettext meson ninja pkg-config python3 rustPlatform.rust.cargo rustPlatform.cargoSetupHook rustPlatform.rust.rustc wrapGAppsHook4 glib # for glib-compile-resources + ]; buildInputs = [ cairo glib gtk4 libadwaita pango ]; postPatch = '' + patchShebangs build-aux/meson_post_install.py + # https://gitlab.gnome.org/World/design/contrast/-/merge_requests/23 + substituteInPlace build-aux/meson_post_install.py \ + --replace "gtk-update-icon-cache" "gtk4-update-icon-cache" + ''; meta = with lib; { description = "Checks whether the contrast between two colors meet the WCAG requirements"; homepage = "https://gitlab.gnome.org/World/design/contrast"; license = licenses.gpl3Plus; maintainers = with maintainers; [ jtojnar ]; platforms = platforms.unix; }; } diff --git a/src/alejandra/tests/cases/monsters_3/out.nix b/src/alejandra/tests/cases/monsters_3/out.nix new file mode 100644 index 0000000..25ff805 --- /dev/null +++ b/src/alejandra/tests/cases/monsters_3/out.nix @@ -0,0 +1,62 @@ +{ + stdenv, + lib, + fetchFromGitLab, + cairo, + desktop-file-utils, + gettext, + glib, + gtk4, + libadwaita, + meson, + ninja, + pango, + pkg-config, + python3, + rustPlatform, + wrapGAppsHook4, +}: +stdenv.mkDerivation rec { + pname = "contrast"; + version = "0.0.5"; + src = fetchFromGitLab { + domain = "gitlab.gnome.org"; + group = "World"; + owner = "design"; + repo = "contrast"; + rev = version; + sha256 = "cypSbqLwSmauOoWOuppWpF3hvrxiqmkLspxAWzvlUC0="; + }; + cargoDeps = rustPlatform.fetchCargoTarball { + inherit src; + name = "${pname}-${version}"; + hash = "sha256-W4FyqwJpimf0isQRCq9TegpTQPQfsumx40AFQCFG5VQ="; + }; + nativeBuildInputs = [ + desktop-file-utils + gettext + meson + ninja + pkg-config + python3 + rustPlatform.rust.cargo + rustPlatform.cargoSetupHook + rustPlatform.rust.rustc + wrapGAppsHook4 + glib # for glib-compile-resources + ]; + buildInputs = [cairo glib gtk4 libadwaita pango]; + postPatch = '' + patchShebangs build-aux/meson_post_install.py + # https://gitlab.gnome.org/World/design/contrast/-/merge_requests/23 + substituteInPlace build-aux/meson_post_install.py \ + --replace "gtk-update-icon-cache" "gtk4-update-icon-cache" + ''; + meta = with lib; { + description = "Checks whether the contrast between two colors meet the WCAG requirements"; + homepage = "https://gitlab.gnome.org/World/design/contrast"; + license = licenses.gpl3Plus; + maintainers = with maintainers; [jtojnar]; + platforms = platforms.unix; + }; +} diff --git a/src/alejandra/tests/cases/monsters_4/in.nix b/src/alejandra/tests/cases/monsters_4/in.nix new file mode 100644 index 0000000..a48c863 --- /dev/null +++ b/src/alejandra/tests/cases/monsters_4/in.nix @@ -0,0 +1,7 @@ +{/*Foo*/stdenv/*Foo*/,/*Foo*/lib/*Foo*/,/*Foo*/fetchFromGitLab/*Foo*/,/*Foo*/cairo/*Foo*/,/*Foo*/desktop-file-utils/*Foo*/,/*Foo*/gettext/*Foo*/,/*Foo*/glib/*Foo*/,/*Foo*/gtk4/*Foo*/,/*Foo*/libadwaita/*Foo*/,/*Foo*/meson/*Foo*/,/*Foo*/ninja/*Foo*/,/*Foo*/pango/*Foo*/,/*Foo*/pkg-config/*Foo*/,/*Foo*/python3/*Foo*/,/*Foo*/rustPlatform/*Foo*/,/*Foo*/wrapGAppsHook4/*Foo*/}:/*Foo*/stdenv.mkDerivation/*Foo*/rec/*Foo*/{/*Foo*/pname/*Foo*/=/*Foo*/"contrast";/*Foo*/version/*Foo*/=/*Foo*/"0.0.5";/*Foo*/src/*Foo*/=/*Foo*/fetchFromGitLab/*Foo*/{/*Foo*/domain/*Foo*/=/*Foo*/"gitlab.gnome.org";/*Foo*/group/*Foo*/=/*Foo*/"World";/*Foo*/owner/*Foo*/=/*Foo*/"design";/*Foo*/repo/*Foo*/=/*Foo*/"contrast";/*Foo*/rev/*Foo*/=/*Foo*/version;/*Foo*/sha256/*Foo*/=/*Foo*/"cypSbqLwSmauOoWOuppWpF3hvrxiqmkLspxAWzvlUC0=";/*Foo*/};/*Foo*/cargoDeps/*Foo*/=/*Foo*/rustPlatform.fetchCargoTarball/*Foo*/{/*Foo*/inherit/*Foo*/src;/*Foo*/name/*Foo*/=/*Foo*/"${pname}-${version}";/*Foo*/hash/*Foo*/=/*Foo*/"sha256-W4FyqwJpimf0isQRCq9TegpTQPQfsumx40AFQCFG5VQ=";/*Foo*/};/*Foo*/nativeBuildInputs/*Foo*/=/*Foo*/[/*Foo*/desktop-file-utils/*Foo*/gettext/*Foo*/meson/*Foo*/ninja/*Foo*/pkg-config/*Foo*/python3/*Foo*/rustPlatform.rust.cargo/*Foo*/rustPlatform.cargoSetupHook/*Foo*/rustPlatform.rust.rustc/*Foo*/wrapGAppsHook4/*Foo*/glib/*Foo*/# for glib-compile-resources +/*Foo*/];/*Foo*/buildInputs/*Foo*/=/*Foo*/[/*Foo*/cairo/*Foo*/glib/*Foo*/gtk4/*Foo*/libadwaita/*Foo*/pango/*Foo*/];/*Foo*/postPatch/*Foo*/=/*Foo*/'' + patchShebangs build-aux/meson_post_install.py + # https://gitlab.gnome.org/World/design/contrast/-/merge_requests/23 + substituteInPlace build-aux/meson_post_install.py \ + --replace "gtk-update-icon-cache" "gtk4-update-icon-cache" + '';/*Foo*/meta/*Foo*/=/*Foo*/with/*Foo*/lib;/*Foo*/{/*Foo*/description/*Foo*/=/*Foo*/"Checks whether the contrast between two colors meet the WCAG requirements";/*Foo*/homepage/*Foo*/=/*Foo*/"https://gitlab.gnome.org/World/design/contrast";/*Foo*/license/*Foo*/=/*Foo*/licenses.gpl3Plus;/*Foo*/maintainers/*Foo*/=/*Foo*/with/*Foo*/maintainers;/*Foo*/[/*Foo*/jtojnar/*Foo*/];/*Foo*/platforms/*Foo*/=/*Foo*/platforms.unix;/*Foo*/};/*Foo*/} diff --git a/src/alejandra/tests/cases/monsters_4/out.nix b/src/alejandra/tests/cases/monsters_4/out.nix new file mode 100644 index 0000000..241e6ec --- /dev/null +++ b/src/alejandra/tests/cases/monsters_4/out.nix @@ -0,0 +1,516 @@ +{ + /* + Foo + */ + stdenv, + /* + Foo + */ + /* + Foo + */ + lib, + /* + Foo + */ + /* + Foo + */ + fetchFromGitLab, + /* + Foo + */ + /* + Foo + */ + cairo, + /* + Foo + */ + /* + Foo + */ + desktop-file-utils, + /* + Foo + */ + /* + Foo + */ + gettext, + /* + Foo + */ + /* + Foo + */ + glib, + /* + Foo + */ + /* + Foo + */ + gtk4, + /* + Foo + */ + /* + Foo + */ + libadwaita, + /* + Foo + */ + /* + Foo + */ + meson, + /* + Foo + */ + /* + Foo + */ + ninja, + /* + Foo + */ + /* + Foo + */ + pango, + /* + Foo + */ + /* + Foo + */ + pkg-config, + /* + Foo + */ + /* + Foo + */ + python3, + /* + Foo + */ + /* + Foo + */ + rustPlatform, + /* + Foo + */ + /* + Foo + */ + wrapGAppsHook4, + /* + Foo + */ +}: +/* +Foo +*/ +stdenv.mkDerivation +/* +Foo +*/ +rec +/* +Foo +*/ +{ + /* + Foo + */ + pname + /* + Foo + */ + = + /* + Foo + */ + "contrast"; + /* + Foo + */ + version + /* + Foo + */ + = + /* + Foo + */ + "0.0.5"; + /* + Foo + */ + src + /* + Foo + */ + = + /* + Foo + */ + fetchFromGitLab + /* + Foo + */ + { + /* + Foo + */ + domain + /* + Foo + */ + = + /* + Foo + */ + "gitlab.gnome.org"; + /* + Foo + */ + group + /* + Foo + */ + = + /* + Foo + */ + "World"; + /* + Foo + */ + owner + /* + Foo + */ + = + /* + Foo + */ + "design"; + /* + Foo + */ + repo + /* + Foo + */ + = + /* + Foo + */ + "contrast"; + /* + Foo + */ + rev + /* + Foo + */ + = + /* + Foo + */ + version; + /* + Foo + */ + sha256 + /* + Foo + */ + = + /* + Foo + */ + "cypSbqLwSmauOoWOuppWpF3hvrxiqmkLspxAWzvlUC0="; + /* + Foo + */ + }; + /* + Foo + */ + cargoDeps + /* + Foo + */ + = + /* + Foo + */ + rustPlatform.fetchCargoTarball + /* + Foo + */ + { + /* + Foo + */ + inherit + /* + Foo + */ + src + ; + /* + Foo + */ + name + /* + Foo + */ + = + /* + Foo + */ + "${pname}-${version}"; + /* + Foo + */ + hash + /* + Foo + */ + = + /* + Foo + */ + "sha256-W4FyqwJpimf0isQRCq9TegpTQPQfsumx40AFQCFG5VQ="; + /* + Foo + */ + }; + /* + Foo + */ + nativeBuildInputs + /* + Foo + */ + = + /* + Foo + */ + [ + /* + Foo + */ + desktop-file-utils + /* + Foo + */ + gettext + /* + Foo + */ + meson + /* + Foo + */ + ninja + /* + Foo + */ + pkg-config + /* + Foo + */ + python3 + /* + Foo + */ + rustPlatform.rust.cargo + /* + Foo + */ + rustPlatform.cargoSetupHook + /* + Foo + */ + rustPlatform.rust.rustc + /* + Foo + */ + wrapGAppsHook4 + /* + Foo + */ + glib + /* + Foo + */ + # for glib-compile-resources + /* + Foo + */ + ]; + /* + Foo + */ + buildInputs + /* + Foo + */ + = + /* + Foo + */ + [ + /* + Foo + */ + cairo + /* + Foo + */ + glib + /* + Foo + */ + gtk4 + /* + Foo + */ + libadwaita + /* + Foo + */ + pango + /* + Foo + */ + ]; + /* + Foo + */ + postPatch + /* + Foo + */ + = + /* + Foo + */ + '' + patchShebangs build-aux/meson_post_install.py + # https://gitlab.gnome.org/World/design/contrast/-/merge_requests/23 + substituteInPlace build-aux/meson_post_install.py \ + --replace "gtk-update-icon-cache" "gtk4-update-icon-cache" + ''; + /* + Foo + */ + meta + /* + Foo + */ + = + /* + Foo + */ + with + /* + Foo + */ + lib; + /* + Foo + */ + { + /* + Foo + */ + description + /* + Foo + */ + = + /* + Foo + */ + "Checks whether the contrast between two colors meet the WCAG requirements"; + /* + Foo + */ + homepage + /* + Foo + */ + = + /* + Foo + */ + "https://gitlab.gnome.org/World/design/contrast"; + /* + Foo + */ + license + /* + Foo + */ + = + /* + Foo + */ + licenses.gpl3Plus; + /* + Foo + */ + maintainers + /* + Foo + */ + = + /* + Foo + */ + with + /* + Foo + */ + maintainers; + /* + Foo + */ + [ + /* + Foo + */ + jtojnar + /* + Foo + */ + ]; + /* + Foo + */ + platforms + /* + Foo + */ + = + /* + Foo + */ + platforms.unix; + /* + Foo + */ + }; + /* + Foo + */ +} diff --git a/src/alejandra/tests/cases/monsters_5/in.nix b/src/alejandra/tests/cases/monsters_5/in.nix new file mode 100644 index 0000000..54ed128 --- /dev/null +++ b/src/alejandra/tests/cases/monsters_5/in.nix @@ -0,0 +1,327 @@ +{ + +config, + +lib, + +pkgs, + +... + +}: + +with + +lib; + +let + + + + + +inherit + +(config.boot) + +kernelPatches; + + + + +inherit + +(config.boot.kernel) + +features + +randstructSeed; + + + + +inherit + +(config.boot.kernelPackages) + +kernel; + + + + + +kernelModulesConf + += + +pkgs.writeText + +"nixos.conf" + + + + + + + + +'' + ${concatStringsSep "\n" config.boot.kernelModules} + ''; + +in + +{ + + + + + +###### interface + + + + + +options + += + +{ + + + + + + + + + +boot.kernel.features + += + +mkOption + +{ + + +default + += + +{}; + + +example + += + +literalExpression + +"{debug= true;}"; + + +internal + += + +true; + + +description + += + +'' + This option allows to enable or disable certain kernel features. + It's not API, because it's about kernel feature sets, that + make sense for specific use cases. Mostly along with programs, + which would have separate nixos options. + `grep features pkgs/os-specific/linux/kernel/common-config.nix` + ''; + + +}; + + + +boot.kernelPackages + += + +mkOption + +{ + + +default + += + +pkgs.linuxPackages; + + +type + += + +types.unspecified + +// + +{ + +merge + += + +mergeEqualOption; + +}; + + +apply + += + +kernelPackages: + +kernelPackages.extend + +(self: + +super: + +{ + + +kernel + += + +super.kernel.override + +(originalArgs: + +{ + + +inherit + +randstructSeed; + + +kernelPatches + += + +(originalArgs.kernelPatches + +or + +[]) + +++ + +kernelPatches; + + +features + += + +lib.recursiveUpdate + +super.kernel.features + +features; + + +}); + + +}); + + +# We don't want to evaluate all of linuxPackages for the manual + # - some of it might not even evaluate correctly. + + +defaultText + += + +literalExpression + +"pkgs.linuxPackages"; + + +example + += + +literalExpression + +"pkgs.linuxKernel.packages.linux_5_10"; + + +description + += + +'' + This option allows you to override the Linux kernel used by + NixOS. Since things like external kernel module packages are + tied to the kernel you're using, it also overrides those. + This option is a function that takes Nixpkgs as an argument + (as a convenience), and returns an attribute set containing at + the very least an attribute kernel. + Additional attributes may be needed depending on your + configuration. For instance, if you use the NVIDIA X driver, + then it also needs to contain an attribute + nvidia_x11. + ''; + + +}; + + + +boot.kernelPatches + += + +mkOption + +{ + + +type + += + +types.listOf + +types.attrs; + + +default + += + +[]; + + +example + += + +literalExpression + +"[ pkgs.kernelPatches.ubuntu_fan_4_4 ]"; + description = "A list of additional patches to apply to the kernel."; + }; + + +};} diff --git a/src/alejandra/tests/cases/monsters_5/out.nix b/src/alejandra/tests/cases/monsters_5/out.nix new file mode 100644 index 0000000..e4178b8 --- /dev/null +++ b/src/alejandra/tests/cases/monsters_5/out.nix @@ -0,0 +1,130 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; let + inherit + (config.boot) + kernelPatches + ; + + inherit + (config.boot.kernel) + features + randstructSeed + ; + + inherit + (config.boot.kernelPackages) + kernel + ; + + kernelModulesConf = + pkgs.writeText + "nixos.conf" + '' + ${concatStringsSep "\n" config.boot.kernelModules} + ''; +in { + ###### interface + + options = { + boot.kernel.features = + mkOption + { + default = {}; + + example = + literalExpression + "{debug= true;}"; + + internal = + true; + + description = '' + This option allows to enable or disable certain kernel features. + It's not API, because it's about kernel feature sets, that + make sense for specific use cases. Mostly along with programs, + which would have separate nixos options. + `grep features pkgs/os-specific/linux/kernel/common-config.nix` + ''; + }; + + boot.kernelPackages = + mkOption + { + default = + pkgs.linuxPackages; + + type = + types.unspecified + // { + merge = + mergeEqualOption; + }; + + apply = kernelPackages: + kernelPackages.extend + (self: super: { + kernel = + super.kernel.override + (originalArgs: { + inherit + randstructSeed + ; + + kernelPatches = + (originalArgs.kernelPatches + or []) + ++ kernelPatches; + + features = + lib.recursiveUpdate + super.kernel.features + features; + }); + }); + + # We don't want to evaluate all of linuxPackages for the manual + # - some of it might not even evaluate correctly. + + defaultText = + literalExpression + "pkgs.linuxPackages"; + + example = + literalExpression + "pkgs.linuxKernel.packages.linux_5_10"; + + description = '' + This option allows you to override the Linux kernel used by + NixOS. Since things like external kernel module packages are + tied to the kernel you're using, it also overrides those. + This option is a function that takes Nixpkgs as an argument + (as a convenience), and returns an attribute set containing at + the very least an attribute kernel. + Additional attributes may be needed depending on your + configuration. For instance, if you use the NVIDIA X driver, + then it also needs to contain an attribute + nvidia_x11. + ''; + }; + + boot.kernelPatches = + mkOption + { + type = + types.listOf + types.attrs; + + default = []; + + example = + literalExpression + "[ pkgs.kernelPatches.ubuntu_fan_4_4 ]"; + description = "A list of additional patches to apply to the kernel."; + }; + }; +}