treewide: Format all Nix files

Format all Nix files using the officially approved formatter,
making the CI check introduced in the previous commit succeed:

  nix-build ci -A fmt.check

This is the next step of the of the [implementation](https://github.com/NixOS/nixfmt/issues/153)
of the accepted [RFC 166](https://github.com/NixOS/rfcs/pull/166).

This commit will lead to merge conflicts for a number of PRs,
up to an estimated ~1100 (~33%) among the PRs with activity in the past 2
months, but that should be lower than what it would be without the previous
[partial treewide format](https://github.com/NixOS/nixpkgs/pull/322537).

Merge conflicts caused by this commit can now automatically be resolved while rebasing using the
[auto-rebase script](8616af08d9/maintainers/scripts/auto-rebase).

If you run into any problems regarding any of this, please reach out to the
[formatting team](https://nixos.org/community/teams/formatting/) by
pinging @NixOS/nix-formatting.
This commit is contained in:
Silvan Mosberger 2025-04-01 20:10:43 +02:00
parent 2140bf39e4
commit 374e6bcc40
1523 changed files with 986047 additions and 513621 deletions

245
flake.nix
View File

@ -3,17 +3,21 @@
{
description = "A collection of packages for the Nix package manager";
outputs = { self }:
outputs =
{ self }:
let
libVersionInfoOverlay = import ./lib/flake-version-info.nix self;
lib = (import ./lib).extend libVersionInfoOverlay;
forAllSystems = lib.genAttrs lib.systems.flakeExposed;
jobs = forAllSystems (system: import ./pkgs/top-level/release.nix {
nixpkgs = self;
inherit system;
});
jobs = forAllSystems (
system:
import ./pkgs/top-level/release.nix {
nixpkgs = self;
inherit system;
}
);
in
{
/**
@ -26,112 +30,142 @@
*/
# DON'T USE lib.extend TO ADD NEW FUNCTIONALITY.
# THIS WAS A MISTAKE. See the warning in lib/default.nix.
lib = lib.extend (final: prev: {
lib = lib.extend (
final: prev: {
/**
Other NixOS-provided functionality, such as [`runTest`](https://nixos.org/manual/nixos/unstable/#sec-call-nixos-test-outside-nixos).
See also `lib.nixosSystem`.
*/
nixos = import ./nixos/lib { lib = final; };
/**
Other NixOS-provided functionality, such as [`runTest`](https://nixos.org/manual/nixos/unstable/#sec-call-nixos-test-outside-nixos).
See also `lib.nixosSystem`.
*/
nixos = import ./nixos/lib { lib = final; };
/**
Create a NixOS system configuration.
/**
Create a NixOS system configuration.
Example:
Example:
lib.nixosSystem {
modules = [ ./configuration.nix ];
lib.nixosSystem {
modules = [ ./configuration.nix ];
}
Inputs:
- `modules` (list of paths or inline modules): The NixOS modules to include in the system configuration.
- `specialArgs` (attribute set): Extra arguments to pass to all modules, that are available in `imports` but can not be extended or overridden by the `modules`.
- `modulesLocation` (path): A default location for modules that aren't passed by path, used for error messages.
Legacy inputs:
- `system`: Legacy alias for `nixpkgs.hostPlatform`, but this is already set in the generated `hardware-configuration.nix`, included by `configuration.nix`.
- `pkgs`: Legacy alias for `nixpkgs.pkgs`; use `nixpkgs.pkgs` and `nixosModules.readOnlyPkgs` instead.
*/
nixosSystem =
args:
import ./nixos/lib/eval-config.nix (
{
lib = final;
# Allow system to be set modularly in nixpkgs.system.
# We set it to null, to remove the "legacy" entrypoint's
# non-hermetic default.
system = null;
modules = args.modules ++ [
# This module is injected here since it exposes the nixpkgs self-path in as
# constrained of contexts as possible to avoid more things depending on it and
# introducing unnecessary potential fragility to changes in flakes itself.
#
# See: failed attempt to make pkgs.path not copy when using flakes:
# https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1023287913
(
{
config,
pkgs,
lib,
...
}:
{
config.nixpkgs.flake.source = self.outPath;
}
)
];
}
// builtins.removeAttrs args [ "modules" ]
);
}
);
Inputs:
- `modules` (list of paths or inline modules): The NixOS modules to include in the system configuration.
- `specialArgs` (attribute set): Extra arguments to pass to all modules, that are available in `imports` but can not be extended or overridden by the `modules`.
- `modulesLocation` (path): A default location for modules that aren't passed by path, used for error messages.
Legacy inputs:
- `system`: Legacy alias for `nixpkgs.hostPlatform`, but this is already set in the generated `hardware-configuration.nix`, included by `configuration.nix`.
- `pkgs`: Legacy alias for `nixpkgs.pkgs`; use `nixpkgs.pkgs` and `nixosModules.readOnlyPkgs` instead.
*/
nixosSystem = args:
import ./nixos/lib/eval-config.nix (
checks = forAllSystems (
system:
{
tarball = jobs.${system}.tarball;
}
//
lib.optionalAttrs
(
self.legacyPackages.${system}.stdenv.hostPlatform.isLinux
# Exclude power64 due to "libressl is not available on the requested hostPlatform" with hostPlatform being power64
&& !self.legacyPackages.${system}.targetPlatform.isPower64
)
{
lib = final;
# Allow system to be set modularly in nixpkgs.system.
# We set it to null, to remove the "legacy" entrypoint's
# non-hermetic default.
system = null;
modules = args.modules ++ [
# This module is injected here since it exposes the nixpkgs self-path in as
# constrained of contexts as possible to avoid more things depending on it and
# introducing unnecessary potential fragility to changes in flakes itself.
#
# See: failed attempt to make pkgs.path not copy when using flakes:
# https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1023287913
({ config, pkgs, lib, ... }: {
config.nixpkgs.flake.source = self.outPath;
})
];
} // builtins.removeAttrs args [ "modules" ]
);
});
checks = forAllSystems (system: {
tarball = jobs.${system}.tarball;
} // lib.optionalAttrs
(
self.legacyPackages.${system}.stdenv.hostPlatform.isLinux
# Exclude power64 due to "libressl is not available on the requested hostPlatform" with hostPlatform being power64
&& !self.legacyPackages.${system}.targetPlatform.isPower64
)
{
# Test that ensures that the nixosSystem function can accept a lib argument
# Note: prefer not to extend or modify `lib`, especially if you want to share reusable modules
# alternatives include: `import` a file, or put a custom library in an option or in `_module.args.<libname>`
nixosSystemAcceptsLib = (self.lib.nixosSystem {
pkgs = self.legacyPackages.${system};
lib = self.lib.extend (final: prev: {
ifThisFunctionIsMissingTheTestFails = final.id;
});
modules = [
./nixos/modules/profiles/minimal.nix
({ lib, ... }: lib.ifThisFunctionIsMissingTheTestFails {
# Define a minimal config without eval warnings
nixpkgs.hostPlatform = "x86_64-linux";
boot.loader.grub.enable = false;
fileSystems."/".device = "nodev";
# See https://search.nixos.org/options?show=system.stateVersion&query=stateversion
system.stateVersion = lib.trivial.release; # DON'T do this in real configs!
})
];
}).config.system.build.toplevel;
});
# Test that ensures that the nixosSystem function can accept a lib argument
# Note: prefer not to extend or modify `lib`, especially if you want to share reusable modules
# alternatives include: `import` a file, or put a custom library in an option or in `_module.args.<libname>`
nixosSystemAcceptsLib =
(self.lib.nixosSystem {
pkgs = self.legacyPackages.${system};
lib = self.lib.extend (
final: prev: {
ifThisFunctionIsMissingTheTestFails = final.id;
}
);
modules = [
./nixos/modules/profiles/minimal.nix
(
{ lib, ... }:
lib.ifThisFunctionIsMissingTheTestFails {
# Define a minimal config without eval warnings
nixpkgs.hostPlatform = "x86_64-linux";
boot.loader.grub.enable = false;
fileSystems."/".device = "nodev";
# See https://search.nixos.org/options?show=system.stateVersion&query=stateversion
system.stateVersion = lib.trivial.release; # DON'T do this in real configs!
}
)
];
}).config.system.build.toplevel;
}
);
htmlDocs = {
nixpkgsManual = builtins.mapAttrs (_: jobSet: jobSet.manual) jobs;
nixosManual = (import ./nixos/release-small.nix {
nixpkgs = self;
}).nixos.manual;
nixosManual =
(import ./nixos/release-small.nix {
nixpkgs = self;
}).nixos.manual;
};
devShells = forAllSystems (system:
{ } // lib.optionalAttrs
(
# Exclude armv6l-linux because "Package ghc-9.6.6 in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
system != "armv6l-linux"
# Exclude riscv64-linux because "Package ghc-9.6.6 in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
&& system != "riscv64-linux"
# Exclude FreeBSD because "Package ghc-9.6.6 in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
&& !self.legacyPackages.${system}.stdenv.hostPlatform.isFreeBSD
)
{
/** A shell to get tooling for Nixpkgs development. See nixpkgs/shell.nix. */
default = import ./shell.nix { inherit system; };
});
devShells = forAllSystems (
system:
{ }
//
lib.optionalAttrs
(
# Exclude armv6l-linux because "Package ghc-9.6.6 in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
system != "armv6l-linux"
# Exclude riscv64-linux because "Package ghc-9.6.6 in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
&& system != "riscv64-linux"
# Exclude FreeBSD because "Package ghc-9.6.6 in .../pkgs/development/compilers/ghc/common-hadrian.nix:579 is not available on the requested hostPlatform"
&& !self.legacyPackages.${system}.stdenv.hostPlatform.isFreeBSD
)
{
/**
A shell to get tooling for Nixpkgs development. See nixpkgs/shell.nix.
*/
default = import ./shell.nix { inherit system; };
}
);
formatter = forAllSystems (system: (import ./ci { inherit system; }).fmt.pkg);
@ -154,10 +188,13 @@
evaluation. Evaluating the attribute value tends to require a significant
amount of computation, even considering lazy evaluation.
*/
legacyPackages = forAllSystems (system:
(import ./. { inherit system; }).extend (final: prev: {
lib = prev.lib.extend libVersionInfoOverlay;
})
legacyPackages = forAllSystems (
system:
(import ./. { inherit system; }).extend (
final: prev: {
lib = prev.lib.extend libVersionInfoOverlay;
}
)
);
/**
@ -176,7 +213,7 @@
};
};
};
*/
*/
nixosModules = {
notDetected = ./nixos/modules/installer/scan/not-detected.nix;

File diff suppressed because it is too large Load Diff

View File

@ -1,212 +1,566 @@
/* Library of low-level helper functions for nix expressions.
*
* Please implement (mostly) exhaustive unit tests
* for new functions in `./tests.nix`.
*/
/*
Library of low-level helper functions for nix expressions.
Please implement (mostly) exhaustive unit tests
for new functions in `./tests.nix`.
*/
let
# A copy of `lib.makeExtensible'` in order to document `extend`.
# It has been leading to some trouble, so we have to document it specially.
makeExtensible' =
rattrs:
let self = rattrs self // {
/**
Patch the Nixpkgs library
let
self = rattrs self // {
/**
Patch the Nixpkgs library
A function that applies patches onto the nixpkgs library.
Usage is discouraged for most scenarios.
A function that applies patches onto the nixpkgs library.
Usage is discouraged for most scenarios.
:::{.note}
The name `extends` is a bit misleading, as it doesn't actually extend the library, but rather patches it.
It is merely a consequence of being implemented by `makeExtensible`.
:::
:::{.note}
The name `extends` is a bit misleading, as it doesn't actually extend the library, but rather patches it.
It is merely a consequence of being implemented by `makeExtensible`.
:::
# Inputs
# Inputs
- An "extension function" `f` that returns attributes that will be updated in the returned Nixpkgs library.
- An "extension function" `f` that returns attributes that will be updated in the returned Nixpkgs library.
# Output
# Output
A patched Nixpkgs library.
A patched Nixpkgs library.
:::{.warning}
This functionality is intended as an escape hatch for when the provided version of the Nixpkgs library has a flaw.
:::{.warning}
This functionality is intended as an escape hatch for when the provided version of the Nixpkgs library has a flaw.
If you were to use it to add new functionality, you will run into compatibility and interoperability issues.
:::
*/
extend = f: lib.makeExtensible (lib.extends f rattrs);
};
in self;
If you were to use it to add new functionality, you will run into compatibility and interoperability issues.
:::
*/
extend = f: lib.makeExtensible (lib.extends f rattrs);
};
in
self;
lib = makeExtensible' (self: let
callLibs = file: import file { lib = self; };
in {
lib = makeExtensible' (
self:
let
callLibs = file: import file { lib = self; };
in
{
# often used, or depending on very little
trivial = callLibs ./trivial.nix;
fixedPoints = callLibs ./fixed-points.nix;
# often used, or depending on very little
trivial = callLibs ./trivial.nix;
fixedPoints = callLibs ./fixed-points.nix;
# datatypes
attrsets = callLibs ./attrsets.nix;
lists = callLibs ./lists.nix;
strings = callLibs ./strings.nix;
stringsWithDeps = callLibs ./strings-with-deps.nix;
# datatypes
attrsets = callLibs ./attrsets.nix;
lists = callLibs ./lists.nix;
strings = callLibs ./strings.nix;
stringsWithDeps = callLibs ./strings-with-deps.nix;
# packaging
customisation = callLibs ./customisation.nix;
derivations = callLibs ./derivations.nix;
maintainers = import ../maintainers/maintainer-list.nix;
teams = callLibs ../maintainers/team-list.nix;
meta = callLibs ./meta.nix;
versions = callLibs ./versions.nix;
# packaging
customisation = callLibs ./customisation.nix;
derivations = callLibs ./derivations.nix;
maintainers = import ../maintainers/maintainer-list.nix;
teams = callLibs ../maintainers/team-list.nix;
meta = callLibs ./meta.nix;
versions = callLibs ./versions.nix;
# module system
modules = callLibs ./modules.nix;
options = callLibs ./options.nix;
types = callLibs ./types.nix;
# module system
modules = callLibs ./modules.nix;
options = callLibs ./options.nix;
types = callLibs ./types.nix;
# constants
licenses = callLibs ./licenses.nix;
sourceTypes = callLibs ./source-types.nix;
systems = callLibs ./systems;
# constants
licenses = callLibs ./licenses.nix;
sourceTypes = callLibs ./source-types.nix;
systems = callLibs ./systems;
# serialization
cli = callLibs ./cli.nix;
gvariant = callLibs ./gvariant.nix;
generators = callLibs ./generators.nix;
# serialization
cli = callLibs ./cli.nix;
gvariant = callLibs ./gvariant.nix;
generators = callLibs ./generators.nix;
# misc
asserts = callLibs ./asserts.nix;
debug = callLibs ./debug.nix;
misc = callLibs ./deprecated/misc.nix;
# misc
asserts = callLibs ./asserts.nix;
debug = callLibs ./debug.nix;
misc = callLibs ./deprecated/misc.nix;
# domain-specific
fetchers = callLibs ./fetchers.nix;
# domain-specific
fetchers = callLibs ./fetchers.nix;
# Eval-time filesystem handling
path = callLibs ./path;
filesystem = callLibs ./filesystem.nix;
fileset = callLibs ./fileset;
sources = callLibs ./sources.nix;
# Eval-time filesystem handling
path = callLibs ./path;
filesystem = callLibs ./filesystem.nix;
fileset = callLibs ./fileset;
sources = callLibs ./sources.nix;
# back-compat aliases
platforms = self.systems.doubles;
# back-compat aliases
platforms = self.systems.doubles;
# linux kernel configuration
kernel = callLibs ./kernel.nix;
# linux kernel configuration
kernel = callLibs ./kernel.nix;
# network
network = callLibs ./network;
# network
network = callLibs ./network;
# TODO: For consistency, all builtins should also be available from a sub-library;
# these are the only ones that are currently not
inherit (builtins) addErrorContext isPath trace typeOf unsafeGetAttrPos;
inherit (self.trivial) id const pipe concat or and xor bitAnd bitOr bitXor
bitNot boolToString mergeAttrs flip defaultTo mapNullable inNixShell isFloat min max
importJSON importTOML warn warnIf warnIfNot throwIf throwIfNot checkListOfEnum
info showWarnings nixpkgsVersion version isInOldestRelease oldestSupportedReleaseIsAtLeast
mod compare splitByAndCompare seq deepSeq lessThan add sub
functionArgs setFunctionArgs isFunction toFunction mirrorFunctionArgs
fromHexString toHexString toBaseDigits inPureEvalMode isBool isInt pathExists
genericClosure readFile;
inherit (self.fixedPoints) fix fix' converge extends composeExtensions
composeManyExtensions makeExtensible makeExtensibleWithCustomName
toExtension;
inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
getAttrFromPath attrVals attrNames attrValues getAttrs catAttrs filterAttrs
filterAttrsRecursive foldlAttrs foldAttrs collect nameValuePair mapAttrs
mapAttrs' mapAttrsToList attrsToList concatMapAttrs mapAttrsRecursive
mapAttrsRecursiveCond genAttrs isDerivation toDerivation optionalAttrs
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
recursiveUpdate matchAttrs mergeAttrsList overrideExisting showAttrPath getOutput getFirstOutput
getBin getLib getStatic getDev getInclude getMan chooseDevOutputs zipWithNames zip
recurseIntoAttrs dontRecurseIntoAttrs cartesianProduct cartesianProductOfSets
mapCartesianProduct updateManyAttrsByPath listToAttrs hasAttr getAttr isAttrs intersectAttrs removeAttrs;
inherit (self.lists) singleton forEach map foldr fold foldl foldl' imap0 imap1
filter ifilter0 concatMap flatten remove findSingle findFirst any all count
optional optionals toList range replicate partition zipListsWith zipLists
reverseList listDfs toposort sort sortOn naturalSort compareLists
take drop dropEnd sublist last init
crossLists unique allUnique intersectLists
subtractLists mutuallyExclusive groupBy groupBy' concatLists genList
length head tail elem elemAt isList;
inherit (self.strings) concatStrings concatMapStrings concatImapStrings
stringLength substring isString replaceStrings
intersperse concatStringsSep concatMapStringsSep concatMapAttrsStringSep
concatImapStringsSep concatLines makeSearchPath makeSearchPathOutput
makeLibraryPath makeIncludePath makeBinPath optionalString
hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
escapeShellArg escapeShellArgs
isStorePath isStringLike
isValidPosixName toShellVar toShellVars trim trimWith
escapeRegex escapeURL escapeXML replaceChars lowerChars
upperChars toLower toUpper toSentenceCase addContextFrom splitString
removePrefix removeSuffix versionOlder versionAtLeast
getName getVersion match split
cmakeOptionType cmakeBool cmakeFeature
mesonOption mesonBool mesonEnable
nameFromURL enableFeature enableFeatureAs withFeature
withFeatureAs fixedWidthString fixedWidthNumber
toInt toIntBase10 readPathsFromFile fileContents;
inherit (self.stringsWithDeps) textClosureList textClosureMap
noDepEntry fullDepEntry packEntry stringAfter;
inherit (self.customisation) overrideDerivation makeOverridable
callPackageWith callPackagesWith extendDerivation hydraJob
makeScope makeScopeWithSplicing makeScopeWithSplicing'
extendMkDerivation;
inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate;
inherit (self.generators) mkLuaInline;
inherit (self.meta) addMetaAttrs dontDistribute setName updateName
appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
hiPrioSet licensesSpdx getLicenseFromSpdxId getLicenseFromSpdxIdOr
getExe getExe';
inherit (self.filesystem) pathType pathIsDirectory pathIsRegularFile
packagesFromDirectoryRecursive;
inherit (self.sources) cleanSourceFilter
cleanSource sourceByRegex sourceFilesBySuffices
commitIdFromGitRepo cleanSourceWith pathHasContext
canCleanSource pathIsGitRepo;
inherit (self.modules) evalModules setDefaultModuleLocation
unifyModuleSyntax applyModuleArgsIfFunction mergeModules
mergeModules' mergeOptionDecls mergeDefinitions
pushDownProperties dischargeProperties filterOverrides
sortProperties fixupOptionType mkIf mkAssert mkMerge mkOverride
mkOptionDefault mkDefault mkImageMediaOverride mkForce mkVMOverride
mkFixStrictness mkOrder mkBefore mkAfter mkAliasDefinitions
mkAliasAndWrapDefinitions fixMergeModules mkRemovedOptionModule
mkRenamedOptionModule mkRenamedOptionModuleWith
mkMergedOptionModule mkChangedOptionModule
mkAliasOptionModule mkDerivedConfig doRename
mkAliasOptionModuleMD;
evalOptionValue = lib.warn "External use of `lib.evalOptionValue` is deprecated. If your use case isn't covered by non-deprecated functions, we'd like to know more and perhaps support your use case well, instead of providing access to these low level functions. In this case please open an issue in https://github.com/nixos/nixpkgs/issues/." self.modules.evalOptionValue;
inherit (self.options) isOption mkEnableOption mkSinkUndeclaredOptions
mergeDefaultOption mergeOneOption mergeEqualOption mergeUniqueOption
getValues getFiles
optionAttrSetToDocList optionAttrSetToDocList'
scrubOptionValue literalExpression literalExample
showOption showOptionWithDefLocs showFiles
unknownModule mkOption mkPackageOption mkPackageOptionMD
literalMD;
inherit (self.types) isType setType defaultTypeMerge defaultFunctor
isOptionType mkOptionType;
inherit (self.asserts)
assertMsg assertOneOf;
inherit (self.debug) traceIf traceVal traceValFn
traceSeq traceSeqN traceValSeq
traceValSeqFn traceValSeqN traceValSeqNFn traceFnSeqN
runTests testAllTrue;
inherit (self.misc) maybeEnv defaultMergeArg defaultMerge foldArgs
maybeAttrNullable maybeAttr ifEnable checkFlag getValue
checkReqs uniqList uniqListExt condConcat lazyGenericClosure
innerModifySumArgs modifySumArgs innerClosePropagation
closePropagation mapAttrsFlatten nvs setAttr setAttrMerge
mergeAttrsWithFunc mergeAttrsConcatenateValues
mergeAttrsNoOverride mergeAttrByFunc mergeAttrsByFuncDefaults
mergeAttrsByFuncDefaultsClean mergeAttrBy
fakeHash fakeSha256 fakeSha512
nixType imap;
inherit (self.versions)
splitVersion;
});
in lib
# TODO: For consistency, all builtins should also be available from a sub-library;
# these are the only ones that are currently not
inherit (builtins)
addErrorContext
isPath
trace
typeOf
unsafeGetAttrPos
;
inherit (self.trivial)
id
const
pipe
concat
or
and
xor
bitAnd
bitOr
bitXor
bitNot
boolToString
mergeAttrs
flip
defaultTo
mapNullable
inNixShell
isFloat
min
max
importJSON
importTOML
warn
warnIf
warnIfNot
throwIf
throwIfNot
checkListOfEnum
info
showWarnings
nixpkgsVersion
version
isInOldestRelease
oldestSupportedReleaseIsAtLeast
mod
compare
splitByAndCompare
seq
deepSeq
lessThan
add
sub
functionArgs
setFunctionArgs
isFunction
toFunction
mirrorFunctionArgs
fromHexString
toHexString
toBaseDigits
inPureEvalMode
isBool
isInt
pathExists
genericClosure
readFile
;
inherit (self.fixedPoints)
fix
fix'
converge
extends
composeExtensions
composeManyExtensions
makeExtensible
makeExtensibleWithCustomName
toExtension
;
inherit (self.attrsets)
attrByPath
hasAttrByPath
setAttrByPath
getAttrFromPath
attrVals
attrNames
attrValues
getAttrs
catAttrs
filterAttrs
filterAttrsRecursive
foldlAttrs
foldAttrs
collect
nameValuePair
mapAttrs
mapAttrs'
mapAttrsToList
attrsToList
concatMapAttrs
mapAttrsRecursive
mapAttrsRecursiveCond
genAttrs
isDerivation
toDerivation
optionalAttrs
zipAttrsWithNames
zipAttrsWith
zipAttrs
recursiveUpdateUntil
recursiveUpdate
matchAttrs
mergeAttrsList
overrideExisting
showAttrPath
getOutput
getFirstOutput
getBin
getLib
getStatic
getDev
getInclude
getMan
chooseDevOutputs
zipWithNames
zip
recurseIntoAttrs
dontRecurseIntoAttrs
cartesianProduct
cartesianProductOfSets
mapCartesianProduct
updateManyAttrsByPath
listToAttrs
hasAttr
getAttr
isAttrs
intersectAttrs
removeAttrs
;
inherit (self.lists)
singleton
forEach
map
foldr
fold
foldl
foldl'
imap0
imap1
filter
ifilter0
concatMap
flatten
remove
findSingle
findFirst
any
all
count
optional
optionals
toList
range
replicate
partition
zipListsWith
zipLists
reverseList
listDfs
toposort
sort
sortOn
naturalSort
compareLists
take
drop
dropEnd
sublist
last
init
crossLists
unique
allUnique
intersectLists
subtractLists
mutuallyExclusive
groupBy
groupBy'
concatLists
genList
length
head
tail
elem
elemAt
isList
;
inherit (self.strings)
concatStrings
concatMapStrings
concatImapStrings
stringLength
substring
isString
replaceStrings
intersperse
concatStringsSep
concatMapStringsSep
concatMapAttrsStringSep
concatImapStringsSep
concatLines
makeSearchPath
makeSearchPathOutput
makeLibraryPath
makeIncludePath
makeBinPath
optionalString
hasInfix
hasPrefix
hasSuffix
stringToCharacters
stringAsChars
escape
escapeShellArg
escapeShellArgs
isStorePath
isStringLike
isValidPosixName
toShellVar
toShellVars
trim
trimWith
escapeRegex
escapeURL
escapeXML
replaceChars
lowerChars
upperChars
toLower
toUpper
toSentenceCase
addContextFrom
splitString
removePrefix
removeSuffix
versionOlder
versionAtLeast
getName
getVersion
match
split
cmakeOptionType
cmakeBool
cmakeFeature
mesonOption
mesonBool
mesonEnable
nameFromURL
enableFeature
enableFeatureAs
withFeature
withFeatureAs
fixedWidthString
fixedWidthNumber
toInt
toIntBase10
readPathsFromFile
fileContents
;
inherit (self.stringsWithDeps)
textClosureList
textClosureMap
noDepEntry
fullDepEntry
packEntry
stringAfter
;
inherit (self.customisation)
overrideDerivation
makeOverridable
callPackageWith
callPackagesWith
extendDerivation
hydraJob
makeScope
makeScopeWithSplicing
makeScopeWithSplicing'
extendMkDerivation
;
inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate;
inherit (self.generators) mkLuaInline;
inherit (self.meta)
addMetaAttrs
dontDistribute
setName
updateName
appendToName
mapDerivationAttrset
setPrio
lowPrio
lowPrioSet
hiPrio
hiPrioSet
licensesSpdx
getLicenseFromSpdxId
getLicenseFromSpdxIdOr
getExe
getExe'
;
inherit (self.filesystem)
pathType
pathIsDirectory
pathIsRegularFile
packagesFromDirectoryRecursive
;
inherit (self.sources)
cleanSourceFilter
cleanSource
sourceByRegex
sourceFilesBySuffices
commitIdFromGitRepo
cleanSourceWith
pathHasContext
canCleanSource
pathIsGitRepo
;
inherit (self.modules)
evalModules
setDefaultModuleLocation
unifyModuleSyntax
applyModuleArgsIfFunction
mergeModules
mergeModules'
mergeOptionDecls
mergeDefinitions
pushDownProperties
dischargeProperties
filterOverrides
sortProperties
fixupOptionType
mkIf
mkAssert
mkMerge
mkOverride
mkOptionDefault
mkDefault
mkImageMediaOverride
mkForce
mkVMOverride
mkFixStrictness
mkOrder
mkBefore
mkAfter
mkAliasDefinitions
mkAliasAndWrapDefinitions
fixMergeModules
mkRemovedOptionModule
mkRenamedOptionModule
mkRenamedOptionModuleWith
mkMergedOptionModule
mkChangedOptionModule
mkAliasOptionModule
mkDerivedConfig
doRename
mkAliasOptionModuleMD
;
evalOptionValue = lib.warn "External use of `lib.evalOptionValue` is deprecated. If your use case isn't covered by non-deprecated functions, we'd like to know more and perhaps support your use case well, instead of providing access to these low level functions. In this case please open an issue in https://github.com/nixos/nixpkgs/issues/." self.modules.evalOptionValue;
inherit (self.options)
isOption
mkEnableOption
mkSinkUndeclaredOptions
mergeDefaultOption
mergeOneOption
mergeEqualOption
mergeUniqueOption
getValues
getFiles
optionAttrSetToDocList
optionAttrSetToDocList'
scrubOptionValue
literalExpression
literalExample
showOption
showOptionWithDefLocs
showFiles
unknownModule
mkOption
mkPackageOption
mkPackageOptionMD
literalMD
;
inherit (self.types)
isType
setType
defaultTypeMerge
defaultFunctor
isOptionType
mkOptionType
;
inherit (self.asserts)
assertMsg
assertOneOf
;
inherit (self.debug)
traceIf
traceVal
traceValFn
traceSeq
traceSeqN
traceValSeq
traceValSeqFn
traceValSeqN
traceValSeqNFn
traceFnSeqN
runTests
testAllTrue
;
inherit (self.misc)
maybeEnv
defaultMergeArg
defaultMerge
foldArgs
maybeAttrNullable
maybeAttr
ifEnable
checkFlag
getValue
checkReqs
uniqList
uniqListExt
condConcat
lazyGenericClosure
innerModifySumArgs
modifySumArgs
innerClosePropagation
closePropagation
mapAttrsFlatten
nvs
setAttr
setAttrMerge
mergeAttrsWithFunc
mergeAttrsConcatenateValues
mergeAttrsNoOverride
mergeAttrByFunc
mergeAttrsByFuncDefaults
mergeAttrsByFuncDefaultsClean
mergeAttrBy
fakeHash
fakeSha256
fakeSha512
nixType
imap
;
inherit (self.versions)
splitVersion
;
}
);
in
lib

View File

@ -35,153 +35,212 @@ let
inherit (lib.attrsets) removeAttrs mapAttrsToList;
# returns default if env var is not set
maybeEnv = name: default:
let value = builtins.getEnv name; in
maybeEnv =
name: default:
let
value = builtins.getEnv name;
in
if value == "" then default else value;
defaultMergeArg = x : y: if builtins.isAttrs y then
y
else
(y x);
defaultMergeArg = x: y: if builtins.isAttrs y then y else (y x);
defaultMerge = x: y: x // (defaultMergeArg x y);
foldArgs = merger: f: init: x:
let arg = (merger init (defaultMergeArg init x));
# now add the function with composed args already applied to the final attrs
base = (setAttrMerge "passthru" {} (f arg)
( z: z // {
function = foldArgs merger f arg;
args = (attrByPath ["passthru" "args"] {} z) // x;
} ));
withStdOverrides = base // {
override = base.passthru.function;
};
in
withStdOverrides;
foldArgs =
merger: f: init: x:
let
arg = (merger init (defaultMergeArg init x));
# now add the function with composed args already applied to the final attrs
base = (
setAttrMerge "passthru" { } (f arg) (
z:
z
// {
function = foldArgs merger f arg;
args = (attrByPath [ "passthru" "args" ] { } z) // x;
}
)
);
withStdOverrides = base // {
override = base.passthru.function;
};
in
withStdOverrides;
# shortcut for attrByPath ["name"] default attrs
maybeAttrNullable = maybeAttr;
# shortcut for attrByPath ["name"] default attrs
maybeAttr = name: default: attrs: attrs.${name} or default;
maybeAttr =
name: default: attrs:
attrs.${name} or default;
# Return the second argument if the first one is true or the empty version
# of the second argument.
ifEnable = cond: val:
if cond then val
else if builtins.isList val then []
else if builtins.isAttrs val then {}
ifEnable =
cond: val:
if cond then
val
else if builtins.isList val then
[ ]
else if builtins.isAttrs val then
{ }
# else if builtins.isString val then ""
else if val == true || val == false then false
else null;
else if val == true || val == false then
false
else
null;
# Return true only if there is an attribute and it is true.
checkFlag = attrSet: name:
if name == "true" then true else
if name == "false" then false else
if (elem name (attrByPath ["flags"] [] attrSet)) then true else
attrByPath [name] false attrSet ;
checkFlag =
attrSet: name:
if name == "true" then
true
else if name == "false" then
false
else if (elem name (attrByPath [ "flags" ] [ ] attrSet)) then
true
else
attrByPath [ name ] false attrSet;
# Input : attrSet, [ [name default] ... ], name
# Output : its value or default.
getValue = attrSet: argList: name:
( attrByPath [name] (if checkFlag attrSet name then true else
if argList == [] then null else
let x = builtins.head argList; in
if (head x) == name then
(head (tail x))
else (getValue attrSet
(tail argList) name)) attrSet );
getValue =
attrSet: argList: name:
(attrByPath [ name ] (
if checkFlag attrSet name then
true
else if argList == [ ] then
null
else
let
x = builtins.head argList;
in
if (head x) == name then (head (tail x)) else (getValue attrSet (tail argList) name)
) attrSet);
# Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
# Output : are reqs satisfied? It's asserted.
checkReqs = attrSet: argList: condList:
(
foldr and true
(map (x: let name = (head x); in
((checkFlag attrSet name) ->
(foldr and true
(map (y: let val=(getValue attrSet argList y); in
(val!=null) && (val!=false))
(tail x))))) condList));
checkReqs =
attrSet: argList: condList:
(foldr and true (
map (
x:
let
name = (head x);
in
(
(checkFlag attrSet name)
-> (foldr and true (
map (
y:
let
val = (getValue attrSet argList y);
in
(val != null) && (val != false)
) (tail x)
))
)
) condList
));
# This function has O(n^2) performance.
uniqList = { inputList, acc ? [] }:
let go = xs: acc:
if xs == []
then []
else let x = head xs;
y = if elem x acc then [] else [x];
in y ++ go (tail xs) (y ++ acc);
in go inputList acc;
uniqListExt = { inputList,
outputList ? [],
getter ? (x: x),
compare ? (x: y: x==y) }:
if inputList == [] then outputList else
let x = head inputList;
isX = y: (compare (getter y) (getter x));
newOutputList = outputList ++
(if any isX outputList then [] else [x]);
in uniqListExt { outputList = newOutputList;
inputList = (tail inputList);
inherit getter compare;
};
condConcat = name: list: checker:
if list == [] then name else
if checker (head list) then
condConcat
(name + (head (tail list)))
(tail (tail list))
checker
else condConcat
name (tail (tail list)) checker;
lazyGenericClosure = {startSet, operator}:
uniqList =
{
inputList,
acc ? [ ],
}:
let
work = list: doneKeys: result:
if list == [] then
go =
xs: acc:
if xs == [ ] then
[ ]
else
let
x = head xs;
y = if elem x acc then [ ] else [ x ];
in
y ++ go (tail xs) (y ++ acc);
in
go inputList acc;
uniqListExt =
{
inputList,
outputList ? [ ],
getter ? (x: x),
compare ? (x: y: x == y),
}:
if inputList == [ ] then
outputList
else
let
x = head inputList;
isX = y: (compare (getter y) (getter x));
newOutputList = outputList ++ (if any isX outputList then [ ] else [ x ]);
in
uniqListExt {
outputList = newOutputList;
inputList = (tail inputList);
inherit getter compare;
};
condConcat =
name: list: checker:
if list == [ ] then
name
else if checker (head list) then
condConcat (name + (head (tail list))) (tail (tail list)) checker
else
condConcat name (tail (tail list)) checker;
lazyGenericClosure =
{ startSet, operator }:
let
work =
list: doneKeys: result:
if list == [ ] then
result
else
let x = head list; key = x.key; in
let
x = head list;
key = x.key;
in
if elem key doneKeys then
work (tail list) doneKeys result
else
work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result);
work (tail list ++ operator x) ([ key ] ++ doneKeys) ([ x ] ++ result);
in
work startSet [] [];
work startSet [ ] [ ];
innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
innerModifySumArgs f x (a // b);
modifySumArgs = f: x: innerModifySumArgs f x {};
innerModifySumArgs =
f: x: a: b:
if b == null then (f a b) // x else innerModifySumArgs f x (a // b);
modifySumArgs = f: x: innerModifySumArgs f x { };
innerClosePropagation =
acc: xs:
if xs == [ ] then
acc
else
let
y = head xs;
ys = tail xs;
in
if !isAttrs y then
innerClosePropagation acc ys
else
let
acc' = [ y ] ++ acc;
in
innerClosePropagation acc' (uniqList {
inputList =
(maybeAttrNullable "propagatedBuildInputs" [ ] y)
++ (maybeAttrNullable "propagatedNativeBuildInputs" [ ] y)
++ ys;
acc = acc';
});
innerClosePropagation = acc: xs:
if xs == []
then acc
else let y = head xs;
ys = tail xs;
in if ! isAttrs y
then innerClosePropagation acc ys
else let acc' = [y] ++ acc;
in innerClosePropagation
acc'
(uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y)
++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y)
++ ys;
acc = acc';
}
);
closePropagationSlow = list: (uniqList {inputList = (innerClosePropagation [] list);});
closePropagationSlow = list: (uniqList { inputList = (innerClosePropagation [ ] list); });
# This is an optimisation of closePropagation which avoids the O(n^2) behavior
# Using a list of derivations, it generates the full closure of the propagatedXXXBuildInputs
@ -189,28 +248,35 @@ let
# attribute of each derivation.
# On some benchmarks, it performs up to 15 times faster than closePropagation.
# See https://github.com/NixOS/nixpkgs/pull/194391 for details.
closePropagationFast = list:
builtins.map (x: x.val) (builtins.genericClosure {
startSet = builtins.map (x: {
key = x.outPath;
val = x;
}) (builtins.filter (x: x != null) list);
operator = item:
if !builtins.isAttrs item.val then
[ ]
else
builtins.concatMap (x:
if x != null then [{
key = x.outPath;
val = x;
}] else
[ ]) ((item.val.propagatedBuildInputs or [ ])
++ (item.val.propagatedNativeBuildInputs or [ ]));
});
closePropagationFast =
list:
builtins.map (x: x.val) (
builtins.genericClosure {
startSet = builtins.map (x: {
key = x.outPath;
val = x;
}) (builtins.filter (x: x != null) list);
operator =
item:
if !builtins.isAttrs item.val then
[ ]
else
builtins.concatMap (
x:
if x != null then
[
{
key = x.outPath;
val = x;
}
]
else
[ ]
) ((item.val.propagatedBuildInputs or [ ]) ++ (item.val.propagatedNativeBuildInputs or [ ]));
}
);
closePropagation = if builtins ? genericClosure
then closePropagationFast
else closePropagationSlow;
closePropagation = if builtins ? genericClosure then closePropagationFast else closePropagationSlow;
# calls a function (f attr value ) for each record item. returns a list
mapAttrsFlatten = warn "lib.misc.mapAttrsFlatten is deprecated, please use lib.attrsets.mapAttrsToList instead." mapAttrsToList;
@ -218,26 +284,29 @@ let
# attribute set containing one attribute
nvs = name: value: listToAttrs [ (nameValuePair name value) ];
# adds / replaces an attribute of an attribute set
setAttr = set: name: v: set // (nvs name v);
setAttr =
set: name: v:
set // (nvs name v);
# setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name)
# setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; }
# setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; }
setAttrMerge = name: default: attrs: f:
setAttrMerge =
name: default: attrs: f:
setAttr attrs name (f (maybeAttr name default attrs));
# Using f = a: b = b the result is similar to //
# merge attributes with custom function handling the case that the attribute
# exists in both sets
mergeAttrsWithFunc = f: set1: set2:
foldr (n: set: if set ? ${n}
then setAttr set n (f set.${n} set2.${n})
else set )
(set2 // set1) (attrNames set2);
mergeAttrsWithFunc =
f: set1: set2:
foldr (n: set: if set ? ${n} then setAttr set n (f set.${n} set2.${n}) else set) (set2 // set1) (
attrNames set2
);
# merging two attribute set concatenating the values of same attribute names
# eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) );
mergeAttrsConcatenateValues = mergeAttrsWithFunc (a: b: (toList a) ++ (toList b));
# merges attributes using //, if a name exists in both attributes
# an error will be triggered unless its listed in mergeLists
@ -246,20 +315,31 @@ let
# merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs?
# in these cases the first buildPhase will override the second one
# ! deprecated, use mergeAttrByFunc instead
mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
overrideSnd ? [ "buildPhase" ]
}: attrs1: attrs2:
foldr (n: set:
setAttr set n ( if set ? ${n}
then # merge
if elem n mergeLists # attribute contains list, merge them by concatenating
then attrs2.${n} ++ attrs1.${n}
else if elem n overrideSnd
then attrs1.${n}
else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
else attrs2.${n} # add attribute not existing in attr1
)) attrs1 (attrNames attrs2);
mergeAttrsNoOverride =
{
mergeLists ? [
"buildInputs"
"propagatedBuildInputs"
],
overrideSnd ? [ "buildPhase" ],
}:
attrs1: attrs2:
foldr (
n: set:
setAttr set n (
if set ? ${n} then # merge
if
elem n mergeLists # attribute contains list, merge them by concatenating
then
attrs2.${n} ++ attrs1.${n}
else if elem n overrideSnd then
attrs1.${n}
else
throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
else
attrs2.${n} # add attribute not existing in attr1
)
) attrs1 (attrNames attrs2);
# example usage:
# mergeAttrByFunc {
@ -272,48 +352,82 @@ let
# { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
# is used by defaultOverridableDelayableArgs and can be used when composing using
# foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
mergeAttrByFunc = x: y:
mergeAttrByFunc =
x: y:
let
mergeAttrBy2 = { mergeAttrBy = mergeAttrs; }
// (maybeAttr "mergeAttrBy" {} x)
// (maybeAttr "mergeAttrBy" {} y); in
foldr mergeAttrs {} [
x y
(mapAttrs ( a: v: # merge special names using given functions
if x ? ${a}
then if y ? ${a}
then v x.${a} y.${a} # both have attr, use merge func
else x.${a} # only x has attr
else y.${a} # only y has attr)
) (removeAttrs mergeAttrBy2
# don't merge attrs which are neither in x nor y
(filter (a: ! x ? ${a} && ! y ? ${a})
(attrNames mergeAttrBy2))
)
mergeAttrBy2 =
{ mergeAttrBy = mergeAttrs; } // (maybeAttr "mergeAttrBy" { } x) // (maybeAttr "mergeAttrBy" { } y);
in
foldr mergeAttrs { } [
x
y
(mapAttrs
(
a: v: # merge special names using given functions
if x ? ${a} then
if y ? ${a} then
v x.${a} y.${a} # both have attr, use merge func
else
x.${a} # only x has attr
else
y.${a} # only y has attr)
)
(
removeAttrs mergeAttrBy2
# don't merge attrs which are neither in x nor y
(filter (a: !x ? ${a} && !y ? ${a}) (attrNames mergeAttrBy2))
)
)
];
mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; };
mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"];
mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) [ "mergeAttrBy" ];
# sane defaults (same name as attr name so that inherit can be used)
mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
listToAttrs (map (n: nameValuePair n concat)
[ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
// listToAttrs (map (n: nameValuePair n mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
// listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
;
listToAttrs (
map (n: nameValuePair n concat) [
"nativeBuildInputs"
"buildInputs"
"propagatedBuildInputs"
"configureFlags"
"prePhases"
"postAll"
"patches"
]
)
// listToAttrs (
map (n: nameValuePair n mergeAttrs) [
"passthru"
"meta"
"cfg"
"flags"
]
)
// listToAttrs (
map (n: nameValuePair n (a: b: "${a}\n${b}")) [
"preConfigure"
"postInstall"
]
);
nixType = x:
if isAttrs x then
if x ? outPath then "derivation"
else "attrs"
else if isFunction x then "function"
else if isList x then "list"
else if x == true then "bool"
else if x == false then "bool"
else if x == null then "null"
else if isInt x then "int"
else "string";
nixType =
x:
if isAttrs x then
if x ? outPath then "derivation" else "attrs"
else if isFunction x then
"function"
else if isList x then
"list"
else if x == true then
"bool"
else if x == false then
"bool"
else if x == null then
"null"
else if isInt x then
"int"
else
"string";
/**
# Deprecated

View File

@ -59,23 +59,26 @@ in
pathType =
builtins.readFileType or
# Nix <2.14 compatibility shim
(path:
if ! pathExists path
(
path:
if
!pathExists path
# Fail irrecoverably to mimic the historic behavior of this function and
# the new builtins.readFileType
then abort "lib.filesystem.pathType: Path ${toString path} does not exist."
then
abort "lib.filesystem.pathType: Path ${toString path} does not exist."
# The filesystem root is the only path where `dirOf / == /` and
# `baseNameOf /` is not valid. We can detect this and directly return
# "directory", since we know the filesystem root can't be anything else.
else if dirOf path == path
then "directory"
else (readDir (dirOf path)).${baseNameOf path}
else if dirOf path == path then
"directory"
else
(readDir (dirOf path)).${baseNameOf path}
);
/**
Whether a path exists and is a directory.
# Inputs
`path`
@ -105,13 +108,11 @@ in
:::
*/
pathIsDirectory = path:
pathExists path && pathType path == "directory";
pathIsDirectory = path: pathExists path && pathType path == "directory";
/**
Whether a path exists and is a regular file, meaning not a symlink or any other special file type.
# Inputs
`path`
@ -141,15 +142,13 @@ in
:::
*/
pathIsRegularFile = path:
pathExists path && pathType path == "regular";
pathIsRegularFile = path: pathExists path && pathType path == "regular";
/**
A map of all haskell packages defined in the given path,
identified by having a cabal file with the same name as the
directory itself.
# Inputs
`root`
@ -164,25 +163,25 @@ in
*/
haskellPathsInDir =
root:
let # Files in the root
root-files = builtins.attrNames (builtins.readDir root);
# Files with their full paths
root-files-with-paths =
map (file:
{ name = file; value = root + "/${file}"; }
) root-files;
# Subdirectories of the root with a cabal file.
cabal-subdirs =
builtins.filter ({ name, value }:
builtins.pathExists (value + "/${name}.cabal")
) root-files-with-paths;
in builtins.listToAttrs cabal-subdirs;
let
# Files in the root
root-files = builtins.attrNames (builtins.readDir root);
# Files with their full paths
root-files-with-paths = map (file: {
name = file;
value = root + "/${file}";
}) root-files;
# Subdirectories of the root with a cabal file.
cabal-subdirs = builtins.filter (
{ name, value }: builtins.pathExists (value + "/${name}.cabal")
) root-files-with-paths;
in
builtins.listToAttrs cabal-subdirs;
/**
Find the first directory containing a file matching 'pattern'
upward from a given 'file'.
Returns 'null' if no directories contain a file matching 'pattern'.
# Inputs
`pattern`
@ -200,30 +199,33 @@ in
```
*/
locateDominatingFile =
pattern:
file:
let go = path:
let files = builtins.attrNames (builtins.readDir path);
matches = builtins.filter (match: match != null)
(map (builtins.match pattern) files);
in
if builtins.length matches != 0
then { inherit path matches; }
else if path == /.
then null
else go (dirOf path);
parent = dirOf file;
isDir =
let base = baseNameOf file;
type = (builtins.readDir parent).${base} or null;
in file == /. || type == "directory";
in go (if isDir then file else parent);
pattern: file:
let
go =
path:
let
files = builtins.attrNames (builtins.readDir path);
matches = builtins.filter (match: match != null) (map (builtins.match pattern) files);
in
if builtins.length matches != 0 then
{ inherit path matches; }
else if path == /. then
null
else
go (dirOf path);
parent = dirOf file;
isDir =
let
base = baseNameOf file;
type = (builtins.readDir parent).${base} or null;
in
file == /. || type == "directory";
in
go (if isDir then file else parent);
/**
Given a directory, return a flattened list of all files within it recursively.
# Inputs
`dir`
@ -238,12 +240,15 @@ in
*/
listFilesRecursive =
dir:
lib.flatten (lib.mapAttrsToList (name: type:
if type == "directory" then
lib.filesystem.listFilesRecursive (dir + "/${name}")
else
dir + "/${name}"
) (builtins.readDir dir));
lib.flatten (
lib.mapAttrsToList (
name: type:
if type == "directory" then
lib.filesystem.listFilesRecursive (dir + "/${name}")
else
dir + "/${name}"
) (builtins.readDir dir)
);
/**
Transform a directory tree containing package files suitable for
@ -373,29 +378,49 @@ in
*/
packagesFromDirectoryRecursive =
let
inherit (lib) concatMapAttrs id makeScope recurseIntoAttrs removeSuffix;
inherit (lib)
concatMapAttrs
id
makeScope
recurseIntoAttrs
removeSuffix
;
inherit (lib.path) append;
# Generate an attrset corresponding to a given directory.
# This function is outside `packagesFromDirectoryRecursive`'s lambda expression,
# to prevent accidentally using its parameters.
processDir = { callPackage, directory, ... }@args:
concatMapAttrs (name: type:
processDir =
{ callPackage, directory, ... }@args:
concatMapAttrs (
name: type:
# for each directory entry
let path = append directory name; in
if type == "directory" then {
# recurse into directories
"${name}" = packagesFromDirectoryRecursive (args // {
directory = path;
});
} else if type == "regular" && hasSuffix ".nix" name then {
# call .nix files
"${removeSuffix ".nix" name}" = callPackage path {};
} else if type == "regular" then {
# ignore non-nix files
} else throw ''
lib.filesystem.packagesFromDirectoryRecursive: Unsupported file type ${type} at path ${toString path}
''
let
path = append directory name;
in
if type == "directory" then
{
# recurse into directories
"${name}" = packagesFromDirectoryRecursive (
args
// {
directory = path;
}
);
}
else if type == "regular" && hasSuffix ".nix" name then
{
# call .nix files
"${removeSuffix ".nix" name}" = callPackage path { };
}
else if type == "regular" then
{
# ignore non-nix files
}
else
throw ''
lib.filesystem.packagesFromDirectoryRecursive: Unsupported file type ${type} at path ${toString path}
''
) (builtins.readDir directory);
in
{
@ -408,20 +433,25 @@ in
in
if pathExists defaultPath then
# if `${directory}/package.nix` exists, call it directly
callPackage defaultPath {}
callPackage defaultPath { }
else if args ? newScope then
# Create a new scope and mark it `recurseForDerivations`.
# This lets the packages refer to each other.
# See:
# [lib.makeScope](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope) and
# [lib.recurseIntoAttrs](https://nixos.org/manual/nixpkgs/unstable/#function-library-lib.customisation.makeScope)
recurseIntoAttrs (makeScope newScope (self:
# generate the attrset representing the directory, using the new scope's `callPackage` and `newScope`
processDir (args // {
inherit (self) callPackage newScope;
})
))
recurseIntoAttrs (
makeScope newScope (
self:
# generate the attrset representing the directory, using the new scope's `callPackage` and `newScope`
processDir (
args
// {
inherit (self) callPackage newScope;
}
)
)
)
else
processDir args
;
processDir args;
}

View File

@ -35,7 +35,7 @@ let
filter
flatten
foldl
functionArgs # Note: not the builtin; considers `__functor` in attrsets.
functionArgs # Note: not the builtin; considers `__functor` in attrsets.
gvariant
hasInfix
head
@ -45,7 +45,7 @@ let
isBool
isDerivation
isFloat
isFunction # Note: not the builtin; considers `__functor` in attrsets.
isFunction # Note: not the builtin; considers `__functor` in attrsets.
isInt
isList
isPath
@ -74,7 +74,8 @@ let
;
## -- HELPER FUNCTIONS & DEFAULTS --
in rec {
in
rec {
/**
Convert a value to a sensible default string representation.
The builtin `toString` function has some strange defaults,
@ -88,32 +89,44 @@ in rec {
`v`
: 2\. Function argument
*/
mkValueStringDefault = {}: v:
let err = t: v: abort
("generators.mkValueStringDefault: " +
"${t} not supported: ${toPretty {} v}");
in if isInt v then toString v
mkValueStringDefault =
{ }:
v:
let
err = t: v: abort ("generators.mkValueStringDefault: " + "${t} not supported: ${toPretty { } v}");
in
if isInt v then
toString v
# convert derivations to store paths
else if isDerivation v then toString v
else if isDerivation v then
toString v
# we default to not quoting strings
else if isString v then v
else if isString v then
v
# isString returns "1", which is not a good default
else if true == v then "true"
else if true == v then
"true"
# here it returns to "", which is even less of a good default
else if false == v then "false"
else if null == v then "null"
else if false == v then
"false"
else if null == v then
"null"
# if you have lists you probably want to replace this
else if isList v then err "lists" v
else if isList v then
err "lists" v
# same as for lists, might want to replace
else if isAttrs v then err "attrsets" v
else if isAttrs v then
err "attrsets" v
# functions cant be printed of course
else if isFunction v then err "functions" v
else if isFunction v then
err "functions" v
# Floats currently can't be converted to precise strings,
# condition warning on nix version once this isn't a problem anymore
# See https://github.com/NixOS/nix/pull/3480
else if isFloat v then floatToString v
else err "this value is" (toString v);
else if isFloat v then
floatToString v
else
err "this value is" (toString v);
/**
Generate a line of key k and value v, separated by
@ -145,15 +158,15 @@ in rec {
: 4\. Function argument
*/
mkKeyValueDefault = {
mkValueString ? mkValueStringDefault {}
}: sep: k: v:
"${escape [sep] k}${sep}${mkValueString v}";
mkKeyValueDefault =
{
mkValueString ? mkValueStringDefault { },
}:
sep: k: v:
"${escape [ sep ] k}${sep}${mkValueString v}";
## -- FILE FORMAT GENERATORS --
/**
Generate a key-value-style config file from an attrset.
@ -169,19 +182,22 @@ in rec {
: indent (optional, default: `""`)
: Initial indentation level
*/
toKeyValue = {
mkKeyValue ? mkKeyValueDefault {} "=",
listsAsDuplicateKeys ? false,
indent ? ""
}:
let mkLine = k: v: indent + mkKeyValue k v + "\n";
mkLines = if listsAsDuplicateKeys
then k: v: map (mkLine k) (if isList v then v else [v])
else k: v: [ (mkLine k v) ];
in attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
toKeyValue =
{
mkKeyValue ? mkKeyValueDefault { } "=",
listsAsDuplicateKeys ? false,
indent ? "",
}:
let
mkLine = k: v: indent + mkKeyValue k v + "\n";
mkLines =
if listsAsDuplicateKeys then
k: v: map (mkLine k) (if isList v then v else [ v ])
else
k: v: [ (mkLine k v) ];
in
attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
/**
Generate an INI-style config file from an
@ -225,22 +241,27 @@ in rec {
:::
*/
toINI = {
mkSectionName ? (name: escape [ "[" "]" ] name),
mkKeyValue ? mkKeyValueDefault {} "=",
listsAsDuplicateKeys ? false
}: attrsOfAttrs:
toINI =
{
mkSectionName ? (name: escape [ "[" "]" ] name),
mkKeyValue ? mkKeyValueDefault { } "=",
listsAsDuplicateKeys ? false,
}:
attrsOfAttrs:
let
# map function to string for each key val
mapAttrsToStringsSep = sep: mapFn: attrs:
concatStringsSep sep
(mapAttrsToList mapFn attrs);
mkSection = sectName: sectValues: ''
# map function to string for each key val
mapAttrsToStringsSep =
sep: mapFn: attrs:
concatStringsSep sep (mapAttrsToList mapFn attrs);
mkSection =
sectName: sectValues:
''
[${mkSectionName sectName}]
'' + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
''
+ toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
in
# map input to ini sections
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
# map input to ini sections
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
/**
Generate an INI-style config file from an attrset
@ -303,15 +324,22 @@ in rec {
`generators.toINI` directly, which only takes
the part in `sections`.
*/
toINIWithGlobalSection = {
mkSectionName ? (name: escape [ "[" "]" ] name),
mkKeyValue ? mkKeyValueDefault {} "=",
listsAsDuplicateKeys ? false
}: { globalSection, sections ? {} }:
( if globalSection == {}
then ""
else (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection)
+ "\n")
toINIWithGlobalSection =
{
mkSectionName ? (name: escape [ "[" "]" ] name),
mkKeyValue ? mkKeyValueDefault { } "=",
listsAsDuplicateKeys ? false,
}:
{
globalSection,
sections ? { },
}:
(
if globalSection == { } then
""
else
(toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection) + "\n"
)
+ (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
/**
@ -349,50 +377,57 @@ in rec {
: Key-value pairs to be converted to a git-config file.
See: https://git-scm.com/docs/git-config#_variables for possible values.
*/
toGitINI = attrs:
toGitINI =
attrs:
let
mkSectionName = name:
mkSectionName =
name:
let
containsQuote = hasInfix ''"'' name;
sections = splitString "." name;
section = head sections;
subsections = tail sections;
subsection = concatStringsSep "." subsections;
in if containsQuote || subsections == [ ] then
name
else
''${section} "${subsection}"'';
in
if containsQuote || subsections == [ ] then name else ''${section} "${subsection}"'';
mkValueString = v:
mkValueString =
v:
let
escapedV = ''
"${
replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v
}"'';
in mkValueStringDefault { } (if isString v then escapedV else v);
escapedV = ''"${replaceStrings [ "\n" " " ''"'' "\\" ] [ "\\n" "\\t" ''\"'' "\\\\" ] v}"'';
in
mkValueStringDefault { } (if isString v then escapedV else v);
# generation for multiple ini values
mkKeyValue = k: v:
let mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList v));
mkKeyValue =
k: v:
let
mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
in
concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList v));
# converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI
gitFlattenAttrs = let
recurse = path: value:
if isAttrs value && !isDerivation value then
mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
else if length path > 1 then {
${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
} else {
${head path} = value;
};
in attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
gitFlattenAttrs =
let
recurse =
path: value:
if isAttrs value && !isDerivation value then
mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
else if length path > 1 then
{
${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
}
else
{
${head path} = value;
};
in
attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
toINI_ = toINI { inherit mkKeyValue mkSectionName; };
in
toINI_ (gitFlattenAttrs attrs);
toINI_ (gitFlattenAttrs attrs);
/**
mkKeyValueDefault wrapper that handles dconf INI quirks.
@ -427,35 +462,39 @@ in rec {
withRecursion =
{
depthLimit,
throwOnDepthLimit ? true
throwOnDepthLimit ? true,
}:
assert isInt depthLimit;
let
specialAttrs = [
"__functor"
"__functionArgs"
"__toString"
"__pretty"
];
stepIntoAttr = evalNext: name:
if elem name specialAttrs
then id
else evalNext;
transform = depth:
if depthLimit != null && depth > depthLimit then
if throwOnDepthLimit
then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
else const "<unevaluated>"
else id;
mapAny = depth: v:
let
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
in
if isAttrs v then mapAttrs (stepIntoAttr evalNext) v
else if isList v then map evalNext v
else transform (depth + 1) v;
in
mapAny 0;
assert isInt depthLimit;
let
specialAttrs = [
"__functor"
"__functionArgs"
"__toString"
"__pretty"
];
stepIntoAttr = evalNext: name: if elem name specialAttrs then id else evalNext;
transform =
depth:
if depthLimit != null && depth > depthLimit then
if throwOnDepthLimit then
throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
else
const "<unevaluated>"
else
id;
mapAny =
depth: v:
let
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
in
if isAttrs v then
mapAttrs (stepIntoAttr evalNext) v
else if isList v then
map evalNext v
else
transform (depth + 1) v;
in
mapAny 0;
/**
Pretty print a value, akin to `builtins.trace`.
@ -481,68 +520,96 @@ in rec {
Value
: The value to be pretty printed
*/
toPretty = {
allowPrettyValues ? false,
multiline ? true,
indent ? ""
}:
toPretty =
{
allowPrettyValues ? false,
multiline ? true,
indent ? "",
}:
let
go = indent: v:
let introSpace = if multiline then "\n${indent} " else " ";
outroSpace = if multiline then "\n${indent}" else " ";
in if isInt v then toString v
# toString loses precision on floats, so we use toJSON instead. This isn't perfect
# as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
# pretty-printing purposes this is acceptable.
else if isFloat v then builtins.toJSON v
else if isString v then
let
lines = filter (v: ! isList v) (split "\n" v);
escapeSingleline = escape [ "\\" "\"" "\${" ];
escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
multilineResult = let
escapedLines = map escapeMultiline lines;
# The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
# indentation level. Otherwise, '' is appended to the last line.
lastLine = last escapedLines;
in "''" + introSpace + concatStringsSep introSpace (init escapedLines)
+ (if lastLine == "" then outroSpace else introSpace + lastLine) + "''";
in
if multiline && length lines > 1 then multilineResult else singlelineResult
else if true == v then "true"
else if false == v then "false"
else if null == v then "null"
else if isPath v then toString v
else if isList v then
if v == [] then "[ ]"
else "[" + introSpace
+ concatMapStringsSep introSpace (go (indent + " ")) v
+ outroSpace + "]"
else if isFunction v then
let fna = functionArgs v;
showFnas = concatStringsSep ", " (mapAttrsToList
(name: hasDefVal: if hasDefVal then name + "?" else name)
fna);
in if fna == {} then "<function>"
else "<function, args: {${showFnas}}>"
else if isAttrs v then
# apply pretty values if allowed
if allowPrettyValues && v ? __pretty && v ? val
then v.__pretty v.val
else if v == {} then "{ }"
else if v ? type && v.type == "derivation" then
"<derivation ${v.name or "???"}>"
else "{" + introSpace
+ concatStringsSep introSpace (mapAttrsToList
(name: value:
go =
indent: v:
let
introSpace = if multiline then "\n${indent} " else " ";
outroSpace = if multiline then "\n${indent}" else " ";
in
if isInt v then
toString v
# toString loses precision on floats, so we use toJSON instead. This isn't perfect
# as the resulting string may not parse back as a float (e.g. 42, 1e-06), but for
# pretty-printing purposes this is acceptable.
else if isFloat v then
builtins.toJSON v
else if isString v then
let
lines = filter (v: !isList v) (split "\n" v);
escapeSingleline = escape [
"\\"
"\""
"\${"
];
escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
multilineResult =
let
escapedLines = map escapeMultiline lines;
# The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
# indentation level. Otherwise, '' is appended to the last line.
lastLine = last escapedLines;
in
"''"
+ introSpace
+ concatStringsSep introSpace (init escapedLines)
+ (if lastLine == "" then outroSpace else introSpace + lastLine)
+ "''";
in
if multiline && length lines > 1 then multilineResult else singlelineResult
else if true == v then
"true"
else if false == v then
"false"
else if null == v then
"null"
else if isPath v then
toString v
else if isList v then
if v == [ ] then
"[ ]"
else
"[" + introSpace + concatMapStringsSep introSpace (go (indent + " ")) v + outroSpace + "]"
else if isFunction v then
let
fna = functionArgs v;
showFnas = concatStringsSep ", " (
mapAttrsToList (name: hasDefVal: if hasDefVal then name + "?" else name) fna
);
in
if fna == { } then "<function>" else "<function, args: {${showFnas}}>"
else if isAttrs v then
# apply pretty values if allowed
if allowPrettyValues && v ? __pretty && v ? val then
v.__pretty v.val
else if v == { } then
"{ }"
else if v ? type && v.type == "derivation" then
"<derivation ${v.name or "???"}>"
else
"{"
+ introSpace
+ concatStringsSep introSpace (
mapAttrsToList (
name: value:
"${escapeNixIdentifier name} = ${
addErrorContext "while evaluating an attribute `${name}`"
(go (indent + " ") value)
};") v)
+ outroSpace + "}"
else abort "generators.toPretty: should never happen (v = ${v})";
in go indent;
addErrorContext "while evaluating an attribute `${name}`" (go (indent + " ") value)
};"
) v
)
+ outroSpace
+ "}"
else
abort "generators.toPretty: should never happen (v = ${v})";
in
go indent;
/**
Translate a simple Nix expression to [Plist notation](https://en.wikipedia.org/wiki/Property_list).
@ -557,61 +624,90 @@ in rec {
Value
: The value to be converted to Plist
*/
toPlist = {
escape ? false
}: v: let
expr = ind: x:
if x == null then "" else
if isBool x then bool ind x else
if isInt x then int ind x else
if isString x then str ind x else
if isList x then list ind x else
if isAttrs x then attrs ind x else
if isPath x then str ind (toString x) else
if isFloat x then float ind x else
abort "generators.toPlist: should never happen (v = ${v})";
toPlist =
{
escape ? false,
}:
v:
let
expr =
ind: x:
if x == null then
""
else if isBool x then
bool ind x
else if isInt x then
int ind x
else if isString x then
str ind x
else if isList x then
list ind x
else if isAttrs x then
attrs ind x
else if isPath x then
str ind (toString x)
else if isFloat x then
float ind x
else
abort "generators.toPlist: should never happen (v = ${v})";
literal = ind: x: ind + x;
literal = ind: x: ind + x;
maybeEscapeXML = if escape then escapeXML else x: x;
maybeEscapeXML = if escape then escapeXML else x: x;
bool = ind: x: literal ind (if x then "<true/>" else "<false/>");
int = ind: x: literal ind "<integer>${toString x}</integer>";
str = ind: x: literal ind "<string>${maybeEscapeXML x}</string>";
key = ind: x: literal ind "<key>${maybeEscapeXML x}</key>";
float = ind: x: literal ind "<real>${toString x}</real>";
bool = ind: x: literal ind (if x then "<true/>" else "<false/>");
int = ind: x: literal ind "<integer>${toString x}</integer>";
str = ind: x: literal ind "<string>${maybeEscapeXML x}</string>";
key = ind: x: literal ind "<key>${maybeEscapeXML x}</key>";
float = ind: x: literal ind "<real>${toString x}</real>";
indent = ind: expr "\t${ind}";
indent = ind: expr "\t${ind}";
item = ind: concatMapStringsSep "\n" (indent ind);
item = ind: concatMapStringsSep "\n" (indent ind);
list = ind: x: concatStringsSep "\n" [
(literal ind "<array>")
(item ind x)
(literal ind "</array>")
];
list =
ind: x:
concatStringsSep "\n" [
(literal ind "<array>")
(item ind x)
(literal ind "</array>")
];
attrs = ind: x: concatStringsSep "\n" [
(literal ind "<dict>")
(attr ind x)
(literal ind "</dict>")
];
attrs =
ind: x:
concatStringsSep "\n" [
(literal ind "<dict>")
(attr ind x)
(literal ind "</dict>")
];
attr = let attrFilter = name: value: name != "_module" && value != null;
in ind: x: concatStringsSep "\n" (flatten (mapAttrsToList
(name: value: optionals (attrFilter name value) [
(key "\t${ind}" name)
(expr "\t${ind}" value)
]) x));
attr =
let
attrFilter = name: value: name != "_module" && value != null;
in
ind: x:
concatStringsSep "\n" (
flatten (
mapAttrsToList (
name: value:
optionals (attrFilter name value) [
(key "\t${ind}" name)
(expr "\t${ind}" value)
]
) x
)
);
in
# TODO: As discussed in #356502, deprecated functionality should be removed sometime after 25.11.
lib.warnIf (!escape && lib.oldestSupportedReleaseIsAtLeast 2505) "Using `lib.generators.toPlist` without `escape = true` is deprecated" ''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
${expr "" v}
</plist>'';
in
# TODO: As discussed in #356502, deprecated functionality should be removed sometime after 25.11.
lib.warnIf (!escape && lib.oldestSupportedReleaseIsAtLeast 2505)
"Using `lib.generators.toPlist` without `escape = true` is deprecated"
''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
${expr "" v}
</plist>'';
/**
Translate a simple Nix expression to Dhall notation.
@ -629,13 +725,14 @@ in rec {
: The value to be converted to Dhall
*/
toDhall = { }@args: v:
let concatItems = concatStringsSep ", ";
in if isAttrs v then
"{ ${
concatItems (mapAttrsToList
(key: value: "${key} = ${toDhall args value}") v)
} }"
toDhall =
{ }@args:
v:
let
concatItems = concatStringsSep ", ";
in
if isAttrs v then
"{ ${concatItems (mapAttrsToList (key: value: "${key} = ${toDhall args value}") v)} }"
else if isList v then
"[ ${concatItems (map (toDhall args) v)} ]"
else if isInt v then
@ -663,7 +760,6 @@ in rec {
Regardless of multiline parameter there is no trailing newline.
# Inputs
Structured function argument
@ -711,11 +807,13 @@ in rec {
:::
*/
toLua = {
multiline ? true,
indent ? "",
asBindings ? false,
}@args: v:
toLua =
{
multiline ? true,
indent ? "",
asBindings ? false,
}@args:
v:
let
innerIndent = "${indent} ";
introSpace = if multiline then "\n${innerIndent}" else " ";
@ -725,13 +823,16 @@ in rec {
asBindings = false;
};
concatItems = concatStringsSep ",${introSpace}";
isLuaInline = { _type ? null, ... }: _type == "lua-inline";
isLuaInline =
{
_type ? null,
...
}:
_type == "lua-inline";
generatedBindings =
assert assertMsg (badVarNames == []) "Bad Lua var names: ${toPretty {} badVarNames}";
concatStrings (
mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v
);
assert assertMsg (badVarNames == [ ]) "Bad Lua var names: ${toPretty { } badVarNames}";
concatStrings (mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v);
# https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names
matchVarName = match "[[:alpha:]_][[:alnum:]_]*(\\.[[:alpha:]_][[:alnum:]_]*)*";
@ -746,8 +847,12 @@ in rec {
else if isPath v || isDerivation v then
toJSON "${v}"
else if isList v then
(if v == [ ] then "{}" else
"{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}")
(
if v == [ ] then
"{}"
else
"{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}"
)
else if isAttrs v then
(
if isLuaInline v then
@ -755,9 +860,9 @@ in rec {
else if v == { } then
"{}"
else
"{${introSpace}${concatItems (
mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v
)}${outroSpace}}"
"{${introSpace}${
concatItems (mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v)
}${outroSpace}}"
)
else
abort "generators.toLua: type ${typeOf v} is unsupported";
@ -765,7 +870,6 @@ in rec {
/**
Mark string as Lua expression to be inlined when processed by toLua.
# Inputs
`expr`
@ -778,8 +882,12 @@ in rec {
mkLuaInline :: String -> AttrSet
```
*/
mkLuaInline = expr: { _type = "lua-inline"; inherit expr; };
} // {
mkLuaInline = expr: {
_type = "lua-inline";
inherit expr;
};
}
// {
/**
Generates JSON from an arbitrary (non-function) value.
For more information see the documentation of the builtin.
@ -794,7 +902,7 @@ in rec {
: The value to be converted to JSON
*/
toJSON = {}: lib.strings.toJSON;
toJSON = { }: lib.strings.toJSON;
/**
YAML has been a strict superset of JSON since 1.2, so we
@ -812,5 +920,5 @@ in rec {
: The value to be converted to YAML
*/
toYAML = {}: lib.strings.toJSON;
toYAML = { }: lib.strings.toJSON;
}

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,30 @@
{ lib }:
let
inherit (lib.strings) toInt;
inherit (lib.trivial) compare min id warn pipe;
inherit (lib.trivial)
compare
min
id
warn
pipe
;
inherit (lib.attrsets) mapAttrs;
inherit (lib) max;
in
rec {
inherit (builtins) head tail length isList elemAt concatLists filter elem genList map;
inherit (builtins)
head
tail
length
isList
elemAt
concatLists
filter
elem
genList
map
;
/**
Create a list consisting of a single element. `singleton x` is
@ -40,7 +57,7 @@ rec {
:::
*/
singleton = x: [x];
singleton = x: [ x ];
/**
Apply the function to each element in the list.
@ -82,7 +99,6 @@ rec {
`list` with `nul` as the starting value, i.e.,
`foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`.
# Inputs
`op`
@ -119,14 +135,13 @@ rec {
:::
*/
foldr = op: nul: list:
foldr =
op: nul: list:
let
len = length list;
fold' = n:
if n == len
then nul
else op (elemAt list n) (fold' (n + 1));
in fold' 0;
fold' = n: if n == len then nul else op (elemAt list n) (fold' (n + 1));
in
fold' 0;
/**
`fold` is an alias of `foldr` for historic reasons
@ -134,7 +149,6 @@ rec {
# FIXME(Profpatsch): deprecate?
fold = foldr;
/**
left fold, like `foldr`, but from the left:
@ -176,13 +190,12 @@ rec {
:::
*/
foldl = op: nul: list:
foldl =
op: nul: list:
let
foldl' = n:
if n == -1
then nul
else op (foldl' (n - 1)) (elemAt list n);
in foldl' (length list - 1);
foldl' = n: if n == -1 then nul else op (foldl' (n - 1)) (elemAt list n);
in
foldl' (length list - 1);
/**
Reduce a list by applying a binary operator from left to right,
@ -261,13 +274,11 @@ rec {
:::
*/
foldl' =
op:
acc:
op: acc:
# The builtin `foldl'` is a bit lazier than one might expect.
# See https://github.com/NixOS/nix/pull/7158.
# In particular, the initial accumulator value is not forced before the first iteration starts.
builtins.seq acc
(builtins.foldl' op acc);
builtins.seq acc (builtins.foldl' op acc);
/**
Map with index starting from 0
@ -304,7 +315,6 @@ rec {
/**
Map with index starting from 1
# Inputs
`f`
@ -374,12 +384,9 @@ rec {
:::
*/
ifilter0 =
ipred:
input:
ipred: input:
map (idx: elemAt input idx) (
filter (idx: ipred idx (elemAt input idx)) (
genList (x: x) (length input)
)
filter (idx: ipred idx (elemAt input idx)) (genList (x: x) (length input))
);
/**
@ -408,14 +415,12 @@ rec {
Flatten the argument into a single list; that is, nested lists are
spliced into the top-level lists.
# Inputs
`x`
: 1\. Function argument
# Examples
:::{.example}
## `lib.lists.flatten` usage example
@ -429,15 +434,11 @@ rec {
:::
*/
flatten = x:
if isList x
then concatMap (y: flatten y) x
else [x];
flatten = x: if isList x then concatMap (y: flatten y) x else [ x ];
/**
Remove elements equal to 'e' from a list. Useful for buildInputs.
# Inputs
`e`
@ -465,8 +466,7 @@ rec {
:::
*/
remove =
e: filter (x: x != e);
remove = e: filter (x: x != e);
/**
Find the sole element in the list matching the specified
@ -475,7 +475,6 @@ rec {
Returns `default` if no such element exists, or
`multiple` if there are multiple matching elements.
# Inputs
`pred`
@ -516,14 +515,17 @@ rec {
:::
*/
findSingle =
pred:
default:
multiple:
list:
let found = filter pred list; len = length found;
in if len == 0 then default
else if len != 1 then multiple
else head found;
pred: default: multiple: list:
let
found = filter pred list;
len = length found;
in
if len == 0 then
default
else if len != 1 then
multiple
else
head found;
/**
Find the first index in the list matching the specified
@ -563,9 +565,7 @@ rec {
:::
*/
findFirstIndex =
pred:
default:
list:
pred: default: list:
let
# A naive recursive implementation would be much simpler, but
# would also overflow the evaluator stack. We use `foldl'` as a workaround
@ -580,12 +580,13 @@ rec {
# - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred
#
# We start with index -1 and the 0'th element of the list, which satisfies the invariant
resultIndex = foldl' (index: el:
resultIndex = foldl' (
index: el:
if index < 0 then
# No match yet before the current index, we need to check the element
if pred el then
# We have a match! Turn it into the actual index to prevent future iterations from modifying it
- index - 1
-index - 1
else
# Still no match, update the index to the next element (we're counting down, so minus one)
index - 1
@ -594,10 +595,7 @@ rec {
index
) (-1) list;
in
if resultIndex < 0 then
default
else
resultIndex;
if resultIndex < 0 then default else resultIndex;
/**
Find the first element in the list matching the specified
@ -637,16 +635,11 @@ rec {
:::
*/
findFirst =
pred:
default:
list:
pred: default: list:
let
index = findFirstIndex pred null list;
in
if index == null then
default
else
elemAt list index;
if index == null then default else elemAt list index;
/**
Return true if function `pred` returns true for at least one
@ -745,8 +738,7 @@ rec {
:::
*/
count =
pred: foldl' (c: x: if pred x then c + 1 else c) 0;
count = pred: foldl' (c: x: if pred x then c + 1 else c) 0;
/**
Return a singleton list or an empty list, depending on a boolean
@ -782,7 +774,7 @@ rec {
:::
*/
optional = cond: elem: if cond then [elem] else [];
optional = cond: elem: if cond then [ elem ] else [ ];
/**
Return a list or an empty list, depending on a boolean value.
@ -816,10 +808,7 @@ rec {
:::
*/
optionals =
cond:
elems: if cond then elems else [];
optionals = cond: elems: if cond then elems else [ ];
/**
If argument is a list, return it; else, wrap it in a singleton
@ -845,7 +834,7 @@ rec {
:::
*/
toList = x: if isList x then x else [x];
toList = x: if isList x then x else [ x ];
/**
Return a list of integers from `first` up to and including `last`.
@ -879,13 +868,7 @@ rec {
:::
*/
range =
first:
last:
if first > last then
[]
else
genList (n: first + n) (last - first + 1);
range = first: last: if first > last then [ ] else genList (n: first + n) (last - first + 1);
/**
Return a list with `n` copies of an element.
@ -977,7 +960,6 @@ rec {
: 4\. Function argument
# Examples
:::{.example}
## `lib.lists.groupBy'` usage example
@ -1002,15 +984,21 @@ rec {
:::
*/
groupBy' = op: nul: pred: lst: mapAttrs (name: foldl op nul) (groupBy pred lst);
groupBy' =
op: nul: pred: lst:
mapAttrs (name: foldl op nul) (groupBy pred lst);
groupBy = builtins.groupBy or (
pred: foldl' (r: e:
let
key = pred e;
in
r // { ${key} = (r.${key} or []) ++ [e]; }
) {});
groupBy =
builtins.groupBy or (
pred:
foldl' (
r: e:
let
key = pred e;
in
r // { ${key} = (r.${key} or [ ]) ++ [ e ]; }
) { }
);
/**
Merges two lists of the same size together. If the sizes aren't the same
@ -1049,11 +1037,8 @@ rec {
:::
*/
zipListsWith =
f:
fst:
snd:
genList
(n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
f: fst: snd:
genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
/**
Merges two lists of the same size together. If the sizes aren't the same
@ -1114,8 +1099,12 @@ rec {
:::
*/
reverseList = xs:
let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;
reverseList =
xs:
let
l = length xs;
in
genList (n: elemAt xs (l - n - 1)) l;
/**
Depth-First Search (DFS) for lists `list != []`.
@ -1123,7 +1112,6 @@ rec {
`before a b == true` means that `b` depends on `a` (there's an
edge from `b` to `a`).
# Inputs
`stopOnCycles`
@ -1138,7 +1126,6 @@ rec {
: 3\. Function argument
# Examples
:::{.example}
## `lib.lists.listDfs` usage example
@ -1159,22 +1146,32 @@ rec {
:::
*/
listDfs = stopOnCycles: before: list:
listDfs =
stopOnCycles: before: list:
let
dfs' = us: visited: rest:
dfs' =
us: visited: rest:
let
c = filter (x: before x us) visited;
b = partition (x: before x us) rest;
in if stopOnCycles && (length c > 0)
then { cycle = us; loops = c; inherit visited rest; }
else if length b.right == 0
then # nothing is before us
{ minimal = us; inherit visited rest; }
else # grab the first one before us and continue
dfs' (head b.right)
([ us ] ++ visited)
(tail b.right ++ b.wrong);
in dfs' (head list) [] (tail list);
in
if stopOnCycles && (length c > 0) then
{
cycle = us;
loops = c;
inherit visited rest;
}
else if length b.right == 0 then
# nothing is before us
{
minimal = us;
inherit visited rest;
}
else
# grab the first one before us and continue
dfs' (head b.right) ([ us ] ++ visited) (tail b.right ++ b.wrong);
in
dfs' (head list) [ ] (tail list);
/**
Sort a list based on a partial ordering using DFS. This
@ -1184,7 +1181,6 @@ rec {
`before a b == true` means that `b` should be after `a`
in the result.
# Inputs
`before`
@ -1195,7 +1191,6 @@ rec {
: 2\. Function argument
# Examples
:::{.example}
## `lib.lists.toposort` usage example
@ -1216,24 +1211,28 @@ rec {
:::
*/
toposort = before: list:
toposort =
before: list:
let
dfsthis = listDfs true before list;
toporest = toposort before (dfsthis.visited ++ dfsthis.rest);
in
if length list < 2
then # finish
{ result = list; }
else if dfsthis ? cycle
then # there's a cycle, starting from the current vertex, return it
{ cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
inherit (dfsthis) loops; }
else if toporest ? cycle
then # there's a cycle somewhere else in the graph, return it
toporest
# Slow, but short. Can be made a bit faster with an explicit stack.
else # there are no cycles
{ result = [ dfsthis.minimal ] ++ toporest.result; };
if length list < 2 then
# finish
{ result = list; }
else if dfsthis ? cycle then
# there's a cycle, starting from the current vertex, return it
{
cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited);
inherit (dfsthis) loops;
}
else if toporest ? cycle then
# there's a cycle somewhere else in the graph, return it
toporest
# Slow, but short. Can be made a bit faster with an explicit stack.
else
# there are no cycles
{ result = [ dfsthis.minimal ] ++ toporest.result; };
/**
Sort a list based on a comparator function which compares two
@ -1289,7 +1288,6 @@ rec {
sortOn f == sort (p: q: f p < f q)
```
# Inputs
`f`
@ -1317,18 +1315,22 @@ rec {
:::
*/
sortOn = f: list:
sortOn =
f: list:
let
# Heterogenous list as pair may be ugly, but requires minimal allocations.
pairs = map (x: [(f x) x]) list;
pairs = map (x: [
(f x)
x
]) list;
in
map
(x: builtins.elemAt x 1)
(sort
# Compare the first element of the pairs
# Do not factor out the `<`, to avoid calls in hot code; duplicate instead.
(a: b: head a < head b)
pairs);
map (x: builtins.elemAt x 1) (
sort
# Compare the first element of the pairs
# Do not factor out the `<`, to avoid calls in hot code; duplicate instead.
(a: b: head a < head b)
pairs
);
/**
Compare two lists element-by-element with a comparison function `cmp`.
@ -1360,7 +1362,6 @@ rec {
: The second list
# Examples
:::{.example}
## `lib.lists.compareLists` usage examples
@ -1378,30 +1379,28 @@ rec {
:::
*/
compareLists = cmp: a: b:
if a == []
then if b == []
then 0
else -1
else if b == []
then 1
else let rel = cmp (head a) (head b); in
if rel == 0
then compareLists cmp (tail a) (tail b)
else rel;
compareLists =
cmp: a: b:
if a == [ ] then
if b == [ ] then 0 else -1
else if b == [ ] then
1
else
let
rel = cmp (head a) (head b);
in
if rel == 0 then compareLists cmp (tail a) (tail b) else rel;
/**
Sort list using "Natural sorting".
Numeric portions of strings are sorted in numeric order.
# Inputs
`lst`
: 1\. Function argument
# Examples
:::{.example}
## `lib.lists.naturalSort` usage example
@ -1417,18 +1416,21 @@ rec {
:::
*/
naturalSort = lst:
naturalSort =
lst:
let
vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s);
prepared = map (x: [ (vectorise x) x ]) lst; # remember vectorised version for O(n) regex splits
prepared = map (x: [
(vectorise x)
x
]) lst; # remember vectorised version for O(n) regex splits
less = a: b: (compareLists compare (head a) (head b)) < 0;
in
map (x: elemAt x 1) (sort less prepared);
map (x: elemAt x 1) (sort less prepared);
/**
Return the first (at most) N elements of a list.
# Inputs
`count`
@ -1458,13 +1460,11 @@ rec {
:::
*/
take =
count: sublist 0 count;
take = count: sublist 0 count;
/**
Remove the first (at most) N elements of a list.
# Inputs
`count`
@ -1494,14 +1494,11 @@ rec {
:::
*/
drop =
count:
list: sublist count (length list) list;
drop = count: list: sublist count (length list) list;
/**
Remove the last (at most) N elements of a list.
# Inputs
`count`
@ -1530,18 +1527,12 @@ rec {
=> [ ]
```
:::
*/
dropEnd =
n: xs:
take
(max 0 (length xs - n))
xs;
*/
dropEnd = n: xs: take (max 0 (length xs - n)) xs;
/**
Whether the first list is a prefix of the second list.
# Inputs
`list1`
@ -1571,10 +1562,7 @@ rec {
:::
*/
hasPrefix =
list1:
list2:
take (length list1) list2 == list1;
hasPrefix = list1: list2: take (length list1) list2 == list1;
/**
Remove the first list as a prefix from the second list.
@ -1610,8 +1598,7 @@ rec {
:::
*/
removePrefix =
list1:
list2:
list1: list2:
if hasPrefix list1 list2 then
drop (length list1) list2
else
@ -1655,20 +1642,22 @@ rec {
:::
*/
sublist =
start:
count:
list:
let len = length list; in
genList
(n: elemAt list (n + start))
(if start >= len then 0
else if start + count > len then len - start
else count);
start: count: list:
let
len = length list;
in
genList (n: elemAt list (n + start)) (
if start >= len then
0
else if start + count > len then
len - start
else
count
);
/**
The common prefix of two lists.
# Inputs
`list1`
@ -1701,8 +1690,7 @@ rec {
:::
*/
commonPrefix =
list1:
list2:
list1: list2:
let
# Zip the lists together into a list of booleans whether each element matches
matchings = zipListsWith (fst: snd: fst != snd) list1 list2;
@ -1719,7 +1707,6 @@ rec {
This function throws an error if the list is empty.
# Inputs
`list`
@ -1743,8 +1730,9 @@ rec {
:::
*/
last = list:
assert lib.assertMsg (list != []) "lists.last: list must not be empty!";
last =
list:
assert lib.assertMsg (list != [ ]) "lists.last: list must not be empty!";
elemAt list (length list - 1);
/**
@ -1752,7 +1740,6 @@ rec {
This function throws an error if the list is empty.
# Inputs
`list`
@ -1776,15 +1763,14 @@ rec {
:::
*/
init = list:
assert lib.assertMsg (list != []) "lists.init: list must not be empty!";
init =
list:
assert lib.assertMsg (list != [ ]) "lists.init: list must not be empty!";
take (length list - 1) list;
/**
Return the image of the cross product of some lists by a function.
# Examples
:::{.example}
## `lib.lists.crossLists` usage example
@ -1814,13 +1800,11 @@ rec {
nix-repl> lib.mapCartesianProduct ({x,y}: x+y) { x = [1 2]; y = [3 4]; }
[ 4 5 5 6 ]
''
(f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
'' (f: foldl (fs: args: concatMap (f: map f args) fs) [ f ]);
/**
Remove duplicate elements from the `list`. O(n^2) complexity.
# Inputs
`list`
@ -1844,12 +1828,11 @@ rec {
:::
*/
unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [];
unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [ ];
/**
Check if list contains only unique elements. O(n^2) complexity.
# Inputs
`list`
@ -1877,7 +1860,6 @@ rec {
*/
allUnique = list: (length (unique list) == length list);
/**
Intersects list 'list1' and another list (`list2`).
@ -1893,7 +1875,6 @@ rec {
: Second list
# Examples
:::{.example}
## `lib.lists.intersectLists` usage example
@ -1922,7 +1903,6 @@ rec {
: Second list
# Examples
:::{.example}
## `lib.lists.subtractLists` usage example

View File

@ -6,14 +6,20 @@
{ lib }:
let
inherit (lib) matchAttrs any all isDerivation getBin assertMsg;
inherit (lib)
matchAttrs
any
all
isDerivation
getBin
assertMsg
;
inherit (lib.attrsets) mapAttrs' filterAttrs;
inherit (builtins) isString match typeOf;
in
rec {
/**
Add to or override the meta attributes of the given
derivation.
@ -28,7 +34,6 @@ rec {
: 2\. Function argument
# Examples
:::{.example}
## `lib.meta.addMetaAttrs` usage example
@ -39,9 +44,7 @@ rec {
:::
*/
addMetaAttrs = newAttrs: drv:
drv // { meta = (drv.meta or {}) // newAttrs; };
addMetaAttrs = newAttrs: drv: drv // { meta = (drv.meta or { }) // newAttrs; };
/**
Disable Hydra builds of given derivation.
@ -52,8 +55,7 @@ rec {
: 1\. Function argument
*/
dontDistribute = drv: addMetaAttrs { hydraPlatforms = []; } drv;
dontDistribute = drv: addMetaAttrs { hydraPlatforms = [ ]; } drv;
/**
Change the [symbolic name of a derivation](https://nixos.org/manual/nix/stable/language/derivations.html#attr-name).
@ -72,8 +74,7 @@ rec {
: 2\. Function argument
*/
setName = name: drv: drv // {inherit name;};
setName = name: drv: drv // { inherit name; };
/**
Like `setName`, but takes the previous name as an argument.
@ -88,7 +89,6 @@ rec {
: 2\. Function argument
# Examples
:::{.example}
## `lib.meta.updateName` usage example
@ -99,8 +99,7 @@ rec {
:::
*/
updateName = updater: drv: drv // {name = updater (drv.name);};
updateName = updater: drv: drv // { name = updater (drv.name); };
/**
Append a suffix to the name of a package (before the version
@ -112,14 +111,19 @@ rec {
: 1\. Function argument
*/
appendToName = suffix: updateName (name:
let x = builtins.parseDrvName name; in "${x.name}-${suffix}-${x.version}");
appendToName =
suffix:
updateName (
name:
let
x = builtins.parseDrvName name;
in
"${x.name}-${suffix}-${x.version}"
);
/**
Apply a function to each derivation and only to derivations in an attrset.
# Inputs
`f`
@ -130,11 +134,12 @@ rec {
: 2\. Function argument
*/
mapDerivationAttrset = f: set: lib.mapAttrs (name: pkg: if lib.isDerivation pkg then (f pkg) else pkg) set;
mapDerivationAttrset =
f: set: lib.mapAttrs (name: pkg: if lib.isDerivation pkg then (f pkg) else pkg) set;
/**
The default priority of packages in Nix. See `defaultPriority` in [`src/nix/profile.cc`](https://github.com/NixOS/nix/blob/master/src/nix/profile.cc#L47).
*/
*/
defaultPriority = 5;
/**
@ -159,7 +164,6 @@ rec {
`drv`
: 1\. Function argument
*/
lowPrio = setPrio 10;
@ -174,7 +178,6 @@ rec {
*/
lowPrioSet = set: mapDerivationAttrset lowPrio set;
/**
Increase the nix-env priority of the package, i.e., this
version/variant of the package will be preferred.
@ -198,7 +201,6 @@ rec {
*/
hiPrioSet = set: mapDerivationAttrset hiPrio set;
/**
Check to see if a platform is matched by the given `meta.platforms`
element.
@ -214,7 +216,6 @@ rec {
We can inject these into a pattern for the whole of a structured platform,
and then match that.
# Inputs
`platform`
@ -225,7 +226,6 @@ rec {
: 2\. Function argument
# Examples
:::{.example}
## `lib.meta.platformMatch` usage example
@ -237,21 +237,24 @@ rec {
:::
*/
platformMatch = platform: elem: (
# Check with simple string comparison if elem was a string.
#
# The majority of comparisons done with this function will be against meta.platforms
# which contains a simple platform string.
#
# Avoiding an attrset allocation results in significant performance gains (~2-30) across the board in OfBorg
# because this is a hot path for nixpkgs.
if isString elem then platform ? system && elem == platform.system
else matchAttrs (
# Normalize platform attrset.
if elem ? parsed then elem
else { parsed = elem; }
) platform
);
platformMatch =
platform: elem:
(
# Check with simple string comparison if elem was a string.
#
# The majority of comparisons done with this function will be against meta.platforms
# which contains a simple platform string.
#
# Avoiding an attrset allocation results in significant performance gains (~2-30) across the board in OfBorg
# because this is a hot path for nixpkgs.
if isString elem then
platform ? system && elem == platform.system
else
matchAttrs (
# Normalize platform attrset.
if elem ? parsed then elem else { parsed = elem; }
) platform
);
/**
Check if a package is available on a given platform.
@ -263,7 +266,6 @@ rec {
2. None of `meta.badPlatforms` pattern matches the given platform.
# Inputs
`platform`
@ -274,7 +276,6 @@ rec {
: 2\. Function argument
# Examples
:::{.example}
## `lib.meta.availableOn` usage example
@ -286,9 +287,10 @@ rec {
:::
*/
availableOn = platform: pkg:
((!pkg?meta.platforms) || any (platformMatch platform) pkg.meta.platforms) &&
all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or []);
availableOn =
platform: pkg:
((!pkg ? meta.platforms) || any (platformMatch platform) pkg.meta.platforms)
&& all (elem: !platformMatch platform elem) (pkg.meta.badPlatforms or [ ]);
/**
Mapping of SPDX ID to the attributes in lib.licenses.
@ -309,13 +311,10 @@ rec {
:::
*/
licensesSpdx =
mapAttrs'
(_key: license: {
name = license.spdxId;
value = license;
})
(filterAttrs (_key: license: license ? spdxId) lib.licenses);
licensesSpdx = mapAttrs' (_key: license: {
name = license.spdxId;
value = license;
}) (filterAttrs (_key: license: license ? spdxId) lib.licenses);
/**
Get the corresponding attribute in lib.licenses from the SPDX ID
@ -348,10 +347,11 @@ rec {
*/
getLicenseFromSpdxId =
licstr:
getLicenseFromSpdxIdOr licstr (
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}"
{ shortName = licstr; }
);
getLicenseFromSpdxIdOr licstr (
lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}" {
shortName = licstr;
}
);
/**
Get the corresponding attribute in lib.licenses from the SPDX ID
@ -398,13 +398,12 @@ rec {
name = lib.toLower name;
inherit value;
}) licensesSpdx;
in licstr: default:
lowercaseLicenses.${ lib.toLower licstr } or default;
in
licstr: default: lowercaseLicenses.${lib.toLower licstr} or default;
/**
Get the path to the main program of a package based on meta.mainProgram
# Inputs
`x`
@ -430,17 +429,23 @@ rec {
:::
*/
getExe = x: getExe' x (x.meta.mainProgram or (
# This could be turned into an error when 23.05 is at end of life
lib.warn "getExe: Package ${lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
lib.getName
x
));
getExe =
x:
getExe' x (
x.meta.mainProgram or (
# This could be turned into an error when 23.05 is at end of life
lib.warn
"getExe: Package ${
lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name
} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
lib.getName
x
)
);
/**
Get the path of a program of a derivation.
# Inputs
`x`
@ -470,7 +475,8 @@ rec {
:::
*/
getExe' = x: y:
getExe' =
x: y:
assert assertMsg (isDerivation x)
"lib.meta.getExe': The first argument is of type ${typeOf x}, but it should be a derivation instead.";
assert assertMsg (isString y)

File diff suppressed because it is too large Load Diff

View File

@ -42,7 +42,7 @@ let
last
;
prioritySuggestion = ''
Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
Use `lib.mkForce value` or `lib.mkDefault value` to change the priority on any of these definitions.
'';
in
rec {
@ -71,7 +71,6 @@ rec {
```
isOption :: a -> Bool
```
*/
isOption = lib.isType "option";
@ -91,7 +90,6 @@ rec {
: Can be any nix value that evaluates.
: Usage with `lib.literalMD` or `lib.literalExpression` is supported
`example`
: Optional example value used in the manual.
: Can be any nix value that evaluates.
@ -144,7 +142,7 @@ rec {
internal ? null,
visible ? null,
readOnly ? null,
} @ attrs:
}@attrs:
attrs // { _type = "option"; };
/**
@ -179,12 +177,14 @@ rec {
:::
*/
mkEnableOption = name: mkOption {
default = false;
example = true;
description = "Whether to enable ${name}.";
type = lib.types.bool;
};
mkEnableOption =
name:
mkOption {
default = false;
example = true;
description = "Whether to enable ${name}.";
type = lib.types.bool;
};
/**
Creates an Option attribute set for an option that specifies the
@ -220,7 +220,6 @@ rec {
If you want users to be able to set no package, pass `nullable = true`.
In this mode a `default = null` will not be interpreted as no default and is interpreted literally.
# Inputs
`pkgs`
@ -264,39 +263,33 @@ rec {
mkPackageOption pkgs "hello" { }
=> { ...; default = pkgs.hello; defaultText = literalExpression "pkgs.hello"; description = "The hello package to use."; type = package; }
mkPackageOption pkgs "GHC" {
default = [ "ghc" ];
example = "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])";
}
=> { ...; default = pkgs.ghc; defaultText = literalExpression "pkgs.ghc"; description = "The GHC package to use."; example = literalExpression "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])"; type = package; }
mkPackageOption pkgs [ "python3Packages" "pytorch" ] {
extraDescription = "This is an example and doesn't actually do anything.";
}
=> { ...; default = pkgs.python3Packages.pytorch; defaultText = literalExpression "pkgs.python3Packages.pytorch"; description = "The pytorch package to use. This is an example and doesn't actually do anything."; type = package; }
mkPackageOption pkgs "nushell" {
nullable = true;
}
=> { ...; default = pkgs.nushell; defaultText = literalExpression "pkgs.nushell"; description = "The nushell package to use."; type = nullOr package; }
mkPackageOption pkgs "coreutils" {
default = null;
}
=> { ...; description = "The coreutils package to use."; type = package; }
mkPackageOption pkgs "dbus" {
nullable = true;
default = null;
}
=> { ...; default = null; description = "The dbus package to use."; type = nullOr package; }
mkPackageOption pkgs.javaPackages "OpenJFX" {
default = "openjfx20";
pkgsText = "pkgs.javaPackages";
@ -306,35 +299,44 @@ rec {
:::
*/
mkPackageOption = pkgs:
name:
{
nullable ? false,
default ? name,
example ? null,
extraDescription ? "",
pkgsText ? "pkgs"
}:
let
name' = if isList name then last name else name;
default' = if isList default then default else [ default ];
defaultText = concatStringsSep "." default';
defaultValue = attrByPath default'
(throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
defaults = if default != null then {
default = defaultValue;
defaultText = literalExpression ("${pkgsText}." + defaultText);
} else optionalAttrs nullable {
default = null;
};
in mkOption (defaults // {
description = "The ${name'} package to use."
+ (if extraDescription == "" then "" else " ") + extraDescription;
mkPackageOption =
pkgs: name:
{
nullable ? false,
default ? name,
example ? null,
extraDescription ? "",
pkgsText ? "pkgs",
}:
let
name' = if isList name then last name else name;
default' = if isList default then default else [ default ];
defaultText = concatStringsSep "." default';
defaultValue = attrByPath default' (throw "${defaultText} cannot be found in ${pkgsText}") pkgs;
defaults =
if default != null then
{
default = defaultValue;
defaultText = literalExpression ("${pkgsText}." + defaultText);
}
else
optionalAttrs nullable {
default = null;
};
in
mkOption (
defaults
// {
description =
"The ${name'} package to use." + (if extraDescription == "" then "" else " ") + extraDescription;
type = with lib.types; (if nullable then nullOr else lib.id) package;
} // optionalAttrs (example != null) {
example = literalExpression
(if isList example then "${pkgsText}." + concatStringsSep "." example else example);
});
}
// optionalAttrs (example != null) {
example = literalExpression (
if isList example then "${pkgsText}." + concatStringsSep "." example else example
);
}
);
/**
Deprecated alias of mkPackageOption, to be removed in 25.05.
@ -350,25 +352,29 @@ rec {
without having to implement similar features as long as the
values of the options are not accessed.
# Inputs
`attrs`
: Attribute set whose attributes override the argument to `mkOption`.
*/
mkSinkUndeclaredOptions = attrs: mkOption ({
internal = true;
visible = false;
default = false;
description = "Sink for option definitions.";
type = mkOptionType {
name = "sink";
check = x: true;
merge = loc: defs: false;
};
apply = x: throw "Option value is not readable because the option is not declared.";
} // attrs);
mkSinkUndeclaredOptions =
attrs:
mkOption (
{
internal = true;
visible = false;
default = false;
description = "Sink for option definitions.";
type = mkOptionType {
name = "sink";
check = x: true;
merge = loc: defs: false;
};
apply = x: throw "Option value is not readable because the option is not declared.";
}
// attrs
);
/**
A merge function that merges multiple definitions of an option into a single value
@ -413,18 +419,28 @@ rec {
- If all definitions are attribute sets, they are merged. (`lib.mergeAttrs`)
- If all definitions are functions, the first function is applied to the result of the second function. (`f -> x: f x`)
- Otherwise, an error is thrown.
*/
mergeDefaultOption = loc: defs:
let list = getValues defs; in
if length list == 1 then head list
else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
else if all isList list then concatLists list
else if all isAttrs list then foldl' lib.mergeAttrs {} list
else if all isBool list then foldl' lib.or false list
else if all isString list then lib.concatStrings list
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}";
mergeDefaultOption =
loc: defs:
let
list = getValues defs;
in
if length list == 1 then
head list
else if all isFunction list then
x: mergeDefaultOption loc (map (f: f x) list)
else if all isList list then
concatLists list
else if all isAttrs list then
foldl' lib.mergeAttrs { } list
else if all isBool list then
foldl' lib.or false list
else if all isString list then
lib.concatStrings list
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}";
/**
Require a single definition.
@ -438,7 +454,6 @@ rec {
NOTE: When the type is not checked completely by check, pass a merge function for further checking (of sub-attributes, etc).
# Inputs
`loc`
@ -449,24 +464,25 @@ rec {
: 3\. Function argument
*/
mergeUniqueOption = args@{
mergeUniqueOption =
args@{
message,
# WARNING: the default merge function assumes that the definition is a valid (option) value. You MUST pass a merge function if the return value needs to be
# - type checked beyond what .check does (which should be very litte; only on the value head; not attribute values, etc)
# - if you want attribute values to be checked, or list items
# - if you want coercedTo-like behavior to work
merge ? loc: defs: (head defs).value }:
merge ? loc: defs: (head defs).value,
}:
loc: defs:
if length defs == 1
then merge loc defs
else
assert length defs > 1;
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
if length defs == 1 then
merge loc defs
else
assert length defs > 1;
throw "The option `${showOption loc}' is defined multiple times while it's expected to be unique.\n${message}\nDefinition values:${showDefs defs}\n${prioritySuggestion}";
/**
"Merge" option definitions by checking that they all have the same value.
# Inputs
`loc`
@ -477,17 +493,28 @@ rec {
: 2\. Function argument
*/
mergeEqualOption = loc: defs:
if defs == [] then abort "This case should never happen."
mergeEqualOption =
loc: defs:
if defs == [ ] then
abort "This case should never happen."
# Return early if we only have one element
# This also makes it work for functions, because the foldl' below would try
# to compare the first element with itself, which is false for functions
else if length defs == 1 then (head defs).value
else (foldl' (first: def:
if def.value != first.value then
throw "The option `${showOption loc}' has conflicting definition values:${showDefs [ first def ]}\n${prioritySuggestion}"
else
first) (head defs) (tail defs)).value;
else if length defs == 1 then
(head defs).value
else
(foldl' (
first: def:
if def.value != first.value then
throw "The option `${showOption loc}' has conflicting definition values:${
showDefs [
first
def
]
}\n${prioritySuggestion}"
else
first
) (head defs) (tail defs)).value;
/**
Extracts values of all "value" keys of the given list.
@ -535,48 +562,50 @@ rec {
# Generate documentation template from the list of option declaration like
# the set generated with filterOptionSets.
optionAttrSetToDocList = optionAttrSetToDocList' [];
optionAttrSetToDocList = optionAttrSetToDocList' [ ];
optionAttrSetToDocList' = _: options:
concatMap (opt:
optionAttrSetToDocList' =
_: options:
concatMap (
opt:
let
name = showOption opt.loc;
docOption = {
loc = opt.loc;
inherit name;
description = opt.description or null;
declarations = filter (x: x != unknownModule) opt.declarations;
internal = opt.internal or false;
visible =
if (opt?visible && opt.visible == "shallow")
then true
else opt.visible or true;
readOnly = opt.readOnly or false;
type = opt.type.description or "unspecified";
}
// optionalAttrs (opt ? example) {
example =
builtins.addErrorContext "while evaluating the example of option `${name}`" (
docOption =
{
loc = opt.loc;
inherit name;
description = opt.description or null;
declarations = filter (x: x != unknownModule) opt.declarations;
internal = opt.internal or false;
visible = if (opt ? visible && opt.visible == "shallow") then true else opt.visible or true;
readOnly = opt.readOnly or false;
type = opt.type.description or "unspecified";
}
// optionalAttrs (opt ? example) {
example = builtins.addErrorContext "while evaluating the example of option `${name}`" (
renderOptionValue opt.example
);
}
// optionalAttrs (opt ? defaultText || opt ? default) {
default =
builtins.addErrorContext "while evaluating the ${if opt?defaultText then "defaultText" else "default value"} of option `${name}`" (
renderOptionValue (opt.defaultText or opt.default)
);
}
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) { inherit (opt) relatedPackages; };
}
// optionalAttrs (opt ? defaultText || opt ? default) {
default = builtins.addErrorContext "while evaluating the ${
if opt ? defaultText then "defaultText" else "default value"
} of option `${name}`" (renderOptionValue (opt.defaultText or opt.default));
}
// optionalAttrs (opt ? relatedPackages && opt.relatedPackages != null) {
inherit (opt) relatedPackages;
};
subOptions =
let ss = opt.type.getSubOptions opt.loc;
in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
let
ss = opt.type.getSubOptions opt.loc;
in
if ss != { } then optionAttrSetToDocList' opt.loc ss else [ ];
subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
in
# To find infinite recursion in NixOS option docs:
# builtins.trace opt.loc
[ docOption ] ++ optionals subOptionsVisible subOptions) (collect isOption options);
# To find infinite recursion in NixOS option docs:
# builtins.trace opt.loc
[ docOption ] ++ optionals subOptionsVisible subOptions
) (collect isOption options);
/**
This function recursively removes all derivation attributes from
@ -590,39 +619,49 @@ rec {
This function was made obsolete by renderOptionValue and is kept for
compatibility with out-of-tree code.
# Inputs
`x`
: 1\. Function argument
*/
scrubOptionValue = x:
scrubOptionValue =
x:
if isDerivation x then
{ type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
else if isList x then map scrubOptionValue x
else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
else x;
{
type = "derivation";
drvPath = x.name;
outPath = x.name;
name = x.name;
}
else if isList x then
map scrubOptionValue x
else if isAttrs x then
mapAttrs (n: v: scrubOptionValue v) (removeAttrs x [ "_args" ])
else
x;
/**
Ensures that the given option value (default or example) is a `_type`d string
by rendering Nix values to `literalExpression`s.
# Inputs
`v`
: 1\. Function argument
*/
renderOptionValue = v:
if v ? _type && v ? text then v
else literalExpression (lib.generators.toPretty {
multiline = true;
allowPrettyValues = true;
} v);
renderOptionValue =
v:
if v ? _type && v ? text then
v
else
literalExpression (
lib.generators.toPretty {
multiline = true;
allowPrettyValues = true;
} v
);
/**
For use in the `defaultText` and `example` option attributes. Causes the
@ -630,16 +669,21 @@ rec {
is necessary for complex values, e.g. functions, or values that depend on
other values or packages.
# Inputs
`text`
: 1\. Function argument
*/
literalExpression = text:
if ! isString text then throw "literalExpression expects a string."
else { _type = "literalExpression"; inherit text; };
literalExpression =
text:
if !isString text then
throw "literalExpression expects a string."
else
{
_type = "literalExpression";
inherit text;
};
literalExample = lib.warn "lib.literalExample is deprecated, use lib.literalExpression instead, or use lib.literalMD for a non-Nix description." literalExpression;
@ -648,16 +692,21 @@ rec {
given MD text to be inserted verbatim in the documentation, for when
a `literalExpression` would be too hard to read.
# Inputs
`text`
: 1\. Function argument
*/
literalMD = text:
if ! isString text then throw "literalMD expects a string."
else { _type = "literalMD"; inherit text; };
literalMD =
text:
if !isString text then
throw "literalMD expects a string."
else
{
_type = "literalMD";
inherit text;
};
# Helper functions.
@ -665,14 +714,12 @@ rec {
Convert an option, described as a list of the option parts to a
human-readable version.
# Inputs
`parts`
: 1\. Function argument
# Examples
:::{.example}
## `showOption` usage example
@ -690,36 +737,53 @@ rec {
:::
*/
showOption = parts: let
# If the part is a named placeholder of the form "<...>" don't escape it.
# It may cause misleading escaping if somebody uses literally "<...>" in their option names.
# This is the trade-off to allow for placeholders in option names.
isNamedPlaceholder = builtins.match "<(.*)>";
escapeOptionPart = part:
if part == "*" || isNamedPlaceholder part != null
then part
else lib.strings.escapeNixIdentifier part;
in (concatStringsSep ".") (map escapeOptionPart parts);
showOption =
parts:
let
# If the part is a named placeholder of the form "<...>" don't escape it.
# It may cause misleading escaping if somebody uses literally "<...>" in their option names.
# This is the trade-off to allow for placeholders in option names.
isNamedPlaceholder = builtins.match "<(.*)>";
escapeOptionPart =
part:
if part == "*" || isNamedPlaceholder part != null then
part
else
lib.strings.escapeNixIdentifier part;
in
(concatStringsSep ".") (map escapeOptionPart parts);
showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
showDefs = defs: concatMapStrings (def:
let
# Pretty print the value for display, if successful
prettyEval = builtins.tryEval
(lib.generators.toPretty { }
(lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));
# Split it into its lines
lines = filter (v: ! isList v) (builtins.split "\n" prettyEval.value);
# Only display the first 5 lines, and indent them for better visibility
value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
result =
# Don't print any value if evaluating the value strictly fails
if ! prettyEval.success then ""
# Put it on a new line if it consists of multiple
else if length lines > 1 then ":\n " + value
else ": " + value;
in "\n- In `${def.file}'${result}"
) defs;
showDefs =
defs:
concatMapStrings (
def:
let
# Pretty print the value for display, if successful
prettyEval = builtins.tryEval (
lib.generators.toPretty { } (
lib.generators.withRecursion {
depthLimit = 10;
throwOnDepthLimit = false;
} def.value
)
);
# Split it into its lines
lines = filter (v: !isList v) (builtins.split "\n" prettyEval.value);
# Only display the first 5 lines, and indent them for better visibility
value = concatStringsSep "\n " (take 5 lines ++ optional (length lines > 5) "...");
result =
# Don't print any value if evaluating the value strictly fails
if !prettyEval.success then
""
# Put it on a new line if it consists of multiple
else if length lines > 1 then
":\n " + value
else
": " + value;
in
"\n- In `${def.file}'${result}"
) defs;
/**
Pretty prints all option definition locations
@ -733,7 +797,6 @@ rec {
:::{.example}
## `lib.options.showOptionWithDefLocs` usage example
```nix
showOptionWithDefLocs { loc = ["x" "y" ]; files = [ "foo.nix" "bar.nix" ]; }
"x.y, with values defined in:\n - foo.nix\n - bar.nix\n"
@ -763,9 +826,9 @@ rec {
```
*/
showOptionWithDefLocs = opt: ''
${showOption opt.loc}, with values defined in:
${concatMapStringsSep "\n" (defFile: " - ${defFile}") opt.files}
'';
${showOption opt.loc}, with values defined in:
${concatMapStringsSep "\n" (defFile: " - ${defFile}") opt.files}
'';
unknownModule = "<unknown-file>";

View File

@ -125,30 +125,58 @@ rec {
- Ordering the dependent phases of `system.userActivationScripts`
For further examples see: [NixOS activation script](https://nixos.org/manual/nixos/stable/#sec-activation-script)
*/
textClosureList = predefined: arg:
textClosureList =
predefined: arg:
let
f = done: todo:
if todo == [] then {result = []; inherit done;}
f =
done: todo:
if todo == [ ] then
{
result = [ ];
inherit done;
}
else
let entry = head todo; in
let
entry = head todo;
in
if isAttrs entry then
let x = f done entry.deps;
y = f x.done (tail todo);
in { result = x.result ++ [entry.text] ++ y.result;
done = y.done;
}
else if done ? ${entry} then f done (tail todo)
else f (done // listToAttrs [{name = entry; value = 1;}]) ([predefined.${entry}] ++ tail todo);
in (f {} arg).result;
let
x = f done entry.deps;
y = f x.done (tail todo);
in
{
result = x.result ++ [ entry.text ] ++ y.result;
done = y.done;
}
else if done ? ${entry} then
f done (tail todo)
else
f (
done
// listToAttrs [
{
name = entry;
value = 1;
}
]
) ([ predefined.${entry} ] ++ tail todo);
in
(f { } arg).result;
textClosureMap = f: predefined: names:
textClosureMap =
f: predefined: names:
concatStringsSep "\n" (map f (textClosureList predefined names));
noDepEntry = text: {inherit text; deps = [];};
fullDepEntry = text: deps: {inherit text deps;};
packEntry = deps: {inherit deps; text="";};
noDepEntry = text: {
inherit text;
deps = [ ];
};
fullDepEntry = text: deps: { inherit text deps; };
packEntry = deps: {
inherit deps;
text = "";
};
stringAfter = deps: text: { inherit text deps; };

File diff suppressed because it is too large Load Diff

View File

@ -5,94 +5,395 @@ rec {
features = {
# x86_64 Generic
# Spec: https://gitlab.com/x86-psABIs/x86-64-ABI/
default = [ ];
x86-64 = [ ];
x86-64-v2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
x86-64-v3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
x86-64-v4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "avx512" "fma" ];
default = [ ];
x86-64 = [ ];
x86-64-v2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
x86-64-v3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"fma"
];
x86-64-v4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"avx512"
"fma"
];
# x86_64 Intel
nehalem = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
westmere = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
silvermont = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
sandybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" ];
ivybridge = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" ];
haswell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
broadwell = [ "sse3" "ssse3" "sse4_1" "sse4_2" "avx" "avx2" "fma" ];
skylake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
skylake-avx512 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
cannonlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
icelake-client = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
icelake-server = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
cascadelake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
cooperlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
tigerlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
alderlake = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
sapphirerapids = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
emeraldrapids = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "avx512" "fma" ];
sierraforest = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" "avx2" "fma" ];
nehalem = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
westmere = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
silvermont = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
sandybridge = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
];
ivybridge = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
];
haswell = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"fma"
];
broadwell = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"avx"
"avx2"
"fma"
];
skylake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
skylake-avx512 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cannonlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
icelake-client = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
icelake-server = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cascadelake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
cooperlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
tigerlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
alderlake = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
sapphirerapids = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
emeraldrapids = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
sierraforest = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
"avx2"
"fma"
];
# x86_64 AMD
btver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" ];
btver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "aes" "avx" ];
bdver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
bdver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
bdver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "fma" "fma4" ];
bdver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" "fma4" ];
znver1 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
znver2 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
znver3 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "fma" ];
znver4 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "avx512" "fma" ];
znver5 = [ "sse3" "ssse3" "sse4_1" "sse4_2" "sse4a" "aes" "avx" "avx2" "avx512" "fma" ];
btver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
];
btver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"aes"
"avx"
];
bdver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"fma"
"fma4"
];
bdver4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
"fma4"
];
znver1 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver2 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver3 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"fma"
];
znver4 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
znver5 = [
"sse3"
"ssse3"
"sse4_1"
"sse4_2"
"sse4a"
"aes"
"avx"
"avx2"
"avx512"
"fma"
];
# other
armv5te = [ ];
armv6 = [ ];
armv7-a = [ ];
armv8-a = [ ];
mips32 = [ ];
loongson2f = [ ];
armv5te = [ ];
armv6 = [ ];
armv7-a = [ ];
armv8-a = [ ];
mips32 = [ ];
loongson2f = [ ];
};
# a superior CPU has all the features of an inferior and is able to build and test code for it
inferiors = {
# x86_64 Generic
default = [ ];
x86-64 = [ ];
x86-64-v2 = [ "x86-64" ];
default = [ ];
x86-64 = [ ];
x86-64-v2 = [ "x86-64" ];
x86-64-v3 = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
x86-64-v4 = [ "x86-64-v3" ] ++ inferiors.x86-64-v3;
# x86_64 Intel
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
nehalem = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
westmere = [ "nehalem" ] ++ inferiors.nehalem;
sandybridge = [ "westmere" ] ++ inferiors.westmere;
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
nehalem = [ "x86-64-v2" ] ++ inferiors.x86-64-v2;
westmere = [ "nehalem" ] ++ inferiors.nehalem;
sandybridge = [ "westmere" ] ++ inferiors.westmere;
ivybridge = [ "sandybridge" ] ++ inferiors.sandybridge;
haswell = lib.unique ([ "ivybridge" "x86-64-v3" ] ++ inferiors.ivybridge ++ inferiors.x86-64-v3);
broadwell = [ "haswell" ] ++ inferiors.haswell;
skylake = [ "broadwell" ] ++ inferiors.broadwell;
haswell = lib.unique (
[
"ivybridge"
"x86-64-v3"
]
++ inferiors.ivybridge
++ inferiors.x86-64-v3
);
broadwell = [ "haswell" ] ++ inferiors.haswell;
skylake = [ "broadwell" ] ++ inferiors.broadwell;
skylake-avx512 = lib.unique ([ "skylake" "x86-64-v4" ] ++ inferiors.skylake ++ inferiors.x86-64-v4);
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
skylake-avx512 = lib.unique (
[
"skylake"
"x86-64-v4"
]
++ inferiors.skylake
++ inferiors.x86-64-v4
);
cannonlake = [ "skylake-avx512" ] ++ inferiors.skylake-avx512;
icelake-client = [ "cannonlake" ] ++ inferiors.cannonlake;
icelake-server = [ "icelake-client" ] ++ inferiors.icelake-client;
cascadelake = [ "cannonlake" ] ++ inferiors.cannonlake;
cooperlake = [ "cascadelake" ] ++ inferiors.cascadelake;
tigerlake = [ "icelake-server" ] ++ inferiors.icelake-server;
sapphirerapids = [ "tigerlake" ] ++ inferiors.tigerlake;
emeraldrapids = [ "sapphirerapids" ] ++ inferiors.sapphirerapids;
cascadelake = [ "cannonlake" ] ++ inferiors.cannonlake;
cooperlake = [ "cascadelake" ] ++ inferiors.cascadelake;
tigerlake = [ "icelake-server" ] ++ inferiors.icelake-server;
sapphirerapids = [ "tigerlake" ] ++ inferiors.tigerlake;
emeraldrapids = [ "sapphirerapids" ] ++ inferiors.sapphirerapids;
# CX16 does not exist on alderlake, while it does on nearly all other intel CPUs
alderlake = [ ];
sierraforest = [ "alderlake" ] ++ inferiors.alderlake;
alderlake = [ ];
sierraforest = [ "alderlake" ] ++ inferiors.alderlake;
# x86_64 AMD
# TODO: fill this (need testing)
btver1 = [ ];
btver2 = [ ];
bdver1 = [ ];
bdver2 = [ ];
bdver3 = [ ];
bdver4 = [ ];
btver1 = [ ];
btver2 = [ ];
bdver1 = [ ];
bdver2 = [ ];
bdver3 = [ ];
bdver4 = [ ];
# Regarding `skylake` as inferior of `znver1`, there are reports of
# successful usage by Gentoo users and Phoronix benchmarking of different
# `-march` targets.
@ -112,34 +413,43 @@ rec {
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
# https://en.wikichip.org/wiki/amd/microarchitectures/zen
# https://en.wikichip.org/wiki/intel/microarchitectures/skylake
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
znver2 = [ "znver1" ] ++ inferiors.znver1;
znver3 = [ "znver2" ] ++ inferiors.znver2;
znver4 = lib.unique ([ "znver3" "x86-64-v4" ] ++ inferiors.znver3 ++ inferiors.x86-64-v4);
znver5 = [ "znver4" ] ++ inferiors.znver4;
znver1 = [ "skylake" ] ++ inferiors.skylake; # Includes haswell and x86-64-v3
znver2 = [ "znver1" ] ++ inferiors.znver1;
znver3 = [ "znver2" ] ++ inferiors.znver2;
znver4 = lib.unique (
[
"znver3"
"x86-64-v4"
]
++ inferiors.znver3
++ inferiors.x86-64-v4
);
znver5 = [ "znver4" ] ++ inferiors.znver4;
# other
armv5te = [ ];
armv6 = [ ];
armv7-a = [ ];
armv8-a = [ ];
mips32 = [ ];
loongson2f = [ ];
armv5te = [ ];
armv6 = [ ];
armv7-a = [ ];
armv8-a = [ ];
mips32 = [ ];
loongson2f = [ ];
};
predicates = let
featureSupport = feature: x: builtins.elem feature features.${x} or [];
in {
sse3Support = featureSupport "sse3";
ssse3Support = featureSupport "ssse3";
sse4_1Support = featureSupport "sse4_1";
sse4_2Support = featureSupport "sse4_2";
sse4_aSupport = featureSupport "sse4a";
avxSupport = featureSupport "avx";
avx2Support = featureSupport "avx2";
avx512Support = featureSupport "avx512";
aesSupport = featureSupport "aes";
fmaSupport = featureSupport "fma";
fma4Support = featureSupport "fma4";
};
predicates =
let
featureSupport = feature: x: builtins.elem feature features.${x} or [ ];
in
{
sse3Support = featureSupport "sse3";
ssse3Support = featureSupport "ssse3";
sse4_1Support = featureSupport "sse4_1";
sse4_2Support = featureSupport "sse4_2";
sse4_aSupport = featureSupport "sse4a";
avxSupport = featureSupport "avx";
avx2Support = featureSupport "avx2";
avx512Support = featureSupport "avx512";
aesSupport = featureSupport "aes";
fmaSupport = featureSupport "fma";
fma4Support = featureSupport "fma4";
};
}

View File

@ -42,8 +42,10 @@ let
both arguments have been `elaborate`-d.
*/
equals =
let removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
in a: b: removeFunctions a == removeFunctions b;
let
removeFunctions = a: filterAttrs (_: v: !isFunction v) a;
in
a: b: removeFunctions a == removeFunctions b;
/**
List of all Nix system doubles the nixpkgs flake will expose the package set
@ -57,8 +59,8 @@ let
# Turn localSystem or crossSystem, which could be system-string or attrset, into
# attrset.
systemToAttrs = systemOrArgs:
if isAttrs systemOrArgs then systemOrArgs else { system = systemOrArgs; };
systemToAttrs =
systemOrArgs: if isAttrs systemOrArgs then systemOrArgs else { system = systemOrArgs; };
# Elaborate a `localSystem` or `crossSystem` so that it contains everything
# necessary.
@ -66,370 +68,478 @@ let
# `parsed` is inferred from args, both because there are two options with one
# clearly preferred, and to prevent cycles. A simpler fixed point where the RHS
# always just used `final.*` would fail on both counts.
elaborate = systemOrArgs: let
allArgs = systemToAttrs systemOrArgs;
elaborate =
systemOrArgs:
let
allArgs = systemToAttrs systemOrArgs;
# Those two will always be derived from "config", if given, so they should NOT
# be overridden further down with "// args".
args = builtins.removeAttrs allArgs [ "parsed" "system" ];
# Those two will always be derived from "config", if given, so they should NOT
# be overridden further down with "// args".
args = builtins.removeAttrs allArgs [
"parsed"
"system"
];
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
rust = args.rust or args.rustc or {};
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
rust = args.rust or args.rustc or { };
final = {
# Prefer to parse `config` as it is strictly more informative.
parsed = parse.mkSystemFromString (args.config or allArgs.system);
# This can be losslessly-extracted from `parsed` iff parsing succeeds.
system = parse.doubleFromSystem final.parsed;
# TODO: This currently can't be losslessly-extracted from `parsed`, for example
# because of -mingw32.
config = parse.tripleFromSystem final.parsed;
# Determine whether we can execute binaries built for the provided platform.
canExecute = platform:
final.isAndroid == platform.isAndroid &&
parse.isCompatible final.parsed.cpu platform.parsed.cpu
&& final.parsed.kernel == platform.parsed.kernel;
isCompatible = _: throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
# Derived meta-data
useLLVM = final.isFreeBSD || final.isOpenBSD;
final =
{
# Prefer to parse `config` as it is strictly more informative.
parsed = parse.mkSystemFromString (args.config or allArgs.system);
# This can be losslessly-extracted from `parsed` iff parsing succeeds.
system = parse.doubleFromSystem final.parsed;
# TODO: This currently can't be losslessly-extracted from `parsed`, for example
# because of -mingw32.
config = parse.tripleFromSystem final.parsed;
# Determine whether we can execute binaries built for the provided platform.
canExecute =
platform:
final.isAndroid == platform.isAndroid
&& parse.isCompatible final.parsed.cpu platform.parsed.cpu
&& final.parsed.kernel == platform.parsed.kernel;
isCompatible =
_:
throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
# Derived meta-data
useLLVM = final.isFreeBSD || final.isOpenBSD;
libc =
/**/ if final.isDarwin then "libSystem"
else if final.isMinGW then "msvcrt"
else if final.isWasi then "wasilibc"
else if final.isWasm && !final.isWasi then null
else if final.isRedox then "relibc"
else if final.isMusl then "musl"
else if final.isUClibc then "uclibc"
else if final.isAndroid then "bionic"
else if final.isLLVMLibc then "llvm"
else if final.isLinux /* default */ then "glibc"
else if final.isFreeBSD then "fblibc"
else if final.isOpenBSD then "oblibc"
else if final.isNetBSD then "nblibc"
else if final.isAvr then "avrlibc"
else if final.isGhcjs then null
else if final.isNone then "newlib"
# TODO(@Ericson2314) think more about other operating systems
else "native/impure";
# Choose what linker we wish to use by default. Someday we might also
# choose the C compiler, runtime library, C++ standard library, etc. in
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
# the monolithic GCC build we cannot actually make those choices
# independently, so we are just doing `linker` and keeping `useLLVM` for
# now.
linker =
/**/ if final.useLLVM or false then "lld"
else if final.isDarwin then "cctools"
# "bfd" and "gold" both come from GNU binutils. The existence of Gold
# is why we use the more obscure "bfd" and not "binutils" for this
# choice.
else "bfd";
# The standard lib directory name that non-nixpkgs binaries distributed
# for this platform normally assume.
libDir = if final.isLinux then
if final.isx86_64 || final.isMips64 || final.isPower64
then "lib64"
else "lib"
else null;
extensions = optionalAttrs final.hasSharedLibraries {
sharedLibrary =
if final.isDarwin then ".dylib"
else if final.isWindows then ".dll"
else ".so";
} // {
staticLibrary =
/**/ if final.isWindows then ".lib"
else ".a";
library =
/**/ if final.isStatic then final.extensions.staticLibrary
else final.extensions.sharedLibrary;
executable =
/**/ if final.isWindows then ".exe"
else "";
};
# Misc boolean options
useAndroidPrebuilt = false;
useiOSPrebuilt = false;
libc =
if final.isDarwin then
"libSystem"
else if final.isMinGW then
"msvcrt"
else if final.isWasi then
"wasilibc"
else if final.isWasm && !final.isWasi then
null
else if final.isRedox then
"relibc"
else if final.isMusl then
"musl"
else if final.isUClibc then
"uclibc"
else if final.isAndroid then
"bionic"
else if final.isLLVMLibc then
"llvm"
else if
final.isLinux # default
then
"glibc"
else if final.isFreeBSD then
"fblibc"
else if final.isOpenBSD then
"oblibc"
else if final.isNetBSD then
"nblibc"
else if final.isAvr then
"avrlibc"
else if final.isGhcjs then
null
else if final.isNone then
"newlib"
# TODO(@Ericson2314) think more about other operating systems
else
"native/impure";
# Choose what linker we wish to use by default. Someday we might also
# choose the C compiler, runtime library, C++ standard library, etc. in
# this way, nice and orthogonally, and deprecate `useLLVM`. But due to
# the monolithic GCC build we cannot actually make those choices
# independently, so we are just doing `linker` and keeping `useLLVM` for
# now.
linker =
if final.useLLVM or false then
"lld"
else if final.isDarwin then
"cctools"
# "bfd" and "gold" both come from GNU binutils. The existence of Gold
# is why we use the more obscure "bfd" and not "binutils" for this
# choice.
else
"bfd";
# The standard lib directory name that non-nixpkgs binaries distributed
# for this platform normally assume.
libDir =
if final.isLinux then
if final.isx86_64 || final.isMips64 || final.isPower64 then "lib64" else "lib"
else
null;
extensions =
optionalAttrs final.hasSharedLibraries {
sharedLibrary =
if final.isDarwin then
".dylib"
else if final.isWindows then
".dll"
else
".so";
}
// {
staticLibrary = if final.isWindows then ".lib" else ".a";
library = if final.isStatic then final.extensions.staticLibrary else final.extensions.sharedLibrary;
executable = if final.isWindows then ".exe" else "";
};
# Misc boolean options
useAndroidPrebuilt = false;
useiOSPrebuilt = false;
# Output from uname
uname = {
# uname -s
system = {
linux = "Linux";
windows = "Windows";
darwin = "Darwin";
netbsd = "NetBSD";
freebsd = "FreeBSD";
openbsd = "OpenBSD";
wasi = "Wasi";
redox = "Redox";
genode = "Genode";
}.${final.parsed.kernel.name} or null;
# Output from uname
uname = {
# uname -s
system =
{
linux = "Linux";
windows = "Windows";
darwin = "Darwin";
netbsd = "NetBSD";
freebsd = "FreeBSD";
openbsd = "OpenBSD";
wasi = "Wasi";
redox = "Redox";
genode = "Genode";
}
.${final.parsed.kernel.name} or null;
# uname -m
processor =
if final.isPower64
then "ppc64${optionalString final.isLittleEndian "le"}"
else if final.isPower
then "ppc${optionalString final.isLittleEndian "le"}"
else if final.isMips64
then "mips64" # endianness is *not* included on mips64
else final.parsed.cpu.name;
# uname -m
processor =
if final.isPower64 then
"ppc64${optionalString final.isLittleEndian "le"}"
else if final.isPower then
"ppc${optionalString final.isLittleEndian "le"}"
else if final.isMips64 then
"mips64" # endianness is *not* included on mips64
else
final.parsed.cpu.name;
# uname -r
release = null;
};
# It is important that hasSharedLibraries==false when the platform has no
# dynamic library loader. Various tools (including the gcc build system)
# have knowledge of which platforms are incapable of dynamic linking, and
# will still build on/for those platforms with --enable-shared, but simply
# omit any `.so` build products such as libgcc_s.so. When that happens,
# it causes hard-to-troubleshoot build failures.
hasSharedLibraries = with final;
(isAndroid || isGnu || isMusl # Linux (allows multiple libcs)
|| isDarwin || isSunOS || isOpenBSD || isFreeBSD || isNetBSD # BSDs
|| isCygwin || isMinGW || isWindows # Windows
|| isWasm # WASM
) && !isStatic;
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
# addition of the `staticMarker` (see make-derivation.nix). Some
# platforms, like embedded machines without a libc (e.g. arm-none-eabi)
# don't support dynamic linking, but don't get the `staticMarker`.
# `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always
# has the `staticMarker`.
isStatic = final.isWasi || final.isRedox;
# Just a guess, based on `system`
inherit
({
linux-kernel = args.linux-kernel or {};
gcc = args.gcc or {};
} // platforms.select final)
linux-kernel gcc;
# TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
rustc = args.rustc or {};
linuxArch =
if final.isAarch32 then "arm"
else if final.isAarch64 then "arm64"
else if final.isx86_32 then "i386"
else if final.isx86_64 then "x86_64"
# linux kernel does not distinguish microblaze/microblazeel
else if final.isMicroBlaze then "microblaze"
else if final.isMips32 then "mips"
else if final.isMips64 then "mips" # linux kernel does not distinguish mips32/mips64
else if final.isPower then "powerpc"
else if final.isRiscV then "riscv"
else if final.isS390 then "s390"
else if final.isLoongArch64 then "loongarch"
else final.parsed.cpu.name;
# https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
ubootArch =
if final.isx86_32 then "x86" # not i386
else if final.isMips64 then "mips64" # uboot *does* distinguish between mips32/mips64
else final.linuxArch; # other cases appear to agree with linuxArch
qemuArch =
if final.isAarch32 then "arm"
else if final.isS390 && !final.isS390x then null
else if final.isx86_64 then "x86_64"
else if final.isx86 then "i386"
else if final.isMips64n32 then "mipsn32${optionalString final.isLittleEndian "el"}"
else if final.isMips64 then "mips64${optionalString final.isLittleEndian "el"}"
else final.uname.processor;
# Name used by UEFI for architectures.
efiArch =
if final.isx86_32 then "ia32"
else if final.isx86_64 then "x64"
else if final.isAarch32 then "arm"
else if final.isAarch64 then "aa64"
else final.parsed.cpu.name;
darwinArch = {
armv7a = "armv7";
aarch64 = "arm64";
}.${final.parsed.cpu.name} or final.parsed.cpu.name;
darwinPlatform =
if final.isMacOS then "macos"
else if final.isiOS then "ios"
else null;
# The canonical name for this attribute is darwinSdkVersion, but some
# platforms define the old name "sdkVer".
darwinSdkVersion = final.sdkVer or "11.3";
darwinMinVersion = final.darwinSdkVersion;
darwinMinVersionVariable =
if final.isMacOS then "MACOSX_DEPLOYMENT_TARGET"
else if final.isiOS then "IPHONEOS_DEPLOYMENT_TARGET"
else null;
# Remove before 25.05
androidSdkVersion =
if (args ? sdkVer && !args ? androidSdkVersion) then
throw "For android `sdkVer` has been renamed to `androidSdkVersion`"
else if (args ? androidSdkVersion) then
args.androidSdkVersion
else
null;
androidNdkVersion =
if (args ? ndkVer && !args ? androidNdkVersion) then
throw "For android `ndkVer` has been renamed to `androidNdkVersion`"
else if (args ? androidSdkVersion) then
args.androidNdkVersion
else
null;
} // (
let
selectEmulator = pkgs:
let
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
in
# Note: we guarantee that the return value is either `null` or a path
# to an emulator program. That is, if an emulator requires additional
# arguments, a wrapper should be used.
if pkgs.stdenv.hostPlatform.canExecute final
then lib.getExe (pkgs.writeShellScriptBin "exec" ''exec "$@"'')
else if final.isWindows
then "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null
then "${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
else if final.isWasi
then "${pkgs.wasmtime}/bin/wasmtime"
else if final.isMmix
then "${pkgs.mmixware}/bin/mmix"
else null;
in {
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
# whether final.emulator pkgs.pkgsStatic works
staticEmulatorAvailable = pkgs: final.emulatorAvailable pkgs
&& (final.isLinux || final.isWasi || final.isMmix);
emulator = pkgs:
if (final.emulatorAvailable pkgs)
then selectEmulator pkgs
else throw "Don't know how to run ${final.config} executables.";
}) // mapAttrs (n: v: v final.parsed) inspect.predicates
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
// args // {
rust = rust // {
# Once args.rustc.platform.target-family is deprecated and
# removed, there will no longer be any need to modify any
# values from args.rust.platform, so we can drop all the
# "args ? rust" etc. checks, and merge args.rust.platform in
# /after/.
platform = rust.platform or {} // {
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
arch =
/**/ if rust ? platform then rust.platform.arch
else if final.isAarch32 then "arm"
else if final.isMips64 then "mips64" # never add "el" suffix
else if final.isPower64 then "powerpc64" # never add "le" suffix
else final.parsed.cpu.name;
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
os =
/**/ if rust ? platform then rust.platform.os or "none"
else if final.isDarwin then "macos"
else if final.isWasm && !final.isWasi then "unknown" # Needed for {wasm32,wasm64}-unknown-unknown.
else final.parsed.kernel.name;
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
target-family =
/**/ if args ? rust.platform.target-family then args.rust.platform.target-family
else if args ? rustc.platform.target-family
then
(
# Since https://github.com/rust-lang/rust/pull/84072
# `target-family` is a list instead of single value.
let
f = args.rustc.platform.target-family;
in
if isList f then f else [ f ]
)
else optional final.isUnix "unix"
++ optional final.isWindows "windows"
++ optional final.isWasm "wasm";
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
vendor = let
inherit (final.parsed) vendor;
in rust.platform.vendor or {
"w64" = "pc";
}.${vendor.name} or vendor.name;
# uname -r
release = null;
};
# The name of the rust target, even if it is custom. Adjustments are
# because rust has slightly different naming conventions than we do.
rustcTarget = let
inherit (final.parsed) cpu kernel abi;
cpu_ = rust.platform.arch or {
"armv7a" = "armv7";
"armv7l" = "armv7";
"armv6l" = "arm";
"armv5tel" = "armv5te";
"riscv32" = "riscv32gc";
"riscv64" = "riscv64gc";
}.${cpu.name} or cpu.name;
vendor_ = final.rust.platform.vendor;
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
# It is important that hasSharedLibraries==false when the platform has no
# dynamic library loader. Various tools (including the gcc build system)
# have knowledge of which platforms are incapable of dynamic linking, and
# will still build on/for those platforms with --enable-shared, but simply
# omit any `.so` build products such as libgcc_s.so. When that happens,
# it causes hard-to-troubleshoot build failures.
hasSharedLibraries =
with final;
(
isAndroid
|| isGnu
|| isMusl # Linux (allows multiple libcs)
|| isDarwin
|| isSunOS
|| isOpenBSD
|| isFreeBSD
|| isNetBSD # BSDs
|| isCygwin
|| isMinGW
|| isWindows # Windows
|| isWasm # WASM
)
&& !isStatic;
# The difference between `isStatic` and `hasSharedLibraries` is mainly the
# addition of the `staticMarker` (see make-derivation.nix). Some
# platforms, like embedded machines without a libc (e.g. arm-none-eabi)
# don't support dynamic linking, but don't get the `staticMarker`.
# `pkgsStatic` sets `isStatic=true`, so `pkgsStatic.hostPlatform` always
# has the `staticMarker`.
isStatic = final.isWasi || final.isRedox;
# Just a guess, based on `system`
inherit
(
{
linux-kernel = args.linux-kernel or { };
gcc = args.gcc or { };
}
// platforms.select final
)
linux-kernel
gcc
;
# TODO: remove after 23.05 is EOL, with an error pointing to the rust.* attrs.
rustc = args.rustc or { };
linuxArch =
if final.isAarch32 then
"arm"
else if final.isAarch64 then
"arm64"
else if final.isx86_32 then
"i386"
else if final.isx86_64 then
"x86_64"
# linux kernel does not distinguish microblaze/microblazeel
else if final.isMicroBlaze then
"microblaze"
else if final.isMips32 then
"mips"
else if final.isMips64 then
"mips" # linux kernel does not distinguish mips32/mips64
else if final.isPower then
"powerpc"
else if final.isRiscV then
"riscv"
else if final.isS390 then
"s390"
else if final.isLoongArch64 then
"loongarch"
else
final.parsed.cpu.name;
# https://source.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106
ubootArch =
if final.isx86_32 then
"x86" # not i386
else if final.isMips64 then
"mips64" # uboot *does* distinguish between mips32/mips64
else
final.linuxArch; # other cases appear to agree with linuxArch
qemuArch =
if final.isAarch32 then
"arm"
else if final.isS390 && !final.isS390x then
null
else if final.isx86_64 then
"x86_64"
else if final.isx86 then
"i386"
else if final.isMips64n32 then
"mipsn32${optionalString final.isLittleEndian "el"}"
else if final.isMips64 then
"mips64${optionalString final.isLittleEndian "el"}"
else
final.uname.processor;
# Name used by UEFI for architectures.
efiArch =
if final.isx86_32 then
"ia32"
else if final.isx86_64 then
"x64"
else if final.isAarch32 then
"arm"
else if final.isAarch64 then
"aa64"
else
final.parsed.cpu.name;
darwinArch =
{
armv7a = "armv7";
aarch64 = "arm64";
}
.${final.parsed.cpu.name} or final.parsed.cpu.name;
darwinPlatform =
if final.isMacOS then
"macos"
else if final.isiOS then
"ios"
else
null;
# The canonical name for this attribute is darwinSdkVersion, but some
# platforms define the old name "sdkVer".
darwinSdkVersion = final.sdkVer or "11.3";
darwinMinVersion = final.darwinSdkVersion;
darwinMinVersionVariable =
if final.isMacOS then
"MACOSX_DEPLOYMENT_TARGET"
else if final.isiOS then
"IPHONEOS_DEPLOYMENT_TARGET"
else
null;
# Remove before 25.05
androidSdkVersion =
if (args ? sdkVer && !args ? androidSdkVersion) then
throw "For android `sdkVer` has been renamed to `androidSdkVersion`"
else if (args ? androidSdkVersion) then
args.androidSdkVersion
else
null;
androidNdkVersion =
if (args ? ndkVer && !args ? androidNdkVersion) then
throw "For android `ndkVer` has been renamed to `androidNdkVersion`"
else if (args ? androidSdkVersion) then
args.androidNdkVersion
else
null;
}
// (
let
selectEmulator =
pkgs:
let
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
in
# Note: we guarantee that the return value is either `null` or a path
# to an emulator program. That is, if an emulator requires additional
# arguments, a wrapper should be used.
if pkgs.stdenv.hostPlatform.canExecute final then
lib.getExe (pkgs.writeShellScriptBin "exec" ''exec "$@"'')
else if final.isWindows then
"${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null then
"${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
else if final.isWasi then
"${pkgs.wasmtime}/bin/wasmtime"
else if final.isMmix then
"${pkgs.mmixware}/bin/mmix"
else
null;
in
args.rust.rustcTarget or
args.rustc.config or (
# Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
# We cannot know which subversion does the user want, and
# currently use WASI 0.1 as default for compatibility. Custom
# users can set `rust.rustcTarget` to override it.
if final.isWasi
then "${cpu_}-wasip1"
else "${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"
);
{
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
# The name of the rust target if it is standard, or the json file
# containing the custom target spec.
rustcTargetSpec = rust.rustcTargetSpec or (
/**/ if rust ? platform
then builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
else final.rust.rustcTarget);
# whether final.emulator pkgs.pkgsStatic works
staticEmulatorAvailable =
pkgs: final.emulatorAvailable pkgs && (final.isLinux || final.isWasi || final.isMmix);
# The name of the rust target if it is standard, or the
# basename of the file containing the custom target spec,
# without the .json extension.
#
# This is the name used by Cargo for target subdirectories.
cargoShortTarget =
removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
emulator =
pkgs:
if (final.emulatorAvailable pkgs) then
selectEmulator pkgs
else
throw "Don't know how to run ${final.config} executables.";
# When used as part of an environment variable name, triples are
# uppercased and have all hyphens replaced by underscores:
#
# https://github.com/rust-lang/cargo/pull/9169
# https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
cargoEnvVarTarget =
replaceStrings ["-"] ["_"]
(toUpper final.rust.cargoShortTarget);
}
)
// mapAttrs (n: v: v final.parsed) inspect.predicates
// mapAttrs (n: v: v final.gcc.arch or "default") architectures.predicates
// args
// {
rust = rust // {
# Once args.rustc.platform.target-family is deprecated and
# removed, there will no longer be any need to modify any
# values from args.rust.platform, so we can drop all the
# "args ? rust" etc. checks, and merge args.rust.platform in
# /after/.
platform = rust.platform or { } // {
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
arch =
if rust ? platform then
rust.platform.arch
else if final.isAarch32 then
"arm"
else if final.isMips64 then
"mips64" # never add "el" suffix
else if final.isPower64 then
"powerpc64" # never add "le" suffix
else
final.parsed.cpu.name;
# True if the target is no_std
# https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
isNoStdTarget =
any (t: hasInfix t final.rust.rustcTarget) ["-none" "nvptx" "switch" "-uefi"];
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_os
os =
if rust ? platform then
rust.platform.os or "none"
else if final.isDarwin then
"macos"
else if final.isWasm && !final.isWasi then
"unknown" # Needed for {wasm32,wasm64}-unknown-unknown.
else
final.parsed.kernel.name;
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
target-family =
if args ? rust.platform.target-family then
args.rust.platform.target-family
else if args ? rustc.platform.target-family then
(
# Since https://github.com/rust-lang/rust/pull/84072
# `target-family` is a list instead of single value.
let
f = args.rustc.platform.target-family;
in
if isList f then f else [ f ]
)
else
optional final.isUnix "unix" ++ optional final.isWindows "windows" ++ optional final.isWasm "wasm";
# https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
vendor =
let
inherit (final.parsed) vendor;
in
rust.platform.vendor or {
"w64" = "pc";
}
.${vendor.name} or vendor.name;
};
# The name of the rust target, even if it is custom. Adjustments are
# because rust has slightly different naming conventions than we do.
rustcTarget =
let
inherit (final.parsed) cpu kernel abi;
cpu_ =
rust.platform.arch or {
"armv7a" = "armv7";
"armv7l" = "armv7";
"armv6l" = "arm";
"armv5tel" = "armv5te";
"riscv32" = "riscv32gc";
"riscv64" = "riscv64gc";
}
.${cpu.name} or cpu.name;
vendor_ = final.rust.platform.vendor;
# TODO: deprecate args.rustc in favour of args.rust after 23.05 is EOL.
in
args.rust.rustcTarget or args.rustc.config or (
# Rust uses `wasm32-wasip?` rather than `wasm32-unknown-wasi`.
# We cannot know which subversion does the user want, and
# currently use WASI 0.1 as default for compatibility. Custom
# users can set `rust.rustcTarget` to override it.
if final.isWasi then
"${cpu_}-wasip1"
else
"${cpu_}-${vendor_}-${kernel.name}${optionalString (abi.name != "unknown") "-${abi.name}"}"
);
# The name of the rust target if it is standard, or the json file
# containing the custom target spec.
rustcTargetSpec =
rust.rustcTargetSpec or (
if rust ? platform then
builtins.toFile (final.rust.rustcTarget + ".json") (toJSON rust.platform)
else
final.rust.rustcTarget
);
# The name of the rust target if it is standard, or the
# basename of the file containing the custom target spec,
# without the .json extension.
#
# This is the name used by Cargo for target subdirectories.
cargoShortTarget = removeSuffix ".json" (baseNameOf "${final.rust.rustcTargetSpec}");
# When used as part of an environment variable name, triples are
# uppercased and have all hyphens replaced by underscores:
#
# https://github.com/rust-lang/cargo/pull/9169
# https://github.com/rust-lang/cargo/issues/8285#issuecomment-634202431
cargoEnvVarTarget = replaceStrings [ "-" ] [ "_" ] (toUpper final.rust.cargoShortTarget);
# True if the target is no_std
# https://github.com/rust-lang/rust/blob/2e44c17c12cec45b6a682b1e53a04ac5b5fcc9d2/src/bootstrap/config.rs#L415-L421
isNoStdTarget = any (t: hasInfix t final.rust.rustcTarget) [
"-none"
"nvptx"
"switch"
"-uefi"
];
};
};
};
in assert final.useAndroidPrebuilt -> final.isAndroid;
assert foldl
(pass: { assertion, message }:
if assertion final
then pass
else throw message)
true
(final.parsed.abi.assertions or []);
in
assert final.useAndroidPrebuilt -> final.isAndroid;
assert foldl (pass: { assertion, message }: if assertion final then pass else throw message) true (
final.parsed.abi.assertions or [ ]
);
final;
in

View File

@ -7,16 +7,24 @@ let
all = [
# Cygwin
"i686-cygwin" "x86_64-cygwin"
"i686-cygwin"
"x86_64-cygwin"
# Darwin
"x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin"
"x86_64-darwin"
"i686-darwin"
"aarch64-darwin"
"armv7a-darwin"
# FreeBSD
"i686-freebsd" "x86_64-freebsd" "aarch64-freebsd"
"i686-freebsd"
"x86_64-freebsd"
"aarch64-freebsd"
# Genode
"aarch64-genode" "i686-genode" "x86_64-genode"
"aarch64-genode"
"i686-genode"
"x86_64-genode"
# illumos
"x86_64-solaris"
@ -25,94 +33,163 @@ let
"javascript-ghcjs"
# Linux
"aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux"
"armv7l-linux" "i686-linux" "loongarch64-linux" "m68k-linux" "microblaze-linux"
"microblazeel-linux" "mips-linux" "mips64-linux" "mips64el-linux"
"mipsel-linux" "powerpc64-linux" "powerpc64le-linux" "riscv32-linux"
"riscv64-linux" "s390-linux" "s390x-linux" "x86_64-linux"
"aarch64-linux"
"armv5tel-linux"
"armv6l-linux"
"armv7a-linux"
"armv7l-linux"
"i686-linux"
"loongarch64-linux"
"m68k-linux"
"microblaze-linux"
"microblazeel-linux"
"mips-linux"
"mips64-linux"
"mips64el-linux"
"mipsel-linux"
"powerpc64-linux"
"powerpc64le-linux"
"riscv32-linux"
"riscv64-linux"
"s390-linux"
"s390x-linux"
"x86_64-linux"
# MMIXware
"mmix-mmixware"
# NetBSD
"aarch64-netbsd" "armv6l-netbsd" "armv7a-netbsd" "armv7l-netbsd"
"i686-netbsd" "m68k-netbsd" "mipsel-netbsd" "powerpc-netbsd"
"riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd"
"aarch64-netbsd"
"armv6l-netbsd"
"armv7a-netbsd"
"armv7l-netbsd"
"i686-netbsd"
"m68k-netbsd"
"mipsel-netbsd"
"powerpc-netbsd"
"riscv32-netbsd"
"riscv64-netbsd"
"x86_64-netbsd"
# none
"aarch64_be-none" "aarch64-none" "arm-none" "armv6l-none" "avr-none" "i686-none"
"microblaze-none" "microblazeel-none" "mips-none" "mips64-none" "msp430-none" "or1k-none" "m68k-none"
"powerpc-none" "powerpcle-none" "riscv32-none" "riscv64-none" "rx-none"
"s390-none" "s390x-none" "vc4-none" "x86_64-none"
"aarch64_be-none"
"aarch64-none"
"arm-none"
"armv6l-none"
"avr-none"
"i686-none"
"microblaze-none"
"microblazeel-none"
"mips-none"
"mips64-none"
"msp430-none"
"or1k-none"
"m68k-none"
"powerpc-none"
"powerpcle-none"
"riscv32-none"
"riscv64-none"
"rx-none"
"s390-none"
"s390x-none"
"vc4-none"
"x86_64-none"
# OpenBSD
"i686-openbsd" "x86_64-openbsd"
"i686-openbsd"
"x86_64-openbsd"
# Redox
"x86_64-redox"
# WASI
"wasm64-wasi" "wasm32-wasi"
"wasm64-wasi"
"wasm32-wasi"
# Windows
"aarch64-windows" "x86_64-windows" "i686-windows"
"aarch64-windows"
"x86_64-windows"
"i686-windows"
];
allParsed = map parse.mkSystemFromString all;
filterDoubles = f: map parse.doubleFromSystem (lists.filter f allParsed);
in {
in
{
inherit all;
none = [];
none = [ ];
arm = filterDoubles predicates.isAarch32;
armv7 = filterDoubles predicates.isArmv7;
aarch = filterDoubles predicates.isAarch;
aarch64 = filterDoubles predicates.isAarch64;
x86 = filterDoubles predicates.isx86;
i686 = filterDoubles predicates.isi686;
x86_64 = filterDoubles predicates.isx86_64;
microblaze = filterDoubles predicates.isMicroBlaze;
mips = filterDoubles predicates.isMips;
mmix = filterDoubles predicates.isMmix;
power = filterDoubles predicates.isPower;
riscv = filterDoubles predicates.isRiscV;
riscv32 = filterDoubles predicates.isRiscV32;
riscv64 = filterDoubles predicates.isRiscV64;
rx = filterDoubles predicates.isRx;
vc4 = filterDoubles predicates.isVc4;
or1k = filterDoubles predicates.isOr1k;
m68k = filterDoubles predicates.isM68k;
s390 = filterDoubles predicates.isS390;
s390x = filterDoubles predicates.isS390x;
loongarch64 = filterDoubles predicates.isLoongArch64;
js = filterDoubles predicates.isJavaScript;
arm = filterDoubles predicates.isAarch32;
armv7 = filterDoubles predicates.isArmv7;
aarch = filterDoubles predicates.isAarch;
aarch64 = filterDoubles predicates.isAarch64;
x86 = filterDoubles predicates.isx86;
i686 = filterDoubles predicates.isi686;
x86_64 = filterDoubles predicates.isx86_64;
microblaze = filterDoubles predicates.isMicroBlaze;
mips = filterDoubles predicates.isMips;
mmix = filterDoubles predicates.isMmix;
power = filterDoubles predicates.isPower;
riscv = filterDoubles predicates.isRiscV;
riscv32 = filterDoubles predicates.isRiscV32;
riscv64 = filterDoubles predicates.isRiscV64;
rx = filterDoubles predicates.isRx;
vc4 = filterDoubles predicates.isVc4;
or1k = filterDoubles predicates.isOr1k;
m68k = filterDoubles predicates.isM68k;
s390 = filterDoubles predicates.isS390;
s390x = filterDoubles predicates.isS390x;
loongarch64 = filterDoubles predicates.isLoongArch64;
js = filterDoubles predicates.isJavaScript;
bigEndian = filterDoubles predicates.isBigEndian;
littleEndian = filterDoubles predicates.isLittleEndian;
bigEndian = filterDoubles predicates.isBigEndian;
littleEndian = filterDoubles predicates.isLittleEndian;
cygwin = filterDoubles predicates.isCygwin;
darwin = filterDoubles predicates.isDarwin;
freebsd = filterDoubles predicates.isFreeBSD;
cygwin = filterDoubles predicates.isCygwin;
darwin = filterDoubles predicates.isDarwin;
freebsd = filterDoubles predicates.isFreeBSD;
# Should be better, but MinGW is unclear.
gnu = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabin32; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabi64; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv1; })
++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnuabielfv2; });
illumos = filterDoubles predicates.isSunOS;
linux = filterDoubles predicates.isLinux;
netbsd = filterDoubles predicates.isNetBSD;
openbsd = filterDoubles predicates.isOpenBSD;
unix = filterDoubles predicates.isUnix;
wasi = filterDoubles predicates.isWasi;
redox = filterDoubles predicates.isRedox;
windows = filterDoubles predicates.isWindows;
genode = filterDoubles predicates.isGenode;
gnu =
filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnu;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnueabi;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnueabihf;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabin32;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabi64;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabielfv1;
})
++ filterDoubles (matchAttrs {
kernel = parse.kernels.linux;
abi = parse.abis.gnuabielfv2;
});
illumos = filterDoubles predicates.isSunOS;
linux = filterDoubles predicates.isLinux;
netbsd = filterDoubles predicates.isNetBSD;
openbsd = filterDoubles predicates.isOpenBSD;
unix = filterDoubles predicates.isUnix;
wasi = filterDoubles predicates.isWasi;
redox = filterDoubles predicates.isRedox;
windows = filterDoubles predicates.isWindows;
genode = filterDoubles predicates.isGenode;
embedded = filterDoubles predicates.isNone;
embedded = filterDoubles predicates.isNone;
}

View File

@ -26,7 +26,9 @@ rec {
};
ppc64-musl = {
config = "powerpc64-unknown-linux-musl";
gcc = { abi = "elfv2"; };
gcc = {
abi = "elfv2";
};
};
sheevaplug = {
@ -95,16 +97,28 @@ rec {
} // platforms.fuloong2f_n32;
# can execute on 32bit chip
mips-linux-gnu = { config = "mips-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
mipsel-linux-gnu = { config = "mipsel-unknown-linux-gnu"; } // platforms.gcc_mips32r2_o32;
mips-linux-gnu = {
config = "mips-unknown-linux-gnu";
} // platforms.gcc_mips32r2_o32;
mipsel-linux-gnu = {
config = "mipsel-unknown-linux-gnu";
} // platforms.gcc_mips32r2_o32;
# require 64bit chip (for more registers, 64-bit floating point, 64-bit "long long") but use 32bit pointers
mips64-linux-gnuabin32 = { config = "mips64-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
mips64el-linux-gnuabin32 = { config = "mips64el-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r2_n32;
mips64-linux-gnuabin32 = {
config = "mips64-unknown-linux-gnuabin32";
} // platforms.gcc_mips64r2_n32;
mips64el-linux-gnuabin32 = {
config = "mips64el-unknown-linux-gnuabin32";
} // platforms.gcc_mips64r2_n32;
# 64bit pointers
mips64-linux-gnuabi64 = { config = "mips64-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
mips64el-linux-gnuabi64 = { config = "mips64el-unknown-linux-gnuabi64"; } // platforms.gcc_mips64r2_64;
mips64-linux-gnuabi64 = {
config = "mips64-unknown-linux-gnuabi64";
} // platforms.gcc_mips64r2_64;
mips64el-linux-gnuabi64 = {
config = "mips64el-unknown-linux-gnuabi64";
} // platforms.gcc_mips64r2_64;
muslpi = raspberryPi // {
config = "armv6l-unknown-linux-musleabihf";
@ -114,12 +128,20 @@ rec {
config = "aarch64-unknown-linux-musl";
};
gnu64 = { config = "x86_64-unknown-linux-gnu"; };
gnu64 = {
config = "x86_64-unknown-linux-gnu";
};
gnu64_simplekernel = gnu64 // platforms.pc_simplekernel; # see test/cross/default.nix
gnu32 = { config = "i686-unknown-linux-gnu"; };
gnu32 = {
config = "i686-unknown-linux-gnu";
};
musl64 = { config = "x86_64-unknown-linux-musl"; };
musl32 = { config = "i686-unknown-linux-musl"; };
musl64 = {
config = "x86_64-unknown-linux-musl";
};
musl32 = {
config = "i686-unknown-linux-musl";
};
riscv64 = riscv "64";
riscv32 = riscv "32";
@ -294,13 +316,13 @@ rec {
aarch64-darwin = {
config = "aarch64-apple-darwin";
xcodePlatform = "MacOSX";
platform = {};
platform = { };
};
x86_64-darwin = {
config = "x86_64-apple-darwin";
xcodePlatform = "MacOSX";
platform = {};
platform = { };
};
#

View File

@ -38,124 +38,429 @@ rec {
# `lib.attrsets.matchAttrs`, which requires a match on *all* attributes of
# the product.
isi686 = { cpu = cpuTypes.i686; };
isx86_32 = { cpu = { family = "x86"; bits = 32; }; };
isx86_64 = { cpu = { family = "x86"; bits = 64; }; };
isPower = { cpu = { family = "power"; }; };
isPower64 = { cpu = { family = "power"; bits = 64; }; };
isi686 = {
cpu = cpuTypes.i686;
};
isx86_32 = {
cpu = {
family = "x86";
bits = 32;
};
};
isx86_64 = {
cpu = {
family = "x86";
bits = 64;
};
};
isPower = {
cpu = {
family = "power";
};
};
isPower64 = {
cpu = {
family = "power";
bits = 64;
};
};
# This ABI is the default in NixOS PowerPC64 BE, but not on mainline GCC,
# so it sometimes causes issues in certain packages that makes the wrong
# assumption on the used ABI.
isAbiElfv2 = [
{ abi = { abi = "elfv2"; }; }
{ abi = { name = "musl"; }; cpu = { family = "power"; bits = 64; }; }
{
abi = {
abi = "elfv2";
};
}
{
abi = {
name = "musl";
};
cpu = {
family = "power";
bits = 64;
};
}
];
isx86 = { cpu = { family = "x86"; }; };
isAarch32 = { cpu = { family = "arm"; bits = 32; }; };
isArmv7 = map ({ arch, ... }: { cpu = { inherit arch; }; })
(filter (cpu: hasPrefix "armv7" cpu.arch or "")
(attrValues cpuTypes));
isAarch64 = { cpu = { family = "arm"; bits = 64; }; };
isAarch = { cpu = { family = "arm"; }; };
isMicroBlaze = { cpu = { family = "microblaze"; }; };
isMips = { cpu = { family = "mips"; }; };
isMips32 = { cpu = { family = "mips"; bits = 32; }; };
isMips64 = { cpu = { family = "mips"; bits = 64; }; };
isMips64n32 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "n32"; }; };
isMips64n64 = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64"; }; };
isMmix = { cpu = { family = "mmix"; }; };
isRiscV = { cpu = { family = "riscv"; }; };
isRiscV32 = { cpu = { family = "riscv"; bits = 32; }; };
isRiscV64 = { cpu = { family = "riscv"; bits = 64; }; };
isRx = { cpu = { family = "rx"; }; };
isSparc = { cpu = { family = "sparc"; }; };
isSparc64 = { cpu = { family = "sparc"; bits = 64; }; };
isWasm = { cpu = { family = "wasm"; }; };
isMsp430 = { cpu = { family = "msp430"; }; };
isVc4 = { cpu = { family = "vc4"; }; };
isAvr = { cpu = { family = "avr"; }; };
isAlpha = { cpu = { family = "alpha"; }; };
isOr1k = { cpu = { family = "or1k"; }; };
isM68k = { cpu = { family = "m68k"; }; };
isS390 = { cpu = { family = "s390"; }; };
isS390x = { cpu = { family = "s390"; bits = 64; }; };
isLoongArch64 = { cpu = { family = "loongarch"; bits = 64; }; };
isJavaScript = { cpu = cpuTypes.javascript; };
isx86 = {
cpu = {
family = "x86";
};
};
isAarch32 = {
cpu = {
family = "arm";
bits = 32;
};
};
isArmv7 = map (
{ arch, ... }:
{
cpu = { inherit arch; };
}
) (filter (cpu: hasPrefix "armv7" cpu.arch or "") (attrValues cpuTypes));
isAarch64 = {
cpu = {
family = "arm";
bits = 64;
};
};
isAarch = {
cpu = {
family = "arm";
};
};
isMicroBlaze = {
cpu = {
family = "microblaze";
};
};
isMips = {
cpu = {
family = "mips";
};
};
isMips32 = {
cpu = {
family = "mips";
bits = 32;
};
};
isMips64 = {
cpu = {
family = "mips";
bits = 64;
};
};
isMips64n32 = {
cpu = {
family = "mips";
bits = 64;
};
abi = {
abi = "n32";
};
};
isMips64n64 = {
cpu = {
family = "mips";
bits = 64;
};
abi = {
abi = "64";
};
};
isMmix = {
cpu = {
family = "mmix";
};
};
isRiscV = {
cpu = {
family = "riscv";
};
};
isRiscV32 = {
cpu = {
family = "riscv";
bits = 32;
};
};
isRiscV64 = {
cpu = {
family = "riscv";
bits = 64;
};
};
isRx = {
cpu = {
family = "rx";
};
};
isSparc = {
cpu = {
family = "sparc";
};
};
isSparc64 = {
cpu = {
family = "sparc";
bits = 64;
};
};
isWasm = {
cpu = {
family = "wasm";
};
};
isMsp430 = {
cpu = {
family = "msp430";
};
};
isVc4 = {
cpu = {
family = "vc4";
};
};
isAvr = {
cpu = {
family = "avr";
};
};
isAlpha = {
cpu = {
family = "alpha";
};
};
isOr1k = {
cpu = {
family = "or1k";
};
};
isM68k = {
cpu = {
family = "m68k";
};
};
isS390 = {
cpu = {
family = "s390";
};
};
isS390x = {
cpu = {
family = "s390";
bits = 64;
};
};
isLoongArch64 = {
cpu = {
family = "loongarch";
bits = 64;
};
};
isJavaScript = {
cpu = cpuTypes.javascript;
};
is32bit = { cpu = { bits = 32; }; };
is64bit = { cpu = { bits = 64; }; };
isILP32 = [ { cpu = { family = "wasm"; bits = 32; }; } ] ++
map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ];
isBigEndian = { cpu = { significantByte = significantBytes.bigEndian; }; };
isLittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };
is32bit = {
cpu = {
bits = 32;
};
};
is64bit = {
cpu = {
bits = 64;
};
};
isILP32 =
[
{
cpu = {
family = "wasm";
bits = 32;
};
}
]
++ map
(a: {
abi = {
abi = a;
};
})
[
"n32"
"ilp32"
"x32"
];
isBigEndian = {
cpu = {
significantByte = significantBytes.bigEndian;
};
};
isLittleEndian = {
cpu = {
significantByte = significantBytes.littleEndian;
};
};
isBSD = { kernel = { families = { inherit (kernelFamilies) bsd; }; }; };
isDarwin = { kernel = { families = { inherit (kernelFamilies) darwin; }; }; };
isUnix = [ isBSD isDarwin isLinux isSunOS isCygwin isRedox ];
isBSD = {
kernel = {
families = { inherit (kernelFamilies) bsd; };
};
};
isDarwin = {
kernel = {
families = { inherit (kernelFamilies) darwin; };
};
};
isUnix = [
isBSD
isDarwin
isLinux
isSunOS
isCygwin
isRedox
];
isMacOS = { kernel = kernels.macos; };
isiOS = { kernel = kernels.ios; };
isLinux = { kernel = kernels.linux; };
isSunOS = { kernel = kernels.solaris; };
isFreeBSD = { kernel = { name = "freebsd"; }; };
isNetBSD = { kernel = kernels.netbsd; };
isOpenBSD = { kernel = kernels.openbsd; };
isWindows = { kernel = kernels.windows; };
isCygwin = { kernel = kernels.windows; abi = abis.cygnus; };
isMinGW = { kernel = kernels.windows; abi = abis.gnu; };
isWasi = { kernel = kernels.wasi; };
isRedox = { kernel = kernels.redox; };
isGhcjs = { kernel = kernels.ghcjs; };
isGenode = { kernel = kernels.genode; };
isNone = { kernel = kernels.none; };
isMacOS = {
kernel = kernels.macos;
};
isiOS = {
kernel = kernels.ios;
};
isLinux = {
kernel = kernels.linux;
};
isSunOS = {
kernel = kernels.solaris;
};
isFreeBSD = {
kernel = {
name = "freebsd";
};
};
isNetBSD = {
kernel = kernels.netbsd;
};
isOpenBSD = {
kernel = kernels.openbsd;
};
isWindows = {
kernel = kernels.windows;
};
isCygwin = {
kernel = kernels.windows;
abi = abis.cygnus;
};
isMinGW = {
kernel = kernels.windows;
abi = abis.gnu;
};
isWasi = {
kernel = kernels.wasi;
};
isRedox = {
kernel = kernels.redox;
};
isGhcjs = {
kernel = kernels.ghcjs;
};
isGenode = {
kernel = kernels.genode;
};
isNone = {
kernel = kernels.none;
};
isAndroid = [ { abi = abis.android; } { abi = abis.androideabi; } ];
isGnu = with abis; map (a: { abi = a; }) [ gnuabi64 gnuabin32 gnu gnueabi gnueabihf gnuabielfv1 gnuabielfv2 ];
isMusl = with abis; map (a: { abi = a; }) [ musl musleabi musleabihf muslabin32 muslabi64 ];
isUClibc = with abis; map (a: { abi = a; }) [ uclibc uclibceabi uclibceabihf ];
isLLVMLibc = [ { abi = abis.llvm; } ];
isAndroid = [
{ abi = abis.android; }
{ abi = abis.androideabi; }
];
isGnu =
with abis;
map (a: { abi = a; }) [
gnuabi64
gnuabin32
gnu
gnueabi
gnueabihf
gnuabielfv1
gnuabielfv2
];
isMusl =
with abis;
map (a: { abi = a; }) [
musl
musleabi
musleabihf
muslabin32
muslabi64
];
isUClibc =
with abis;
map (a: { abi = a; }) [
uclibc
uclibceabi
uclibceabihf
];
isLLVMLibc = [ { abi = abis.llvm; } ];
isEfi = [
{ cpu = { family = "arm"; version = "6"; }; }
{ cpu = { family = "arm"; version = "7"; }; }
{ cpu = { family = "arm"; version = "8"; }; }
{ cpu = { family = "riscv"; }; }
{ cpu = { family = "x86"; }; }
{
cpu = {
family = "arm";
version = "6";
};
}
{
cpu = {
family = "arm";
version = "7";
};
}
{
cpu = {
family = "arm";
version = "8";
};
}
{
cpu = {
family = "riscv";
};
}
{
cpu = {
family = "x86";
};
}
];
isElf = { kernel.execFormat = execFormats.elf; };
isMacho = { kernel.execFormat = execFormats.macho; };
isElf = {
kernel.execFormat = execFormats.elf;
};
isMacho = {
kernel.execFormat = execFormats.macho;
};
};
# given two patterns, return a pattern which is their logical AND.
# Since a pattern is a list-of-disjuncts, this needs to
patternLogicalAnd = pat1_: pat2_:
patternLogicalAnd =
pat1_: pat2_:
let
# patterns can be either a list or a (bare) singleton; turn
# them into singletons for uniform handling
pat1 = toList pat1_;
pat2 = toList pat2_;
in
concatMap (attr1:
map (attr2:
recursiveUpdateUntil
(path: subattr1: subattr2:
if (builtins.intersectAttrs subattr1 subattr2) == {} || subattr1 == subattr2
then true
else throw ''
pattern conflict at path ${toString path}:
${toJSON subattr1}
${toJSON subattr2}
'')
attr1
attr2
)
pat2)
pat1;
concatMap (
attr1:
map (
attr2:
recursiveUpdateUntil (
path: subattr1: subattr2:
if (builtins.intersectAttrs subattr1 subattr2) == { } || subattr1 == subattr2 then
true
else
throw ''
pattern conflict at path ${toString path}:
${toJSON subattr1}
${toJSON subattr2}
''
) attr1 attr2
) pat2
) pat1;
matchAnyAttrs = patterns:
if isList patterns then attrs: any (pattern: matchAttrs pattern attrs) patterns
else matchAttrs patterns;
matchAnyAttrs =
patterns:
if isList patterns then
attrs: any (pattern: matchAttrs pattern attrs) patterns
else
matchAttrs patterns;
predicates = mapAttrs (_: matchAnyAttrs) patterns;
@ -164,7 +469,9 @@ rec {
# that `lib.meta.availableOn` can distinguish them from the patterns which
# apply only to the `parsed` field.
platformPatterns = mapAttrs (_: p: { parsed = {}; } // p) {
isStatic = { isStatic = true; };
platformPatterns = mapAttrs (_: p: { parsed = { }; } // p) {
isStatic = {
isStatic = true;
};
};
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
{ lib, ... }:
let
inherit (builtins)
storeDir;
storeDir
;
inherit (lib)
types
mkOption

View File

@ -1,24 +1,32 @@
{ # The pkgs used for dependencies for the testing itself
{
# The pkgs used for dependencies for the testing itself
# Don't test properties of pkgs.lib, but rather the lib in the parent directory
system ? builtins.currentSystem,
pkgs ? import ../.. { inherit system; } // { lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!"; },
pkgs ? import ../.. { inherit system; } // {
lib = throw "pkgs.lib accessed, but the lib tests should use nixpkgs' lib path directly!";
},
# For testing someone may edit impure.nix to return cross pkgs, use `pkgsBuildBuild` directly so everything here works.
pkgsBB ? pkgs.pkgsBuildBuild,
nix ? pkgs-nixVersions.stable,
nixVersions ? [ pkgs-nixVersions.minimum nix pkgs-nixVersions.latest ],
nixVersions ? [
pkgs-nixVersions.minimum
nix
pkgs-nixVersions.latest
],
pkgs-nixVersions ? import ./nix-for-tests.nix { pkgs = pkgsBB; },
}:
let
lib = import ../.;
testWithNix = nix:
testWithNix =
nix:
import ./test-with-nix.nix {
inherit lib nix;
pkgs = pkgsBB;
};
in
pkgsBB.symlinkJoin {
name = "nixpkgs-lib-tests";
paths = map testWithNix nixVersions;
}
pkgsBB.symlinkJoin {
name = "nixpkgs-lib-tests";
paths = map testWithNix nixVersions;
}

View File

@ -6,7 +6,7 @@
let
lib = import ../default.nix;
mseteq = x: y: {
expr = lib.sort lib.lessThan x;
expr = lib.sort lib.lessThan x;
expected = lib.sort lib.lessThan y;
};
@ -20,99 +20,273 @@ let
NOTE: This property is not guaranteed when `sys` was elaborated by a different
version of Nixpkgs.
*/
toLosslessStringMaybe = sys:
if lib.isString sys then sys
else if lib.systems.equals sys (lib.systems.elaborate sys.system) then sys.system
else null;
toLosslessStringMaybe =
sys:
if lib.isString sys then
sys
else if lib.systems.equals sys (lib.systems.elaborate sys.system) then
sys.system
else
null;
in
lib.runTests (
# We assert that the new algorithmic way of generating these lists matches the
# way they were hard-coded before.
#
# One might think "if we exhaustively test, what's the point of procedurally
# calculating the lists anyway?". The answer is one can mindlessly update these
# tests as new platforms become supported, and then just give the diff a quick
# sanity check before committing :).
# We assert that the new algorithmic way of generating these lists matches the
# way they were hard-coded before.
#
# One might think "if we exhaustively test, what's the point of procedurally
# calculating the lists anyway?". The answer is one can mindlessly update these
# tests as new platforms become supported, and then just give the diff a quick
# sanity check before committing :).
(with lib.systems.doubles; {
testall = mseteq all (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ wasi ++ windows ++ embedded ++ mmix ++ js ++ genode ++ redox);
(with lib.systems.doubles; {
testall = mseteq all (
linux
++ darwin
++ freebsd
++ openbsd
++ netbsd
++ illumos
++ wasi
++ windows
++ embedded
++ mmix
++ js
++ genode
++ redox
);
testarm = mseteq arm [ "armv5tel-linux" "armv6l-linux" "armv6l-netbsd" "armv6l-none" "armv7a-linux" "armv7a-netbsd" "armv7l-linux" "armv7l-netbsd" "arm-none" "armv7a-darwin" ];
testarmv7 = mseteq armv7 [ "armv7a-darwin" "armv7a-linux" "armv7l-linux" "armv7a-netbsd" "armv7l-netbsd" ];
testi686 = mseteq i686 [ "i686-linux" "i686-freebsd" "i686-genode" "i686-netbsd" "i686-openbsd" "i686-cygwin" "i686-windows" "i686-none" "i686-darwin" ];
testmips = mseteq mips [ "mips-none" "mips64-none" "mips-linux" "mips64-linux" "mips64el-linux" "mipsel-linux" "mipsel-netbsd" ];
testmmix = mseteq mmix [ "mmix-mmixware" ];
testpower = mseteq power [ "powerpc-netbsd" "powerpc-none" "powerpc64-linux" "powerpc64le-linux" "powerpcle-none" ];
testriscv = mseteq riscv [ "riscv32-linux" "riscv64-linux" "riscv32-netbsd" "riscv64-netbsd" "riscv32-none" "riscv64-none" ];
testriscv32 = mseteq riscv32 [ "riscv32-linux" "riscv32-netbsd" "riscv32-none" ];
testriscv64 = mseteq riscv64 [ "riscv64-linux" "riscv64-netbsd" "riscv64-none" ];
tests390x = mseteq s390x [ "s390x-linux" "s390x-none" ];
testx86_64 = mseteq x86_64 [ "x86_64-linux" "x86_64-darwin" "x86_64-freebsd" "x86_64-genode" "x86_64-redox" "x86_64-openbsd" "x86_64-netbsd" "x86_64-cygwin" "x86_64-solaris" "x86_64-windows" "x86_64-none" ];
testarm = mseteq arm [
"armv5tel-linux"
"armv6l-linux"
"armv6l-netbsd"
"armv6l-none"
"armv7a-linux"
"armv7a-netbsd"
"armv7l-linux"
"armv7l-netbsd"
"arm-none"
"armv7a-darwin"
];
testarmv7 = mseteq armv7 [
"armv7a-darwin"
"armv7a-linux"
"armv7l-linux"
"armv7a-netbsd"
"armv7l-netbsd"
];
testi686 = mseteq i686 [
"i686-linux"
"i686-freebsd"
"i686-genode"
"i686-netbsd"
"i686-openbsd"
"i686-cygwin"
"i686-windows"
"i686-none"
"i686-darwin"
];
testmips = mseteq mips [
"mips-none"
"mips64-none"
"mips-linux"
"mips64-linux"
"mips64el-linux"
"mipsel-linux"
"mipsel-netbsd"
];
testmmix = mseteq mmix [ "mmix-mmixware" ];
testpower = mseteq power [
"powerpc-netbsd"
"powerpc-none"
"powerpc64-linux"
"powerpc64le-linux"
"powerpcle-none"
];
testriscv = mseteq riscv [
"riscv32-linux"
"riscv64-linux"
"riscv32-netbsd"
"riscv64-netbsd"
"riscv32-none"
"riscv64-none"
];
testriscv32 = mseteq riscv32 [
"riscv32-linux"
"riscv32-netbsd"
"riscv32-none"
];
testriscv64 = mseteq riscv64 [
"riscv64-linux"
"riscv64-netbsd"
"riscv64-none"
];
tests390x = mseteq s390x [
"s390x-linux"
"s390x-none"
];
testx86_64 = mseteq x86_64 [
"x86_64-linux"
"x86_64-darwin"
"x86_64-freebsd"
"x86_64-genode"
"x86_64-redox"
"x86_64-openbsd"
"x86_64-netbsd"
"x86_64-cygwin"
"x86_64-solaris"
"x86_64-windows"
"x86_64-none"
];
testcygwin = mseteq cygwin [ "i686-cygwin" "x86_64-cygwin" ];
testdarwin = mseteq darwin [ "x86_64-darwin" "i686-darwin" "aarch64-darwin" "armv7a-darwin" ];
testfreebsd = mseteq freebsd [ "aarch64-freebsd" "i686-freebsd" "x86_64-freebsd" ];
testgenode = mseteq genode [ "aarch64-genode" "i686-genode" "x86_64-genode" ];
testredox = mseteq redox [ "x86_64-redox" ];
testgnu = mseteq gnu (linux /* ++ kfreebsd ++ ... */);
testillumos = mseteq illumos [ "x86_64-solaris" ];
testlinux = mseteq linux [ "aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux" "armv7l-linux" "i686-linux" "loongarch64-linux" "m68k-linux" "microblaze-linux" "microblazeel-linux" "mips-linux" "mips64-linux" "mips64el-linux" "mipsel-linux" "powerpc64-linux" "powerpc64le-linux" "riscv32-linux" "riscv64-linux" "s390-linux" "s390x-linux" "x86_64-linux" ];
testnetbsd = mseteq netbsd [ "aarch64-netbsd" "armv6l-netbsd" "armv7a-netbsd" "armv7l-netbsd" "i686-netbsd" "m68k-netbsd" "mipsel-netbsd" "powerpc-netbsd" "riscv32-netbsd" "riscv64-netbsd" "x86_64-netbsd" ];
testopenbsd = mseteq openbsd [ "i686-openbsd" "x86_64-openbsd" ];
testwindows = mseteq windows [ "i686-cygwin" "x86_64-cygwin" "aarch64-windows" "i686-windows" "x86_64-windows" ];
testunix = mseteq unix (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ cygwin ++ redox);
})
testcygwin = mseteq cygwin [
"i686-cygwin"
"x86_64-cygwin"
];
testdarwin = mseteq darwin [
"x86_64-darwin"
"i686-darwin"
"aarch64-darwin"
"armv7a-darwin"
];
testfreebsd = mseteq freebsd [
"aarch64-freebsd"
"i686-freebsd"
"x86_64-freebsd"
];
testgenode = mseteq genode [
"aarch64-genode"
"i686-genode"
"x86_64-genode"
];
testredox = mseteq redox [ "x86_64-redox" ];
testgnu = mseteq gnu (
linux # ++ kfreebsd ++ ...
);
testillumos = mseteq illumos [ "x86_64-solaris" ];
testlinux = mseteq linux [
"aarch64-linux"
"armv5tel-linux"
"armv6l-linux"
"armv7a-linux"
"armv7l-linux"
"i686-linux"
"loongarch64-linux"
"m68k-linux"
"microblaze-linux"
"microblazeel-linux"
"mips-linux"
"mips64-linux"
"mips64el-linux"
"mipsel-linux"
"powerpc64-linux"
"powerpc64le-linux"
"riscv32-linux"
"riscv64-linux"
"s390-linux"
"s390x-linux"
"x86_64-linux"
];
testnetbsd = mseteq netbsd [
"aarch64-netbsd"
"armv6l-netbsd"
"armv7a-netbsd"
"armv7l-netbsd"
"i686-netbsd"
"m68k-netbsd"
"mipsel-netbsd"
"powerpc-netbsd"
"riscv32-netbsd"
"riscv64-netbsd"
"x86_64-netbsd"
];
testopenbsd = mseteq openbsd [
"i686-openbsd"
"x86_64-openbsd"
];
testwindows = mseteq windows [
"i686-cygwin"
"x86_64-cygwin"
"aarch64-windows"
"i686-windows"
"x86_64-windows"
];
testunix = mseteq unix (
linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ cygwin ++ redox
);
})
// {
test_equals_example_x86_64-linux = {
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") (lib.systems.elaborate "x86_64-linux");
expected = true;
};
test_toLosslessStringMaybe_example_x86_64-linux = {
expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux");
expected = "x86_64-linux";
};
test_toLosslessStringMaybe_fail = {
expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux" // { something = "extra"; });
expected = null;
};
test_elaborate_config_over_system = {
expr = (lib.systems.elaborate { config = "i686-unknown-linux-gnu"; system = "x86_64-linux"; }).system;
expected = "i686-linux";
};
test_elaborate_config_over_parsed = {
expr = (lib.systems.elaborate { config = "i686-unknown-linux-gnu"; parsed = (lib.systems.elaborate "x86_64-linux").parsed; }).parsed.cpu.arch;
expected = "i686";
};
test_elaborate_system_over_parsed = {
expr = (lib.systems.elaborate { system = "i686-linux"; parsed = (lib.systems.elaborate "x86_64-linux").parsed; }).parsed.cpu.arch;
expected = "i686";
};
}
# Generate test cases to assert that a change in any non-function attribute makes a platform unequal
// lib.concatMapAttrs (platformAttrName: origValue: {
${"test_equals_unequal_${platformAttrName}"} =
let modified =
assert origValue != arbitraryValue;
lib.systems.elaborate "x86_64-linux" // { ${platformAttrName} = arbitraryValue; };
arbitraryValue = x: "<<modified>>";
in {
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified;
expected = {
# Changes in these attrs are not detectable because they're function.
# The functions should be derived from the data, so this is not a problem.
canExecute = null;
emulator = null;
emulatorAvailable = null;
staticEmulatorAvailable = null;
isCompatible = null;
}?${platformAttrName};
// {
test_equals_example_x86_64-linux = {
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") (
lib.systems.elaborate "x86_64-linux"
);
expected = true;
};
}) (lib.systems.elaborate "x86_64-linux" /* arbitrary choice, just to get all the elaborated attrNames */)
test_toLosslessStringMaybe_example_x86_64-linux = {
expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux");
expected = "x86_64-linux";
};
test_toLosslessStringMaybe_fail = {
expr = toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux" // { something = "extra"; });
expected = null;
};
test_elaborate_config_over_system = {
expr =
(lib.systems.elaborate {
config = "i686-unknown-linux-gnu";
system = "x86_64-linux";
}).system;
expected = "i686-linux";
};
test_elaborate_config_over_parsed = {
expr =
(lib.systems.elaborate {
config = "i686-unknown-linux-gnu";
parsed = (lib.systems.elaborate "x86_64-linux").parsed;
}).parsed.cpu.arch;
expected = "i686";
};
test_elaborate_system_over_parsed = {
expr =
(lib.systems.elaborate {
system = "i686-linux";
parsed = (lib.systems.elaborate "x86_64-linux").parsed;
}).parsed.cpu.arch;
expected = "i686";
};
}
# Generate test cases to assert that a change in any non-function attribute makes a platform unequal
//
lib.concatMapAttrs
(platformAttrName: origValue: {
${"test_equals_unequal_${platformAttrName}"} =
let
modified =
assert origValue != arbitraryValue;
lib.systems.elaborate "x86_64-linux" // { ${platformAttrName} = arbitraryValue; };
arbitraryValue = x: "<<modified>>";
in
{
expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified;
expected =
{
# Changes in these attrs are not detectable because they're function.
# The functions should be derived from the data, so this is not a problem.
canExecute = null;
emulator = null;
emulatorAvailable = null;
staticEmulatorAvailable = null;
isCompatible = null;
} ? ${platformAttrName};
};
})
(
lib.systems.elaborate "x86_64-linux" # arbitrary choice, just to get all the elaborated attrNames
)
)

View File

@ -11,11 +11,13 @@ let
toBaseDigits
version
versionSuffix
warn;
warn
;
inherit (lib)
isString
;
in {
in
{
## Simple (higher order) functions
@ -23,7 +25,6 @@ in {
The identity function
For when you need a function that does nothing.
# Inputs
`x`
@ -44,7 +45,6 @@ in {
Ignores the second argument. If called with only one argument,
constructs a function that always returns a static value.
# Inputs
`x`
@ -72,9 +72,7 @@ in {
:::
*/
const =
x:
y: x;
const = x: y: x;
/**
Pipes a value through a list of functions, left to right.
@ -140,7 +138,6 @@ in {
/**
Concatenate two lists
# Inputs
`x`
@ -173,7 +170,6 @@ in {
/**
boolean or
# Inputs
`x`
@ -189,7 +185,6 @@ in {
/**
boolean and
# Inputs
`x`
@ -205,7 +200,6 @@ in {
/**
boolean exclusive or
# Inputs
`x`
@ -232,7 +226,6 @@ in {
boolean values. Calling `toString` on a bool instead returns "1"
and "" (sic!).
# Inputs
`b`
@ -252,7 +245,6 @@ in {
mergeAttrs :: attrs -> attrs -> attrs
# Inputs
`x`
@ -263,7 +255,6 @@ in {
: Right attribute set (higher precedence for equal keys)
# Examples
:::{.example}
## `lib.trivial.mergeAttrs` usage example
@ -275,14 +266,11 @@ in {
:::
*/
mergeAttrs =
x:
y: x // y;
mergeAttrs = x: y: x // y;
/**
Flip the order of the arguments of a binary function.
# Inputs
`f`
@ -314,12 +302,13 @@ in {
:::
*/
flip = f: a: b: f b a;
flip =
f: a: b:
f b a;
/**
Return `maybeValue` if not null, otherwise return `default`.
# Inputs
`default`
@ -330,7 +319,6 @@ in {
: 2\. Function argument
# Examples
:::{.example}
## `lib.trivial.defaultTo` usage example
@ -346,14 +334,11 @@ in {
:::
*/
defaultTo = default: maybeValue:
if maybeValue != null then maybeValue
else default;
defaultTo = default: maybeValue: if maybeValue != null then maybeValue else default;
/**
Apply function if the supplied argument is non-null.
# Inputs
`f`
@ -364,7 +349,6 @@ in {
: Argument to check for null before passing it to `f`
# Examples
:::{.example}
## `lib.trivial.mapNullable` usage example
@ -378,16 +362,25 @@ in {
:::
*/
mapNullable =
f:
a: if a == null then a else f a;
mapNullable = 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
bitAnd bitOr bitXor;
pathExists
readFile
isBool
isInt
isFloat
add
sub
lessThan
seq
deepSeq
genericClosure
bitAnd
bitOr
bitXor
;
## nixpkgs version strings
@ -422,7 +415,6 @@ in {
Whether a feature is supported in all supported releases (at the time of
release branch-off, if applicable). See `oldestSupportedRelease`.
# Inputs
`release`
@ -433,15 +425,13 @@ in {
isInOldestRelease =
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2411)
"lib.isInOldestRelease is deprecated. Use lib.oldestSupportedReleaseIsAtLeast instead."
lib.oldestSupportedReleaseIsAtLeast;
lib.oldestSupportedReleaseIsAtLeast;
/**
Alias for `isInOldestRelease` introduced in 24.11.
Use `isInOldestRelease` in expressions outside of Nixpkgs for greater compatibility.
*/
oldestSupportedReleaseIsAtLeast =
release:
release <= lib.trivial.oldestSupportedRelease;
*/
oldestSupportedReleaseIsAtLeast = release: release <= lib.trivial.oldestSupportedRelease;
/**
Returns the current nixpkgs release code name.
@ -455,16 +445,15 @@ in {
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";
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.
# Inputs
`default`
@ -481,11 +470,14 @@ in {
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;
gitRepo = "${toString ./..}/.git";
in
if lib.pathIsGitRepo gitRepo then
lib.commitIdFromGitRepo gitRepo
else if lib.pathExists revisionFile then
lib.fileContents revisionFile
else
default;
nixpkgsVersion = warn "lib.nixpkgsVersion is a deprecated alias of lib.version." version;
@ -512,14 +504,13 @@ in {
inPureEvalMode :: bool
```
*/
inPureEvalMode = ! builtins ? currentSystem;
inPureEvalMode = !builtins ? currentSystem;
## Integer operations
/**
Return minimum of two numbers.
# Inputs
`x`
@ -535,7 +526,6 @@ in {
/**
Return maximum of two numbers.
# Inputs
`x`
@ -551,7 +541,6 @@ in {
/**
Integer modulus
# Inputs
`base`
@ -562,7 +551,6 @@ in {
: 2\. Function argument
# Examples
:::{.example}
## `lib.trivial.mod` usage example
@ -578,7 +566,6 @@ in {
*/
mod = base: int: base - (int * (builtins.div base int));
## Comparisons
/**
@ -588,7 +575,6 @@ in {
a == b, compare a b => 0
a > b, compare a b => 1
# Inputs
`a`
@ -599,12 +585,14 @@ in {
: 2\. Function argument
*/
compare = a: b:
if a < b
then -1
else if a > b
then 1
else 0;
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
@ -612,7 +600,6 @@ in {
second subtype, compare elements of a single subtype with `yes`
and `no` respectively.
# Inputs
`p`
@ -661,10 +648,12 @@ in {
*/
splitByAndCompare =
p: yes: no: a: b:
if p a
then if p b then yes a b else -1
else if p b then 1 else no a b;
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.
@ -713,8 +702,7 @@ in {
importJSON :: path -> any
```
*/
importJSON = path:
builtins.fromJSON (builtins.readFile path);
importJSON = path: builtins.fromJSON (builtins.readFile path);
/**
Reads a TOML file.
@ -761,11 +749,9 @@ in {
importTOML :: path -> any
```
*/
importTOML = path:
builtins.fromTOML (builtins.readFile path);
importTOML = path: builtins.fromTOML (builtins.readFile path);
/**
`warn` *`message`* *`value`*
Print a warning before returning the second argument.
@ -792,19 +778,26 @@ in {
warn =
# Since Nix 2.23, https://github.com/NixOS/nix/pull/10592
builtins.warn or (
let mustAbort = lib.elem (builtins.getEnv "NIX_ABORT_ON_WARN") ["1" "true" "yes"];
let
mustAbort = lib.elem (builtins.getEnv "NIX_ABORT_ON_WARN") [
"1"
"true"
"yes"
];
in
# Do not eta reduce v, so that we have the same strictness as `builtins.warn`.
msg: v:
# `builtins.warn` requires a string message, so we enforce that in our implementation, so that callers aren't accidentally incompatible with newer Nix versions.
assert isString msg;
if mustAbort
then builtins.trace "evaluation warning: ${msg}" (abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors.")
else builtins.trace "evaluation warning: ${msg}" v
# Do not eta reduce v, so that we have the same strictness as `builtins.warn`.
msg: v:
# `builtins.warn` requires a string message, so we enforce that in our implementation, so that callers aren't accidentally incompatible with newer Nix versions.
assert isString msg;
if mustAbort then
builtins.trace "evaluation warning: ${msg}" (
abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors."
)
else
builtins.trace "evaluation warning: ${msg}" v
);
/**
`warnIf` *`condition`* *`message`* *`value`*
Like `warn`, but only warn when the first argument is `true`.
@ -832,7 +825,6 @@ in {
warnIf = cond: msg: if cond then warn msg else x: x;
/**
`warnIfNot` *`condition`* *`message`* *`value`*
Like `warnIf`, but negated: warn if the first argument is `false`.
@ -870,7 +862,6 @@ in {
Calls can be juxtaposed using function application, as `(r: r) a = a`, so
`(r: r) (r: r) a = a`, and so forth.
# Inputs
`cond`
@ -904,7 +895,6 @@ in {
/**
Like throwIfNot, but negated (throw if the first argument is `true`).
# Inputs
`cond`
@ -926,7 +916,6 @@ in {
/**
Check if the elements in a list are valid values from a enum, returning the identity function, or throwing an error message otherwise.
# Inputs
`msg`
@ -960,12 +949,13 @@ in {
:::
*/
checkListOfEnum = msg: valid: given:
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)}";
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}";
@ -984,7 +974,6 @@ in {
function of the { a, b ? foo, ... }: format, but some facilities
like callPackage expect to be able to query expected arguments.
# Inputs
`f`
@ -995,11 +984,11 @@ in {
: 2\. Function argument
*/
setFunctionArgs = f: args:
{ # TODO: Should we add call-time "type" checking like built in?
__functor = self: f;
__functionArgs = args;
};
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.
@ -1008,37 +997,35 @@ in {
has the same return type and semantics as builtins.functionArgs.
setFunctionArgs : (a b) Map String Bool.
# Inputs
`f`
: 1\. Function argument
*/
functionArgs = f:
if f ? __functor
then f.__functionArgs or (functionArgs (f.__functor f))
else builtins.functionArgs f;
functionArgs =
f:
if f ? __functor then
f.__functionArgs or (functionArgs (f.__functor f))
else
builtins.functionArgs f;
/**
Check whether something is a function or something
annotated with function args.
# Inputs
`f`
: 1\. Function argument
*/
isFunction = f: builtins.isFunction f ||
(f ? __functor && isFunction (f.__functor f));
isFunction = f: builtins.isFunction f || (f ? __functor && isFunction (f.__functor f));
/**
`mirrorFunctionArgs f g` creates a new function `g'` with the same behavior as `g` (`g' x == g x`)
but its function arguments mirroring `f` (`lib.functionArgs g' == lib.functionArgs f`).
# Inputs
`f`
@ -1084,21 +1071,18 @@ in {
let
fArgs = functionArgs f;
in
g:
setFunctionArgs g fArgs;
g: setFunctionArgs g fArgs;
/**
Turns any non-callable values into constant functions.
Returns callable values as is.
# Inputs
`v`
: Any value
# Examples
:::{.example}
## `lib.trivial.toFunction` usage example
@ -1113,11 +1097,7 @@ in {
:::
*/
toFunction =
v:
if isFunction v
then v
else k: v;
toFunction = v: if isFunction v then v else k: v;
/**
Convert a hexadecimal string to it's integer representation.
@ -1138,12 +1118,15 @@ in {
=> 9223372036854775807
```
*/
fromHexString = value:
let
noPrefix = lib.strings.removePrefix "0x" (lib.strings.toLower value);
in let
parsed = builtins.fromTOML "v=0x${noPrefix}";
in parsed.v;
fromHexString =
value:
let
noPrefix = lib.strings.removePrefix "0x" (lib.strings.toLower value);
in
let
parsed = builtins.fromTOML "v=0x${noPrefix}";
in
parsed.v;
/**
Convert the given positive integer to a string of its hexadecimal
@ -1155,20 +1138,19 @@ in {
toHexString 250 => "FA"
*/
toHexString = let
hexDigits = {
"10" = "A";
"11" = "B";
"12" = "C";
"13" = "D";
"14" = "E";
"15" = "F";
};
toHexDigit = d:
if d < 10
then toString d
else hexDigits.${toString d};
in i: lib.concatMapStrings toHexDigit (toBaseDigits 16 i);
toHexString =
let
hexDigits = {
"10" = "A";
"11" = "B";
"12" = "C";
"13" = "D";
"14" = "E";
"15" = "F";
};
toHexDigit = d: if d < 10 then toString d else hexDigits.${toString d};
in
i: lib.concatMapStrings toHexDigit (toBaseDigits 16 i);
/**
`toBaseDigits base i` converts the positive integer i to a list of its
@ -1180,7 +1162,6 @@ in {
toBaseDigits 16 250 => [ 15 10 ]
# Inputs
`base`
@ -1191,21 +1172,23 @@ in {
: 2\. Function argument
*/
toBaseDigits = base: i:
toBaseDigits =
base: i:
let
go = i:
if i < base
then [i]
go =
i:
if i < base then
[ i ]
else
let
r = i - ((i / base) * base);
q = (i - r) / base;
in
[r] ++ go q;
[ r ] ++ go q;
in
assert (isInt base);
assert (isInt i);
assert (base >= 2);
assert (i >= 0);
lib.reverseList (go i);
assert (isInt base);
assert (isInt i);
assert (base >= 2);
assert (i >= 0);
lib.reverseList (go i);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
{ pkgs
, options
, config
, version
, revision
, extraSources ? []
, baseOptionsJSON ? null
, warningsAreErrors ? true
, prefix ? ../../..
{
pkgs,
options,
config,
version,
revision,
extraSources ? [ ],
baseOptionsJSON ? null,
warningsAreErrors ? true,
prefix ? ../../..,
}:
let
@ -38,43 +39,61 @@ let
stripAnyPrefixes = flip (foldr removePrefix) prefixesToStrip;
optionsDoc = buildPackages.nixosOptionsDoc {
inherit options revision baseOptionsJSON warningsAreErrors;
transformOptions = opt: opt // {
# Clean up declaration sites to not refer to the NixOS source tree.
declarations = map stripAnyPrefixes opt.declarations;
};
inherit
options
revision
baseOptionsJSON
warningsAreErrors
;
transformOptions =
opt:
opt
// {
# Clean up declaration sites to not refer to the NixOS source tree.
declarations = map stripAnyPrefixes opt.declarations;
};
};
nixos-lib = import ../../lib { };
testOptionsDoc = let
testOptionsDoc =
let
eval = nixos-lib.evalTest {
# Avoid evaluating a NixOS config prototype.
config.node.type = types.deferredModule;
options._module.args = mkOption { internal = true; };
};
in buildPackages.nixosOptionsDoc {
in
buildPackages.nixosOptionsDoc {
inherit (eval) options;
inherit revision;
transformOptions = opt: opt // {
# Clean up declaration sites to not refer to the NixOS source tree.
declarations =
map
(decl:
if hasPrefix (toString ../../..) (toString decl)
then
let subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl));
in { url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}"; name = subpath; }
else decl)
opt.declarations;
};
transformOptions =
opt:
opt
// {
# Clean up declaration sites to not refer to the NixOS source tree.
declarations = map (
decl:
if hasPrefix (toString ../../..) (toString decl) then
let
subpath = removePrefix "/" (removePrefix (toString ../../..) (toString decl));
in
{
url = "https://github.com/NixOS/nixpkgs/blob/master/${subpath}";
name = subpath;
}
else
decl
) opt.declarations;
};
documentType = "none";
variablelistId = "test-options-list";
optionIdPrefix = "test-opt-";
};
testDriverMachineDocstrings = pkgs.callPackage
../../../nixos/lib/test-driver/nixos-test-driver-docstrings.nix {};
testDriverMachineDocstrings =
pkgs.callPackage ../../../nixos/lib/test-driver/nixos-test-driver-docstrings.nix
{ };
prepareManualFromMD = ''
cp -r --no-preserve=all $inputs/* .
@ -99,49 +118,52 @@ let
-i ./development/writing-nixos-tests.section.md
'';
in rec {
in
rec {
inherit (optionsDoc) optionsJSON optionsNix optionsDocBook;
# Generate the NixOS manual.
manualHTML = runCommand "nixos-manual-html"
{ nativeBuildInputs = [ buildPackages.nixos-render-docs ];
inputs = sourceFilesBySuffices ./. [ ".md" ];
meta.description = "The NixOS manual in HTML format";
allowedReferences = ["out"];
}
''
# Generate the HTML manual.
dst=$out/${common.outputPath}
mkdir -p $dst
manualHTML =
runCommand "nixos-manual-html"
{
nativeBuildInputs = [ buildPackages.nixos-render-docs ];
inputs = sourceFilesBySuffices ./. [ ".md" ];
meta.description = "The NixOS manual in HTML format";
allowedReferences = [ "out" ];
}
''
# Generate the HTML manual.
dst=$out/${common.outputPath}
mkdir -p $dst
cp ${../../../doc/style.css} $dst/style.css
cp ${../../../doc/anchor.min.js} $dst/anchor.min.js
cp ${../../../doc/anchor-use.js} $dst/anchor-use.js
cp ${../../../doc/style.css} $dst/style.css
cp ${../../../doc/anchor.min.js} $dst/anchor.min.js
cp ${../../../doc/anchor-use.js} $dst/anchor-use.js
cp -r ${pkgs.documentation-highlighter} $dst/highlightjs
cp -r ${pkgs.documentation-highlighter} $dst/highlightjs
${prepareManualFromMD}
${prepareManualFromMD}
nixos-render-docs -j $NIX_BUILD_CORES manual html \
--manpage-urls ${manpageUrls} \
--redirects ${./redirects.json} \
--revision ${escapeShellArg revision} \
--generator "nixos-render-docs ${pkgs.lib.version}" \
--stylesheet style.css \
--stylesheet highlightjs/mono-blue.css \
--script ./highlightjs/highlight.pack.js \
--script ./highlightjs/loader.js \
--script ./anchor.min.js \
--script ./anchor-use.js \
--toc-depth 1 \
--chunk-toc-depth 1 \
./manual.md \
$dst/${common.indexPath}
nixos-render-docs -j $NIX_BUILD_CORES manual html \
--manpage-urls ${manpageUrls} \
--redirects ${./redirects.json} \
--revision ${escapeShellArg revision} \
--generator "nixos-render-docs ${pkgs.lib.version}" \
--stylesheet style.css \
--stylesheet highlightjs/mono-blue.css \
--script ./highlightjs/highlight.pack.js \
--script ./highlightjs/loader.js \
--script ./anchor.min.js \
--script ./anchor-use.js \
--toc-depth 1 \
--chunk-toc-depth 1 \
./manual.md \
$dst/${common.indexPath}
mkdir -p $out/nix-support
echo "nix-build out $out" >> $out/nix-support/hydra-build-products
echo "doc manual $dst" >> $out/nix-support/hydra-build-products
''; # */
mkdir -p $out/nix-support
echo "nix-build out $out" >> $out/nix-support/hydra-build-products
echo "doc manual $dst" >> $out/nix-support/hydra-build-products
''; # */
# Alias for backward compatibility. TODO(@oxij): remove eventually.
manual = manualHTML;
@ -149,70 +171,77 @@ in rec {
# Index page of the NixOS manual.
manualHTMLIndex = "${manualHTML}/${common.outputPath}/${common.indexPath}";
manualEpub = runCommand "nixos-manual-epub"
{ nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin buildPackages.zip ];
doc = ''
<book xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="5.0"
xml:id="book-nixos-manual">
<info>
<title>NixOS Manual</title>
<subtitle>Version ${pkgs.lib.version}</subtitle>
</info>
<chapter>
<title>Temporarily unavailable</title>
<para>
The NixOS manual is currently not available in EPUB format,
please use the <link xlink:href="https://nixos.org/nixos/manual">HTML manual</link>
instead.
</para>
<para>
If you've used the EPUB manual in the past and it has been useful to you, please
<link xlink:href="https://github.com/NixOS/nixpkgs/issues/237234">let us know</link>.
</para>
</chapter>
</book>
manualEpub =
runCommand "nixos-manual-epub"
{
nativeBuildInputs = [
buildPackages.libxml2.bin
buildPackages.libxslt.bin
buildPackages.zip
];
doc = ''
<book xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="5.0"
xml:id="book-nixos-manual">
<info>
<title>NixOS Manual</title>
<subtitle>Version ${pkgs.lib.version}</subtitle>
</info>
<chapter>
<title>Temporarily unavailable</title>
<para>
The NixOS manual is currently not available in EPUB format,
please use the <link xlink:href="https://nixos.org/nixos/manual">HTML manual</link>
instead.
</para>
<para>
If you've used the EPUB manual in the past and it has been useful to you, please
<link xlink:href="https://github.com/NixOS/nixpkgs/issues/237234">let us know</link>.
</para>
</chapter>
</book>
'';
passAsFile = [ "doc" ];
}
''
# Generate the epub manual.
dst=$out/${common.outputPath}
xsltproc \
--param chapter.autolabel 0 \
--nonet --xinclude --output $dst/epub/ \
${docbook_xsl_ns}/xml/xsl/docbook/epub/docbook.xsl \
$docPath
echo "application/epub+zip" > mimetype
manual="$dst/nixos-manual.epub"
zip -0Xq "$manual" mimetype
cd $dst/epub && zip -Xr9D "$manual" *
rm -rf $dst/epub
mkdir -p $out/nix-support
echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products
'';
passAsFile = [ "doc" ];
}
''
# Generate the epub manual.
dst=$out/${common.outputPath}
xsltproc \
--param chapter.autolabel 0 \
--nonet --xinclude --output $dst/epub/ \
${docbook_xsl_ns}/xml/xsl/docbook/epub/docbook.xsl \
$docPath
echo "application/epub+zip" > mimetype
manual="$dst/nixos-manual.epub"
zip -0Xq "$manual" mimetype
cd $dst/epub && zip -Xr9D "$manual" *
rm -rf $dst/epub
mkdir -p $out/nix-support
echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products
'';
# Generate the `man configuration.nix` package
nixos-configuration-reference-manpage = runCommand "nixos-configuration-reference-manpage"
{ nativeBuildInputs = [
buildPackages.installShellFiles
buildPackages.nixos-render-docs
];
allowedReferences = ["out"];
}
''
# Generate manpages.
mkdir -p $out/share/man/man5
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
--revision ${escapeShellArg revision} \
${optionsJSON}/${common.outputPath}/options.json \
$out/share/man/man5/configuration.nix.5
'';
nixos-configuration-reference-manpage =
runCommand "nixos-configuration-reference-manpage"
{
nativeBuildInputs = [
buildPackages.installShellFiles
buildPackages.nixos-render-docs
];
allowedReferences = [ "out" ];
}
''
# Generate manpages.
mkdir -p $out/share/man/man5
nixos-render-docs -j $NIX_BUILD_CORES options manpage \
--revision ${escapeShellArg revision} \
${optionsJSON}/${common.outputPath}/options.json \
$out/share/man/man5/configuration.nix.5
'';
}

View File

@ -8,31 +8,33 @@
# as subcomponents (e.g. the container feature, or nixops if network
# expressions are ever made modular at the top level) can just use
# types.submodule instead of using eval-config.nix
evalConfigArgs@
{ # !!! system can be set modularly, would be nice to remove,
evalConfigArgs@{
# !!! system can be set modularly, would be nice to remove,
# however, removing or changing this default is too much
# of a breaking change. To set it modularly, pass `null`.
system ? builtins.currentSystem
, # !!! is this argument needed any more? The pkgs argument can
system ? builtins.currentSystem,
# !!! is this argument needed any more? The pkgs argument can
# be set modularly anyway.
pkgs ? null
, # !!! what do we gain by making this configurable?
pkgs ? null,
# !!! what do we gain by making this configurable?
# we can add modules that are included in specialisations, regardless
# of inheritParentConfig.
baseModules ? import ../modules/module-list.nix
, # !!! See comment about args in lib/modules.nix
extraArgs ? {}
, # !!! See comment about args in lib/modules.nix
specialArgs ? {}
, modules
, modulesLocation ? (builtins.unsafeGetAttrPos "modules" evalConfigArgs).file or null
, # !!! See comment about check in lib/modules.nix
check ? true
, prefix ? []
, lib ? import ../../lib
, extraModules ?
let e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
in lib.optional (e != "") (
baseModules ? import ../modules/module-list.nix,
# !!! See comment about args in lib/modules.nix
extraArgs ? { },
# !!! See comment about args in lib/modules.nix
specialArgs ? { },
modules,
modulesLocation ? (builtins.unsafeGetAttrPos "modules" evalConfigArgs).file or null,
# !!! See comment about check in lib/modules.nix
check ? true,
prefix ? [ ],
lib ? import ../../lib,
extraModules ?
let
e = builtins.getEnv "NIXOS_EXTRA_MODULE_PATH";
in
lib.optional (e != "") (
lib.warn
''
The NIXOS_EXTRA_MODULE_PATH environment variable is deprecated and will be
@ -51,18 +53,20 @@ evalConfigArgs@
''
# NOTE: this import call is unnecessary and it even removes the file name
# from error messages.
import e
)
import
e
),
}:
let
inherit (lib) optional;
evalModulesMinimal = (import ./default.nix {
inherit lib;
# Implicit use of feature is noted in implementation.
featureFlags.minimalModules = { };
}).evalModules;
evalModulesMinimal =
(import ./default.nix {
inherit lib;
# Implicit use of feature is noted in implementation.
featureFlags.minimalModules = { };
}).evalModules;
pkgsModule = rec {
_file = ./eval-config.nix;
@ -75,34 +79,39 @@ let
# they way through, but has the last priority behind everything else.
nixpkgs.system = lib.mkDefault system;
})
++
(optional (pkgs != null) {
++ (optional (pkgs != null) {
# This should be default priority, so it conflicts with any user-defined pkgs.
nixpkgs.pkgs = pkgs;
})
);
};
withWarnings = x:
lib.warnIf (evalConfigArgs?extraArgs) "The extraArgs argument to eval-config.nix is deprecated. Please set config._module.args instead."
lib.warnIf (evalConfigArgs?check) "The check argument to eval-config.nix is deprecated. Please set config._module.check instead."
lib.warnIf (specialArgs?pkgs) ''
You have set specialArgs.pkgs, which means that options like nixpkgs.config
and nixpkgs.overlays will be ignored. If you wish to reuse an already created
pkgs, which you know is configured correctly for this NixOS configuration,
please import the `nixosModules.readOnlyPkgs` module from the nixpkgs flake or
`(modulesPath + "/misc/nixpkgs/read-only.nix"), and set `{ nixpkgs.pkgs = <your pkgs>; }`.
This properly disables the ignored options to prevent future surprises.
''
x;
withWarnings =
x:
lib.warnIf (evalConfigArgs ? extraArgs)
"The extraArgs argument to eval-config.nix is deprecated. Please set config._module.args instead."
lib.warnIf
(evalConfigArgs ? check)
"The check argument to eval-config.nix is deprecated. Please set config._module.check instead."
lib.warnIf
(specialArgs ? pkgs)
''
You have set specialArgs.pkgs, which means that options like nixpkgs.config
and nixpkgs.overlays will be ignored. If you wish to reuse an already created
pkgs, which you know is configured correctly for this NixOS configuration,
please import the `nixosModules.readOnlyPkgs` module from the nixpkgs flake or
`(modulesPath + "/misc/nixpkgs/read-only.nix"), and set `{ nixpkgs.pkgs = <your pkgs>; }`.
This properly disables the ignored options to prevent future surprises.
''
x;
legacyModules =
lib.optional (evalConfigArgs?extraArgs) {
lib.optional (evalConfigArgs ? extraArgs) {
config = {
_module.args = extraArgs;
};
}
++ lib.optional (evalConfigArgs?check) {
++ lib.optional (evalConfigArgs ? check) {
config = {
_module.check = lib.mkDefault check;
};
@ -118,29 +127,43 @@ let
else
map (lib.setDefaultModuleLocation modulesLocation) modules;
in
locatedModules ++ legacyModules;
locatedModules ++ legacyModules;
noUserModules = evalModulesMinimal ({
inherit prefix specialArgs;
modules = baseModules ++ extraModules ++ [ pkgsModule modulesModule ];
modules =
baseModules
++ extraModules
++ [
pkgsModule
modulesModule
];
});
# Extra arguments that are useful for constructing a similar configuration.
modulesModule = {
config = {
_module.args = {
inherit noUserModules baseModules extraModules modules;
inherit
noUserModules
baseModules
extraModules
modules
;
};
};
};
nixosWithUserModules = noUserModules.extendModules { modules = allUserModules; };
withExtraAttrs = configuration: configuration // {
inherit extraArgs;
inherit (configuration._module.args) pkgs;
inherit lib;
extendModules = args: withExtraAttrs (configuration.extendModules args);
};
withExtraAttrs =
configuration:
configuration
// {
inherit extraArgs;
inherit (configuration._module.args) pkgs;
inherit lib;
extendModules = args: withExtraAttrs (configuration.extendModules args);
};
in
withWarnings (withExtraAttrs nixosWithUserModules)

View File

@ -3,20 +3,22 @@
# contents of a directory that can be populated with commands. The
# generated image is sized to only fit its contents, with the expectation
# that a script resizes the filesystem at boot time.
{ pkgs
, lib
# List of derivations to be included
, storePaths
# Whether or not to compress the resulting image with zstd
, compressImage ? false, zstd
# Shell commands to populate the ./files directory.
# All files in that directory are copied to the root of the FS.
, populateImageCommands ? ""
, volumeLabel
, uuid ? "44444444-4444-4444-8888-888888888888"
, btrfs-progs
, libfaketime
, fakeroot
{
pkgs,
lib,
# List of derivations to be included
storePaths,
# Whether or not to compress the resulting image with zstd
compressImage ? false,
zstd,
# Shell commands to populate the ./files directory.
# All files in that directory are copied to the root of the FS.
populateImageCommands ? "",
volumeLabel,
uuid ? "44444444-4444-4444-8888-888888888888",
btrfs-progs,
libfaketime,
fakeroot,
}:
let
@ -25,43 +27,46 @@ in
pkgs.stdenv.mkDerivation {
name = "btrfs-fs.img${lib.optionalString compressImage ".zst"}";
nativeBuildInputs = [ btrfs-progs libfaketime fakeroot ] ++ lib.optional compressImage zstd;
nativeBuildInputs = [
btrfs-progs
libfaketime
fakeroot
] ++ lib.optional compressImage zstd;
buildCommand =
''
${if compressImage then "img=temp.img" else "img=$out"}
buildCommand = ''
${if compressImage then "img=temp.img" else "img=$out"}
set -x
(
mkdir -p ./files
${populateImageCommands}
)
set -x
(
mkdir -p ./files
${populateImageCommands}
)
mkdir -p ./rootImage/nix/store
mkdir -p ./rootImage/nix/store
xargs -I % cp -a --reflink=auto % -t ./rootImage/nix/store/ < ${sdClosureInfo}/store-paths
(
GLOBIGNORE=".:.."
shopt -u dotglob
xargs -I % cp -a --reflink=auto % -t ./rootImage/nix/store/ < ${sdClosureInfo}/store-paths
(
GLOBIGNORE=".:.."
shopt -u dotglob
for f in ./files/*; do
cp -a --reflink=auto -t ./rootImage/ "$f"
done
)
for f in ./files/*; do
cp -a --reflink=auto -t ./rootImage/ "$f"
done
)
cp ${sdClosureInfo}/registration ./rootImage/nix-path-registration
cp ${sdClosureInfo}/registration ./rootImage/nix-path-registration
touch $img
faketime -f "1970-01-01 00:00:01" fakeroot mkfs.btrfs -L ${volumeLabel} -U ${uuid} -r ./rootImage --shrink $img
touch $img
faketime -f "1970-01-01 00:00:01" fakeroot mkfs.btrfs -L ${volumeLabel} -U ${uuid} -r ./rootImage --shrink $img
if ! btrfs check $img; then
echo "--- 'btrfs check' failed for BTRFS image ---"
return 1
fi
if ! btrfs check $img; then
echo "--- 'btrfs check' failed for BTRFS image ---"
return 1
fi
if [ ${builtins.toString compressImage} ]; then
echo "Compressing image"
zstd -v --no-progress ./$img -o $out
fi
'';
if [ ${builtins.toString compressImage} ]; then
echo "Compressing image"
zstd -v --no-progress ./$img -o $out
fi
'';
}

View File

@ -703,28 +703,28 @@ let
''}
${lib.optionalString installBootLoader ''
# In this throwaway resource, we only have /dev/vda, but the actual VM may refer to another disk for bootloader, e.g. /dev/vdb
# Use this option to create a symlink from vda to any arbitrary device you want.
${lib.optionalString (config.boot.loader.grub.enable) (
lib.concatMapStringsSep " " (
device:
lib.optionalString (device != "/dev/vda") ''
mkdir -p "$(dirname ${device})"
ln -s /dev/vda ${device}
''
) config.boot.loader.grub.devices
)}
# In this throwaway resource, we only have /dev/vda, but the actual VM may refer to another disk for bootloader, e.g. /dev/vdb
# Use this option to create a symlink from vda to any arbitrary device you want.
${lib.optionalString (config.boot.loader.grub.enable) (
lib.concatMapStringsSep " " (
device:
lib.optionalString (device != "/dev/vda") ''
mkdir -p "$(dirname ${device})"
ln -s /dev/vda ${device}
''
) config.boot.loader.grub.devices
)}
# Set up core system link, bootloader (sd-boot, GRUB, uboot, etc.), etc.
# Set up core system link, bootloader (sd-boot, GRUB, uboot, etc.), etc.
# NOTE: systemd-boot-builder.py calls nix-env --list-generations which
# clobbers $HOME/.nix-defexpr/channels/nixos This would cause a folder
# /homeless-shelter to show up in the final image which in turn breaks
# nix builds in the target image if sandboxing is turned off (through
# __noChroot for example).
export HOME=$TMPDIR
NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot
''}
# NOTE: systemd-boot-builder.py calls nix-env --list-generations which
# clobbers $HOME/.nix-defexpr/channels/nixos This would cause a folder
# /homeless-shelter to show up in the final image which in turn breaks
# nix builds in the target image if sandboxing is turned off (through
# __noChroot for example).
export HOME=$TMPDIR
NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot
''}
# Set the ownerships of the contents. The modes are set in preVM.
# No globbing on targets, so no need to set -f

View File

@ -1,18 +1,22 @@
{ lib, stdenv, squashfsTools, closureInfo
{
lib,
stdenv,
squashfsTools,
closureInfo,
, fileName ? "squashfs"
, # The root directory of the squashfs filesystem is filled with the
fileName ? "squashfs",
# The root directory of the squashfs filesystem is filled with the
# closures of the Nix store paths listed here.
storeContents ? []
storeContents ? [ ],
# Pseudo files to be added to squashfs image
, pseudoFiles ? []
, noStrip ? false
, # Compression parameters.
pseudoFiles ? [ ],
noStrip ? false,
# Compression parameters.
# For zstd compression you can use "zstd -Xcompression-level 6".
comp ? "xz -Xdict-size 100%"
, # create hydra build product. will put image in directory instead
comp ? "xz -Xdict-size 100%",
# create hydra build product. will put image in directory instead
# of directly in the store
hydraBuildProduct ? false
hydraBuildProduct ? false,
}:
let
@ -34,24 +38,28 @@ stdenv.mkDerivation {
cp $closureInfo/registration nix-path-registration
imgPath="$out"
'' + lib.optionalString hydraBuildProduct ''
''
+ lib.optionalString hydraBuildProduct ''
mkdir $out
imgPath="$out/${fileName}.squashfs"
'' + lib.optionalString stdenv.buildPlatform.is32bit ''
''
+ lib.optionalString stdenv.buildPlatform.is32bit ''
# 64 cores on i686 does not work
# fails with FATAL ERROR: mangle2:: xz compress failed with error code 5
if ((NIX_BUILD_CORES > 48)); then
NIX_BUILD_CORES=48
fi
'' + ''
''
+ ''
# Generate the squashfs image.
mksquashfs nix-path-registration $(cat $closureInfo/store-paths) $imgPath ${pseudoFilesArgs} \
-no-hardlinks ${lib.optionalString noStrip "-no-strip"} -keep-as-directory -all-root -b 1048576 ${compFlag} \
-processors $NIX_BUILD_CORES -root-mode 0755
'' + lib.optionalString hydraBuildProduct ''
''
+ lib.optionalString hydraBuildProduct ''
mkdir -p $out/nix-support
echo "file squashfs-image $out/${fileName}.squashfs" >> $out/nix-support/hydra-build-products

File diff suppressed because it is too large Load Diff

View File

@ -30,24 +30,39 @@ let
checkService = checkUnitConfig "Service" [
(assertValueOneOf "Type" [
"exec" "simple" "forking" "oneshot" "dbus" "notify" "notify-reload" "idle"
"exec"
"simple"
"forking"
"oneshot"
"dbus"
"notify"
"notify-reload"
"idle"
])
(assertValueOneOf "Restart" [
"no" "on-success" "on-failure" "on-abnormal" "on-abort" "always"
"no"
"on-success"
"on-failure"
"on-abnormal"
"on-abort"
"always"
])
];
in rec {
in
rec {
unitOption = mkOptionType {
name = "systemd option";
merge = loc: defs:
merge =
loc: defs:
let
defs' = filterOverrides defs;
in
if any (def: isList def.value) defs'
then concatMap (def: toList def.value) defs'
else mergeEqualOption loc defs';
if any (def: isList def.value) defs' then
concatMap (def: toList def.value) defs'
else
mergeEqualOption loc defs';
};
sharedOptions = {
@ -76,7 +91,10 @@ in rec {
overrideStrategy = mkOption {
default = "asDropinIfExists";
type = types.enum [ "asDropinIfExists" "asDropin" ];
type = types.enum [
"asDropinIfExists"
"asDropin"
];
description = ''
Defines how unit configuration is provided for systemd:
@ -91,7 +109,7 @@ in rec {
};
requiredBy = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
Units that require (i.e. depend on and need to go down with) this unit.
@ -101,7 +119,7 @@ in rec {
};
upheldBy = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
Keep this unit running as long as the listed units are running. This is a continuously
@ -110,7 +128,7 @@ in rec {
};
wantedBy = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
Units that want (i.e. depend on) this unit. The default method for
@ -128,7 +146,7 @@ in rec {
};
aliases = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = "Aliases of that unit.";
};
@ -160,13 +178,13 @@ in rec {
};
documentation = mkOption {
default = [];
default = [ ];
type = types.listOf types.str;
description = "A list of URIs referencing documentation for this unit or its configuration.";
};
requires = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
Start the specified units when this unit is started, and stop
@ -175,7 +193,7 @@ in rec {
};
wants = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
Start the specified units when this unit is started.
@ -183,7 +201,7 @@ in rec {
};
upholds = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
Keeps the specified running while this unit is running. A continuous version of `wants`.
@ -191,7 +209,7 @@ in rec {
};
after = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
If the specified units are started at the same time as
@ -200,7 +218,7 @@ in rec {
};
before = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
If the specified units are started at the same time as
@ -209,7 +227,7 @@ in rec {
};
bindsTo = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
Like requires, but in addition, if the specified units
@ -218,7 +236,7 @@ in rec {
};
partOf = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
If the specified units are stopped or restarted, then this
@ -227,7 +245,7 @@ in rec {
};
conflicts = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
If the specified units are started, then this unit is stopped
@ -236,7 +254,7 @@ in rec {
};
requisite = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
Similar to requires. However if the units listed are not started,
@ -245,8 +263,10 @@ in rec {
};
unitConfig = mkOption {
default = {};
example = { RequiresMountsFor = "/data"; };
default = { };
example = {
RequiresMountsFor = "/data";
};
type = types.attrsOf unitOption;
description = ''
Each attribute in this set specifies an option in the
@ -256,7 +276,7 @@ in rec {
};
onFailure = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
A list of one or more units that are activated when
@ -265,7 +285,7 @@ in rec {
};
onSuccess = mkOption {
default = [];
default = [ ];
type = types.listOf unitNameType;
description = ''
A list of one or more units that are activated when
@ -274,21 +294,21 @@ in rec {
};
startLimitBurst = mkOption {
type = types.int;
description = ''
Configure unit start rate limiting. Units which are started
more than startLimitBurst times within an interval time
interval are not permitted to start any more.
'';
type = types.int;
description = ''
Configure unit start rate limiting. Units which are started
more than startLimitBurst times within an interval time
interval are not permitted to start any more.
'';
};
startLimitIntervalSec = mkOption {
type = types.int;
description = ''
Configure unit start rate limiting. Units which are started
more than startLimitBurst times within an interval time
interval are not permitted to start any more.
'';
type = types.int;
description = ''
Configure unit start rate limiting. Units which are started
more than startLimitBurst times within an interval time
interval are not permitted to start any more.
'';
};
};
@ -301,7 +321,7 @@ in rec {
options = {
restartTriggers = mkOption {
default = [];
default = [ ];
type = types.listOf types.unspecified;
description = ''
An arbitrary list of items such as derivations. If any item
@ -311,7 +331,7 @@ in rec {
};
reloadTriggers = mkOption {
default = [];
default = [ ];
type = types.listOf unitOption;
description = ''
An arbitrary list of items such as derivations. If any item
@ -324,170 +344,188 @@ in rec {
};
stage1CommonUnitOptions = commonUnitOptions;
serviceOptions = { name, config, ... }: {
options = {
serviceOptions =
{ name, config, ... }:
{
options = {
environment = mkOption {
default = {};
type = with types; attrsOf (nullOr (oneOf [ str path package ]));
example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
description = "Environment variables passed to the service's processes.";
};
path = mkOption {
default = [];
type = with types; listOf (oneOf [ package str ]);
description = ''
Packages added to the service's {env}`PATH`
environment variable. Both the {file}`bin`
and {file}`sbin` subdirectories of each
package are added.
'';
};
serviceConfig = mkOption {
default = {};
example =
{ RestartSec = 5;
environment = mkOption {
default = { };
type =
with types;
attrsOf (
nullOr (oneOf [
str
path
package
])
);
example = {
PATH = "/foo/bar/bin";
LANG = "nl_NL.UTF-8";
};
type = types.addCheck (types.attrsOf unitOption) checkService;
description = ''
Each attribute in this set specifies an option in the
`[Service]` section of the unit. See
{manpage}`systemd.service(5)` for details.
'';
description = "Environment variables passed to the service's processes.";
};
path = mkOption {
default = [ ];
type =
with types;
listOf (oneOf [
package
str
]);
description = ''
Packages added to the service's {env}`PATH`
environment variable. Both the {file}`bin`
and {file}`sbin` subdirectories of each
package are added.
'';
};
serviceConfig = mkOption {
default = { };
example = {
RestartSec = 5;
};
type = types.addCheck (types.attrsOf unitOption) checkService;
description = ''
Each attribute in this set specifies an option in the
`[Service]` section of the unit. See
{manpage}`systemd.service(5)` for details.
'';
};
enableStrictShellChecks = mkOption {
type = types.bool;
description = "Enable running shellcheck on the generated scripts for this unit.";
# The default gets set in systemd-lib.nix because we don't have access to
# the full NixOS config here.
defaultText = literalExpression "config.systemd.enableStrictShellChecks";
};
script = mkOption {
type = types.lines;
default = "";
description = "Shell commands executed as the service's main process.";
};
scriptArgs = mkOption {
type = types.str;
default = "";
example = "%i";
description = ''
Arguments passed to the main process script.
Can contain specifiers (`%` placeholders expanded by systemd, see {manpage}`systemd.unit(5)`).
'';
};
preStart = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed before the service's main process
is started.
'';
};
postStart = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed after the service's main process
is started.
'';
};
reload = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed when the service's main process
is reloaded.
'';
};
preStop = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed to stop the service.
'';
};
postStop = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed after the service's main process
has exited.
'';
};
jobScripts = mkOption {
type = with types; coercedTo path singleton (listOf path);
internal = true;
description = "A list of all job script derivations of this unit.";
default = [ ];
};
};
enableStrictShellChecks = mkOption {
type = types.bool;
description = "Enable running shellcheck on the generated scripts for this unit.";
# The default gets set in systemd-lib.nix because we don't have access to
# the full NixOS config here.
defaultText = literalExpression "config.systemd.enableStrictShellChecks";
};
script = mkOption {
type = types.lines;
default = "";
description = "Shell commands executed as the service's main process.";
};
scriptArgs = mkOption {
type = types.str;
default = "";
example = "%i";
description = ''
Arguments passed to the main process script.
Can contain specifiers (`%` placeholders expanded by systemd, see {manpage}`systemd.unit(5)`).
'';
};
preStart = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed before the service's main process
is started.
'';
};
postStart = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed after the service's main process
is started.
'';
};
reload = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed when the service's main process
is reloaded.
'';
};
preStop = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed to stop the service.
'';
};
postStop = mkOption {
type = types.lines;
default = "";
description = ''
Shell commands executed after the service's main process
has exited.
'';
};
jobScripts = mkOption {
type = with types; coercedTo path singleton (listOf path);
internal = true;
description = "A list of all job script derivations of this unit.";
default = [];
};
config = mkMerge [
(mkIf (config.preStart != "") rec {
jobScripts = makeJobScript {
name = "${name}-pre-start";
text = config.preStart;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStartPre = [ jobScripts ];
})
(mkIf (config.script != "") rec {
jobScripts = makeJobScript {
name = "${name}-start";
text = config.script;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStart = jobScripts + " " + config.scriptArgs;
})
(mkIf (config.postStart != "") rec {
jobScripts = makeJobScript {
name = "${name}-post-start";
text = config.postStart;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStartPost = [ jobScripts ];
})
(mkIf (config.reload != "") rec {
jobScripts = makeJobScript {
name = "${name}-reload";
text = config.reload;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecReload = jobScripts;
})
(mkIf (config.preStop != "") rec {
jobScripts = makeJobScript {
name = "${name}-pre-stop";
text = config.preStop;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStop = jobScripts;
})
(mkIf (config.postStop != "") rec {
jobScripts = makeJobScript {
name = "${name}-post-stop";
text = config.postStop;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStopPost = jobScripts;
})
];
};
config = mkMerge [
(mkIf (config.preStart != "") rec {
jobScripts = makeJobScript {
name = "${name}-pre-start";
text = config.preStart;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStartPre = [ jobScripts ];
})
(mkIf (config.script != "") rec {
jobScripts = makeJobScript {
name = "${name}-start";
text = config.script;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStart = jobScripts + " " + config.scriptArgs;
})
(mkIf (config.postStart != "") rec {
jobScripts = makeJobScript {
name = "${name}-post-start";
text = config.postStart;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStartPost = [ jobScripts ];
})
(mkIf (config.reload != "") rec {
jobScripts = makeJobScript {
name = "${name}-reload";
text = config.reload;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecReload = jobScripts;
})
(mkIf (config.preStop != "") rec {
jobScripts = makeJobScript {
name = "${name}-pre-stop";
text = config.preStop;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStop = jobScripts;
})
(mkIf (config.postStop != "") rec {
jobScripts = makeJobScript {
name = "${name}-post-stop";
text = config.postStop;
inherit (config) enableStrictShellChecks;
};
serviceConfig.ExecStopPost = jobScripts;
})
];
};
stage2ServiceOptions = {
imports = [
stage2CommonUnitOptions
@ -549,7 +587,7 @@ in rec {
startAt = mkOption {
type = with types; either str (listOf str);
default = [];
default = [ ];
example = "Sun 14:00:00";
description = ''
Automatically start this unit at the given date/time, which
@ -570,14 +608,16 @@ in rec {
];
};
socketOptions = {
options = {
listenStreams = mkOption {
default = [];
default = [ ];
type = types.listOf types.str;
example = [ "0.0.0.0:993" "/run/my-socket" ];
example = [
"0.0.0.0:993"
"/run/my-socket"
];
description = ''
For each item in this list, a `ListenStream`
option in the `[Socket]` section will be created.
@ -585,9 +625,12 @@ in rec {
};
listenDatagrams = mkOption {
default = [];
default = [ ];
type = types.listOf types.str;
example = [ "0.0.0.0:993" "/run/my-socket" ];
example = [
"0.0.0.0:993"
"/run/my-socket"
];
description = ''
For each item in this list, a `ListenDatagram`
option in the `[Socket]` section will be created.
@ -595,8 +638,10 @@ in rec {
};
socketConfig = mkOption {
default = {};
example = { ListenStream = "/run/my-socket"; };
default = { };
example = {
ListenStream = "/run/my-socket";
};
type = types.attrsOf unitOption;
description = ''
Each attribute in this set specifies an option in the
@ -622,13 +667,15 @@ in rec {
];
};
timerOptions = {
options = {
timerConfig = mkOption {
default = {};
example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
default = { };
example = {
OnCalendar = "Sun 14:00:00";
Unit = "foo.service";
};
type = types.attrsOf unitOption;
description = ''
Each attribute in this set specifies an option in the
@ -655,13 +702,15 @@ in rec {
];
};
pathOptions = {
options = {
pathConfig = mkOption {
default = {};
example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
default = { };
example = {
PathChanged = "/some/path";
Unit = "changedpath.service";
};
type = types.attrsOf unitOption;
description = ''
Each attribute in this set specifies an option in the
@ -687,7 +736,6 @@ in rec {
];
};
mountOptions = {
options = {
@ -721,8 +769,10 @@ in rec {
};
mountConfig = mkOption {
default = {};
example = { DirectoryMode = "0775"; };
default = { };
example = {
DirectoryMode = "0775";
};
type = types.attrsOf unitOption;
description = ''
Each attribute in this set specifies an option in the
@ -761,8 +811,10 @@ in rec {
};
automountConfig = mkOption {
default = {};
example = { DirectoryMode = "0775"; };
default = { };
example = {
DirectoryMode = "0775";
};
type = types.attrsOf unitOption;
description = ''
Each attribute in this set specifies an option in the
@ -792,8 +844,10 @@ in rec {
options = {
sliceConfig = mkOption {
default = {};
example = { MemoryMax = "2G"; };
default = { };
example = {
MemoryMax = "2G";
};
type = types.attrsOf unitOption;
description = ''
Each attribute in this set specifies an option in the

View File

@ -1,4 +1,9 @@
{ config, lib, hostPkgs, ... }:
{
config,
lib,
hostPkgs,
...
}:
let
inherit (lib) mkOption types literalMD;
@ -11,10 +16,9 @@ let
tesseract4 = hostPkgs.tesseract4.override { enableLanguages = [ "eng" ]; };
};
vlans = map (m: (
m.virtualisation.vlans ++
(lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
vlans = map (
m: (m.virtualisation.vlans ++ (lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))
) (lib.attrValues config.nodes);
vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
nodeHostNames =
@ -23,13 +27,14 @@ let
in
nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
pythonizeName = name:
pythonizeName =
name:
let
head = lib.substring 0 1 name;
tail = lib.substring 1 (-1) name;
in
(if builtins.match "[A-z_]" head == null then "_" else head) +
lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
(if builtins.match "[A-z_]" head == null then "_" else head)
+ lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
uniqueVlans = lib.unique (builtins.concatLists vlans);
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
@ -96,7 +101,12 @@ let
--set testScript "$out/test-script" \
--set globalTimeout "${toString config.globalTimeout}" \
--set vlans '${toString vlans}' \
${lib.escapeShellArgs (lib.concatMap (arg: ["--add-flags" arg]) config.extraDriverArgs)}
${lib.escapeShellArgs (
lib.concatMap (arg: [
"--add-flags"
arg
]) config.extraDriverArgs
)}
'';
in
@ -165,7 +175,7 @@ in
They become part of [{option}`driver`](#test-opt-driver) via `wrapProgram`.
'';
type = types.listOf types.str;
default = [];
default = [ ];
};
skipLint = mkOption {
@ -191,8 +201,7 @@ in
_module.args = {
hostPkgs =
# Comment is in nixos/modules/misc/nixpkgs.nix
lib.mkOverride lib.modules.defaultOverridePriority
config.hostPkgs.__splicedPackages;
lib.mkOverride lib.modules.defaultOverridePriority config.hostPkgs.__splicedPackages;
};
driver = withChecks driver;

View File

@ -1,9 +1,14 @@
# nix-build '<nixpkgs/nixos>' -A config.system.build.openstackImage --arg configuration "{ imports = [ ./nixos/maintainers/scripts/openstack/openstack-image.nix ]; }"
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
copyChannel = true;
format = "qcow2";
format = "qcow2";
in
{
imports = [
@ -17,16 +22,20 @@ in
system.nixos.tags = [ "openstack" ];
system.build.image = config.system.build.openstackImage;
system.build.openstackImage = import ../../../lib/make-disk-image.nix {
inherit lib config copyChannel format;
inherit
lib
config
copyChannel
format
;
inherit (config.image) baseName;
additionalSpace = "1024M";
pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
configFile = pkgs.writeText "configuration.nix"
''
{
imports = [ <nixpkgs/nixos/modules/virtualisation/openstack-config.nix> ];
}
'';
configFile = pkgs.writeText "configuration.nix" ''
{
imports = [ <nixpkgs/nixos/modules/virtualisation/openstack-config.nix> ];
}
'';
};
}

View File

@ -1,18 +1,26 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.console;
makeColor = i: lib.concatMapStringsSep "," (x: "0x" + lib.substring (2*i) 2 x);
makeColor = i: lib.concatMapStringsSep "," (x: "0x" + lib.substring (2 * i) 2 x);
isUnicode = lib.hasSuffix "UTF-8" (lib.toUpper config.i18n.defaultLocale);
optimizedKeymap = pkgs.runCommand "keymap" {
nativeBuildInputs = [ pkgs.buildPackages.kbd ];
LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
preferLocalBuild = true;
} ''
loadkeys -b ${lib.optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
'';
optimizedKeymap =
pkgs.runCommand "keymap"
{
nativeBuildInputs = [ pkgs.buildPackages.kbd ];
LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
preferLocalBuild = true;
}
''
loadkeys -b ${lib.optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
'';
# Sadly, systemd-vconsole-setup doesn't support binary keymaps.
vconsoleConf = pkgs.writeText "vconsole.conf" ''
@ -20,22 +28,24 @@ let
${lib.optionalString (cfg.font != null) "FONT=${cfg.font}"}
'';
consoleEnv = kbd: pkgs.buildEnv {
name = "console-env";
paths = [ kbd ] ++ cfg.packages;
pathsToLink = [
"/share/consolefonts"
"/share/consoletrans"
"/share/keymaps"
"/share/unimaps"
];
};
consoleEnv =
kbd:
pkgs.buildEnv {
name = "console-env";
paths = [ kbd ] ++ cfg.packages;
pathsToLink = [
"/share/consolefonts"
"/share/consoletrans"
"/share/keymaps"
"/share/unimaps"
];
};
in
{
###### interface
options.console = {
options.console = {
enable = lib.mkEnableOption "virtual console" // {
default = true;
};
@ -70,10 +80,22 @@ in
type = with lib.types; listOf (strMatching "[[:xdigit:]]{6}");
default = [ ];
example = [
"002b36" "dc322f" "859900" "b58900"
"268bd2" "d33682" "2aa198" "eee8d5"
"002b36" "cb4b16" "586e75" "657b83"
"839496" "6c71c4" "93a1a1" "fdf6e3"
"002b36"
"dc322f"
"859900"
"b58900"
"268bd2"
"d33682"
"2aa198"
"eee8d5"
"002b36"
"cb4b16"
"586e75"
"657b83"
"839496"
"6c71c4"
"93a1a1"
"fdf6e3"
];
description = ''
The 16 colors palette used by the virtual consoles.
@ -112,103 +134,127 @@ in
};
###### implementation
config = lib.mkMerge [
{ console.keyMap = with config.services.xserver;
lib.mkIf cfg.useXkbConfig
(pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
{
console.keyMap =
with config.services.xserver;
lib.mkIf cfg.useXkbConfig (
pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
'${pkgs.buildPackages.ckbcomp}/bin/ckbcomp' \
${lib.optionalString (config.environment.sessionVariables ? XKB_CONFIG_ROOT)
"-I${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
${
lib.optionalString (
config.environment.sessionVariables ? XKB_CONFIG_ROOT
) "-I${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
} \
-model '${xkb.model}' -layout '${xkb.layout}' \
-option '${xkb.options}' -variant '${xkb.variant}' > "$out"
'');
''
);
}
(lib.mkIf cfg.enable (lib.mkMerge [
{ environment.systemPackages = [ pkgs.kbd ];
(lib.mkIf cfg.enable (
lib.mkMerge [
{
environment.systemPackages = [ pkgs.kbd ];
# Let systemd-vconsole-setup.service do the work of setting up the
# virtual consoles.
environment.etc."vconsole.conf".source = vconsoleConf;
# Provide kbd with additional packages.
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
# Let systemd-vconsole-setup.service do the work of setting up the
# virtual consoles.
environment.etc."vconsole.conf".source = vconsoleConf;
# Provide kbd with additional packages.
environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
boot.initrd.preLVMCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (lib.mkBefore ''
kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
loadkmap < ${optimizedKeymap}
boot.initrd.preLVMCommands = lib.mkIf (!config.boot.initrd.systemd.enable) (
lib.mkBefore ''
kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
loadkmap < ${optimizedKeymap}
${lib.optionalString (cfg.earlySetup && cfg.font != null) ''
setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
''}
'');
${lib.optionalString (cfg.earlySetup && cfg.font != null) ''
setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
''}
''
);
boot.initrd.systemd.contents = {
"/etc/vconsole.conf".source = vconsoleConf;
# Add everything if we want full console setup...
"/etc/kbd" = lib.mkIf cfg.earlySetup { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share"; };
# ...but only the keymaps if we don't
"/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps"; };
};
boot.initrd.systemd.additionalUpstreamUnits = [
"systemd-vconsole-setup.service"
];
boot.initrd.systemd.storePaths = [
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
"${config.boot.initrd.systemd.package.kbd}/bin/setfont"
"${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
"${config.boot.initrd.systemd.package.kbd.gzip}/bin/gzip" # Fonts and keyboard layouts are compressed
] ++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
"${cfg.font}"
] ++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
"${cfg.keyMap}"
];
systemd.additionalUpstreamSystemUnits = [
"systemd-vconsole-setup.service"
];
systemd.services.reload-systemd-vconsole-setup =
{ description = "Reset console on configuration changes";
wantedBy = [ "multi-user.target" ];
restartTriggers = [ vconsoleConf (consoleEnv pkgs.kbd) ];
reloadIfChanged = true;
serviceConfig =
{ RemainAfterExit = true;
ExecStart = "${pkgs.coreutils}/bin/true";
ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
};
boot.initrd.systemd.contents = {
"/etc/vconsole.conf".source = vconsoleConf;
# Add everything if we want full console setup...
"/etc/kbd" = lib.mkIf cfg.earlySetup {
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share";
};
# ...but only the keymaps if we don't
"/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) {
source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps";
};
};
}
boot.initrd.systemd.additionalUpstreamUnits = [
"systemd-vconsole-setup.service"
];
boot.initrd.systemd.storePaths =
[
"${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
"${config.boot.initrd.systemd.package.kbd}/bin/setfont"
"${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
"${config.boot.initrd.systemd.package.kbd.gzip}/bin/gzip" # Fonts and keyboard layouts are compressed
]
++ lib.optionals (cfg.font != null && lib.hasPrefix builtins.storeDir cfg.font) [
"${cfg.font}"
]
++ lib.optionals (lib.hasPrefix builtins.storeDir cfg.keyMap) [
"${cfg.keyMap}"
];
(lib.mkIf (cfg.colors != []) {
boot.kernelParams = [
"vt.default_red=${makeColor 0 cfg.colors}"
"vt.default_grn=${makeColor 1 cfg.colors}"
"vt.default_blu=${makeColor 2 cfg.colors}"
];
})
systemd.additionalUpstreamSystemUnits = [
"systemd-vconsole-setup.service"
];
(lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
boot.initrd.extraUtilsCommands = ''
mkdir -p $out/share/consolefonts
${if lib.substring 0 1 cfg.font == "/" then ''
font="${cfg.font}"
'' else ''
font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
''}
if [[ $font == *.gz ]]; then
gzip -cd $font > $out/share/consolefonts/font.psf
else
cp -L $font $out/share/consolefonts/font.psf
fi
'';
})
]))
systemd.services.reload-systemd-vconsole-setup = {
description = "Reset console on configuration changes";
wantedBy = [ "multi-user.target" ];
restartTriggers = [
vconsoleConf
(consoleEnv pkgs.kbd)
];
reloadIfChanged = true;
serviceConfig = {
RemainAfterExit = true;
ExecStart = "${pkgs.coreutils}/bin/true";
ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
};
};
}
(lib.mkIf (cfg.colors != [ ]) {
boot.kernelParams = [
"vt.default_red=${makeColor 0 cfg.colors}"
"vt.default_grn=${makeColor 1 cfg.colors}"
"vt.default_blu=${makeColor 2 cfg.colors}"
];
})
(lib.mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
boot.initrd.extraUtilsCommands = ''
mkdir -p $out/share/consolefonts
${
if lib.substring 0 1 cfg.font == "/" then
''
font="${cfg.font}"
''
else
''
font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
''
}
if [[ $font == *.gz ]]; then
gzip -cd $font > $out/share/consolefonts/font.psf
else
cp -L $font $out/share/consolefonts/font.psf
fi
'';
})
]
))
];
imports = [

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
{
###### interface
@ -39,8 +44,11 @@
extraLocaleSettings = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {};
example = { LC_MESSAGES = "en_US.UTF-8"; LC_TIME = "de_DE.UTF-8"; };
default = { };
example = {
LC_MESSAGES = "en_US.UTF-8";
LC_TIME = "de_DE.UTF-8";
};
description = ''
A set of additional system-wide locale settings other than
`LANG` which can be configured with
@ -50,14 +58,18 @@
supportedLocales = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = lib.unique
(builtins.map (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
[
"C.UTF-8"
"en_US.UTF-8"
config.i18n.defaultLocale
] ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
));
default = lib.unique (
builtins.map
(l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8")
(
[
"C.UTF-8"
"en_US.UTF-8"
config.i18n.defaultLocale
]
++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
)
);
defaultText = lib.literalExpression ''
lib.unique
(builtins.map (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
@ -68,7 +80,11 @@
] ++ (lib.attrValues (lib.filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
))
'';
example = ["en_US.UTF-8/UTF-8" "nl_NL.UTF-8/UTF-8" "nl_NL/ISO-8859-1"];
example = [
"en_US.UTF-8/UTF-8"
"nl_NL.UTF-8/UTF-8"
"nl_NL/ISO-8859-1"
];
description = ''
List of locales that the system should support. The value
`"all"` means that all locales supported by
@ -81,30 +97,30 @@
};
###### implementation
config = {
environment.systemPackages =
# We increase the priority a little, so that plain glibc in systemPackages can't win.
lib.optional (config.i18n.supportedLocales != []) (lib.setPrio (-1) config.i18n.glibcLocales);
lib.optional (config.i18n.supportedLocales != [ ]) (lib.setPrio (-1) config.i18n.glibcLocales);
environment.sessionVariables =
{ LANG = config.i18n.defaultLocale;
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
} // config.i18n.extraLocaleSettings;
environment.sessionVariables = {
LANG = config.i18n.defaultLocale;
LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
} // config.i18n.extraLocaleSettings;
systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != []) {
systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != [ ]) {
LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
};
# /etc/locale.conf is used by systemd.
environment.etc."locale.conf".source = pkgs.writeText "locale.conf"
''
LANG=${config.i18n.defaultLocale}
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings)}
'';
environment.etc."locale.conf".source = pkgs.writeText "locale.conf" ''
LANG=${config.i18n.defaultLocale}
${lib.concatStringsSep "\n" (
lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings
)}
'';
};
}

View File

@ -1,11 +1,24 @@
# /etc files related to networking, such as /etc/services.
{ config, lib, options, pkgs, ... }:
{
config,
lib,
options,
pkgs,
...
}:
let
cfg = config.networking;
opt = options.networking;
localhostMultiple = lib.any (lib.elem "localhost") (lib.attrValues (removeAttrs cfg.hosts [ "127.0.0.1" "::1" ]));
localhostMultiple = lib.any (lib.elem "localhost") (
lib.attrValues (
removeAttrs cfg.hosts [
"127.0.0.1"
"::1"
]
)
);
in
@ -136,7 +149,7 @@ in
envVars = lib.mkOption {
type = lib.types.attrs;
internal = true;
default = {};
default = { };
description = ''
Environment variables used for the network proxy.
'';
@ -146,48 +159,60 @@ in
config = {
assertions = [{
assertion = !localhostMultiple;
message = ''
`networking.hosts` maps "localhost" to something other than "127.0.0.1"
or "::1". This will break some applications. Please use
`networking.extraHosts` if you really want to add such a mapping.
'';
}];
assertions = [
{
assertion = !localhostMultiple;
message = ''
`networking.hosts` maps "localhost" to something other than "127.0.0.1"
or "::1". This will break some applications. Please use
`networking.extraHosts` if you really want to add such a mapping.
'';
}
];
# These entries are required for "hostname -f" and to resolve both the
# hostname and FQDN correctly:
networking.hosts = let
hostnames = # Note: The FQDN (canonical hostname) has to come first:
lib.optional (cfg.hostName != "" && cfg.domain != null) "${cfg.hostName}.${cfg.domain}"
++ lib.optional (cfg.hostName != "") cfg.hostName; # Then the hostname (without the domain)
in {
"127.0.0.2" = hostnames;
};
networking.hosts =
let
hostnames = # Note: The FQDN (canonical hostname) has to come first:
lib.optional (cfg.hostName != "" && cfg.domain != null) "${cfg.hostName}.${cfg.domain}"
++ lib.optional (cfg.hostName != "") cfg.hostName; # Then the hostname (without the domain)
in
{
"127.0.0.2" = hostnames;
};
networking.hostFiles = let
# Note: localhostHosts has to appear first in /etc/hosts so that 127.0.0.1
# resolves back to "localhost" (as some applications assume) instead of
# the FQDN! By default "networking.hosts" also contains entries for the
# FQDN so that e.g. "hostname -f" works correctly.
localhostHosts = pkgs.writeText "localhost-hosts" ''
127.0.0.1 localhost
${lib.optionalString cfg.enableIPv6 "::1 localhost"}
'';
stringHosts =
let
oneToString = set: ip: ip + " " + lib.concatStringsSep " " set.${ip} + "\n";
allToString = set: lib.concatMapStrings (oneToString set) (lib.attrNames set);
in pkgs.writeText "string-hosts" (allToString (lib.filterAttrs (_: v: v != []) cfg.hosts));
extraHosts = pkgs.writeText "extra-hosts" cfg.extraHosts;
in lib.mkBefore [ localhostHosts stringHosts extraHosts ];
networking.hostFiles =
let
# Note: localhostHosts has to appear first in /etc/hosts so that 127.0.0.1
# resolves back to "localhost" (as some applications assume) instead of
# the FQDN! By default "networking.hosts" also contains entries for the
# FQDN so that e.g. "hostname -f" works correctly.
localhostHosts = pkgs.writeText "localhost-hosts" ''
127.0.0.1 localhost
${lib.optionalString cfg.enableIPv6 "::1 localhost"}
'';
stringHosts =
let
oneToString = set: ip: ip + " " + lib.concatStringsSep " " set.${ip} + "\n";
allToString = set: lib.concatMapStrings (oneToString set) (lib.attrNames set);
in
pkgs.writeText "string-hosts" (allToString (lib.filterAttrs (_: v: v != [ ]) cfg.hosts));
extraHosts = pkgs.writeText "extra-hosts" cfg.extraHosts;
in
lib.mkBefore [
localhostHosts
stringHosts
extraHosts
];
environment.etc =
{ # /etc/services: TCP/UDP port assignments.
{
# /etc/services: TCP/UDP port assignments.
services.source = pkgs.iana-etc + "/etc/services";
# /etc/protocols: IP protocol numbers.
protocols.source = pkgs.iana-etc + "/etc/protocols";
protocols.source = pkgs.iana-etc + "/etc/protocols";
# /etc/hosts: Hostname-to-IP mappings.
hosts.source = pkgs.concatText "hosts" cfg.hostFiles;
@ -200,28 +225,35 @@ in
multi on
'';
} // lib.optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
}
// lib.optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
# /etc/rpc: RPC program numbers.
rpc.source = pkgs.stdenv.cc.libc.out + "/etc/rpc";
};
networking.proxy.envVars =
lib.optionalAttrs (cfg.proxy.default != null) {
# other options already fallback to proxy.default
no_proxy = "127.0.0.1,localhost";
} // lib.optionalAttrs (cfg.proxy.httpProxy != null) {
http_proxy = cfg.proxy.httpProxy;
} // lib.optionalAttrs (cfg.proxy.httpsProxy != null) {
https_proxy = cfg.proxy.httpsProxy;
} // lib.optionalAttrs (cfg.proxy.rsyncProxy != null) {
rsync_proxy = cfg.proxy.rsyncProxy;
} // lib.optionalAttrs (cfg.proxy.ftpProxy != null) {
ftp_proxy = cfg.proxy.ftpProxy;
} // lib.optionalAttrs (cfg.proxy.allProxy != null) {
all_proxy = cfg.proxy.allProxy;
} // lib.optionalAttrs (cfg.proxy.noProxy != null) {
no_proxy = cfg.proxy.noProxy;
};
networking.proxy.envVars =
lib.optionalAttrs (cfg.proxy.default != null) {
# other options already fallback to proxy.default
no_proxy = "127.0.0.1,localhost";
}
// lib.optionalAttrs (cfg.proxy.httpProxy != null) {
http_proxy = cfg.proxy.httpProxy;
}
// lib.optionalAttrs (cfg.proxy.httpsProxy != null) {
https_proxy = cfg.proxy.httpsProxy;
}
// lib.optionalAttrs (cfg.proxy.rsyncProxy != null) {
rsync_proxy = cfg.proxy.rsyncProxy;
}
// lib.optionalAttrs (cfg.proxy.ftpProxy != null) {
ftp_proxy = cfg.proxy.ftpProxy;
}
// lib.optionalAttrs (cfg.proxy.allProxy != null) {
all_proxy = cfg.proxy.allProxy;
}
// lib.optionalAttrs (cfg.proxy.noProxy != null) {
no_proxy = cfg.proxy.noProxy;
};
# Install the proxy environment variables
environment.sessionVariables = cfg.proxy.envVars;

View File

@ -5,7 +5,7 @@
See also
- ./nix.nix
- ./nix-flakes.nix
*/
*/
{ config, lib, ... }:
let
inherit (lib)
@ -42,13 +42,14 @@ in
nixPath = mkOption {
type = types.listOf types.str;
default =
if cfg.channel.enable
then [
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
"nixos-config=/etc/nixos/configuration.nix"
"/nix/var/nix/profiles/per-user/root/channels"
]
else [ ];
if cfg.channel.enable then
[
"nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
"nixos-config=/etc/nixos/configuration.nix"
"/nix/var/nix/profiles/per-user/root/channels"
]
else
[ ];
defaultText = ''
if nix.channel.enable
then [
@ -78,12 +79,11 @@ in
config = mkIf cfg.enable {
environment.extraInit =
mkIf cfg.channel.enable ''
if [ -e "$HOME/.nix-defexpr/channels" ]; then
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
fi
'';
environment.extraInit = mkIf cfg.channel.enable ''
if [ -e "$HOME/.nix-defexpr/channels" ]; then
export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
fi
'';
environment.extraSetup = mkIf (!cfg.channel.enable) ''
rm --force $out/bin/nix-channel
@ -99,7 +99,8 @@ in
''f /root/.nix-channels - - - - ${config.system.defaultChannel} nixos\n''
];
system.activationScripts.no-nix-channel = mkIf (!cfg.channel.enable)
(stringAfter [ "etc" "users" ] (builtins.readFile ./nix-channel/activation-check.sh));
system.activationScripts.no-nix-channel = mkIf (!cfg.channel.enable) (
stringAfter [ "etc" "users" ] (builtins.readFile ./nix-channel/activation-check.sh)
);
};
}

View File

@ -6,8 +6,13 @@
- ./nix-flakes.nix
- ./nix-remote-build.nix
- nixos/modules/services/system/nix-daemon.nix
*/
{ config, lib, pkgs, ... }:
*/
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
@ -45,12 +50,14 @@ let
isNixAtLeast = versionAtLeast (getVersion nixPackage);
defaultSystemFeatures = [
"nixos-test"
"benchmark"
"big-parallel"
"kvm"
] ++ optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
defaultSystemFeatures =
[
"nixos-test"
"benchmark"
"big-parallel"
"kvm"
]
++ optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
# a builder can run code for `gcc.arch` and inferior architectures
[ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ]
++ map (x: "gccarch-${x}") (
@ -73,19 +80,21 @@ let
systemFeatures = "system-features";
};
semanticConfType = with types;
semanticConfType =
with types;
let
confAtom = nullOr
(oneOf [
confAtom =
nullOr (oneOf [
bool
int
float
str
path
package
]) // {
description = "Nix config atom (null, bool, int, float, str, path or package)";
};
])
// {
description = "Nix config atom (null, bool, int, float, str, path or package)";
};
in
attrsOf (either confAtom (listOf confAtom));
@ -93,17 +102,28 @@ let
assert isNixAtLeast "2.2";
let
mkValueString = v:
if v == null then ""
else if isInt v then toString v
else if isBool v then boolToString v
else if isFloat v then floatToString v
else if isList v then toString v
else if isDerivation v then toString v
else if builtins.isPath v then toString v
else if isString v then v
else if strings.isConvertibleWithToString v then toString v
else abort "The nix conf value: ${toPretty {} v} can not be encoded";
mkValueString =
v:
if v == null then
""
else if isInt v then
toString v
else if isBool v then
boolToString v
else if isFloat v then
floatToString v
else if isList v then
toString v
else if isDerivation v then
toString v
else if builtins.isPath v then
toString v
else if isString v then
v
else if strings.isConvertibleWithToString v then
toString v
else
abort "The nix conf value: ${toPretty { } v} can not be encoded";
mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}";
@ -125,41 +145,71 @@ let
${cfg.extraOptions}
'';
checkPhase = lib.optionalString cfg.checkConfig (
if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
echo "Ignoring validation for cross-compilation"
''
if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then
''
echo "Ignoring validation for cross-compilation"
''
else
let
showCommand = if isNixAtLeast "2.20pre" then "config show" else "show-config";
in
''
echo "Validating generated nix.conf"
ln -s $out ./nix.conf
set -e
set +o pipefail
NIX_CONF_DIR=$PWD \
${cfg.package}/bin/nix ${showCommand} ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
${optionalString (isNixAtLeast "2.4pre") "--option experimental-features nix-command"} \
|& sed -e 's/^warning:/error:/' \
| (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
set -o pipefail
'');
let
showCommand = if isNixAtLeast "2.20pre" then "config show" else "show-config";
in
''
echo "Validating generated nix.conf"
ln -s $out ./nix.conf
set -e
set +o pipefail
NIX_CONF_DIR=$PWD \
${cfg.package}/bin/nix ${showCommand} ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
${optionalString (isNixAtLeast "2.4pre") "--option experimental-features nix-command"} \
|& sed -e 's/^warning:/error:/' \
| (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
set -o pipefail
''
);
};
in
{
imports = [
(mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "useChroot" ]; to = [ "nix" "useSandbox" ]; })
(mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "chrootDirs" ]; to = [ "nix" "sandboxPaths" ]; })
] ++
mapAttrsToList
(oldConf: newConf:
mkRenamedOptionModuleWith {
sinceRelease = 2205;
from = [ "nix" oldConf ];
to = [ "nix" "settings" newConf ];
imports =
[
(mkRenamedOptionModuleWith {
sinceRelease = 2003;
from = [
"nix"
"useChroot"
];
to = [
"nix"
"useSandbox"
];
})
legacyConfMappings;
(mkRenamedOptionModuleWith {
sinceRelease = 2003;
from = [
"nix"
"chrootDirs"
];
to = [
"nix"
"sandboxPaths"
];
})
]
++ mapAttrsToList (
oldConf: newConf:
mkRenamedOptionModuleWith {
sinceRelease = 2205;
from = [
"nix"
oldConf
];
to = [
"nix"
"settings"
newConf
];
}
) legacyConfMappings;
options = {
nix = {
@ -258,7 +308,10 @@ in
extra-sandbox-paths = mkOption {
type = types.listOf types.str;
default = [ ];
example = [ "/dev" "/proc" ];
example = [
"/dev"
"/proc"
];
description = ''
Directories from the host filesystem to be included
in the sandbox.
@ -314,7 +367,11 @@ in
trusted-users = mkOption {
type = types.listOf types.str;
example = [ "root" "alice" "@wheel" ];
example = [
"root"
"alice"
"@wheel"
];
description = ''
A list of names of users that have additional rights when
connecting to the Nix daemon, such as the ability to specify
@ -342,7 +399,12 @@ in
allowed-users = mkOption {
type = types.listOf types.str;
default = [ "*" ];
example = [ "@wheel" "@builders" "alice" "bob" ];
example = [
"@wheel"
"@builders"
"alice"
"bob"
];
description = ''
A list of names of users (separated by whitespace) that are
allowed to connect to the Nix daemon. As with

View File

@ -1,27 +1,35 @@
# This module defines a global environment configuration and
# a common configuration for all shells.
{ config, lib, utils, pkgs, ... }:
{
config,
lib,
utils,
pkgs,
...
}:
let
cfg = config.environment;
exportedEnvVars =
let
absoluteVariables =
lib.mapAttrs (n: lib.toList) cfg.variables;
absoluteVariables = lib.mapAttrs (n: lib.toList) cfg.variables;
suffixedVariables =
lib.flip lib.mapAttrs cfg.profileRelativeEnvVars (envVar: listSuffixes:
lib.concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
);
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeEnvVars (
envVar: listSuffixes:
lib.concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
);
allVariables =
lib.zipAttrsWith (n: lib.concatLists) [ absoluteVariables suffixedVariables ];
allVariables = lib.zipAttrsWith (n: lib.concatLists) [
absoluteVariables
suffixedVariables
];
exportVariables =
lib.mapAttrsToList (n: v: ''export ${n}="${lib.concatStringsSep ":" v}"'') allVariables;
exportVariables = lib.mapAttrsToList (
n: v: ''export ${n}="${lib.concatStringsSep ":" v}"''
) allVariables;
in
lib.concatStringsSep "\n" exportVariables;
lib.concatStringsSep "\n" exportVariables;
in
{
@ -29,8 +37,11 @@ in
options = {
environment.variables = lib.mkOption {
default = {};
example = { EDITOR = "nvim"; VISUAL = "nvim"; };
default = { };
example = {
EDITOR = "nvim";
VISUAL = "nvim";
};
description = ''
A set of environment variables used in the global environment.
These variables will be set on shell initialisation (e.g. in /etc/profile).
@ -42,14 +53,32 @@ in
Setting a variable to `null` does nothing. You can override a
variable set by another module to `null` to unset it.
'';
type = with lib.types; attrsOf (nullOr (oneOf [ (listOf (oneOf [ int str path ])) int str path ]));
apply = let
toStr = v: if lib.isPath v then "${v}" else toString v;
in attrs: lib.mapAttrs (n: v: if lib.isList v then lib.concatMapStringsSep ":" toStr v else toStr v) (lib.filterAttrs (n: v: v != null) attrs);
type =
with lib.types;
attrsOf (
nullOr (oneOf [
(listOf (oneOf [
int
str
path
]))
int
str
path
])
);
apply =
let
toStr = v: if lib.isPath v then "${v}" else toString v;
in
attrs:
lib.mapAttrs (n: v: if lib.isList v then lib.concatMapStringsSep ":" toStr v else toStr v) (
lib.filterAttrs (n: v: v != null) attrs
);
};
environment.profiles = lib.mkOption {
default = [];
default = [ ];
description = ''
A list of profiles used to setup the global environment.
'';
@ -58,7 +87,13 @@ in
environment.profileRelativeEnvVars = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
example = {
PATH = [ "/bin" ];
MANPATH = [
"/man"
"/share/man"
];
};
description = ''
Attribute set of environment variable. Each attribute maps to a list
of relative paths. Each relative path is appended to the each profile
@ -110,7 +145,10 @@ in
};
environment.shellAliases = lib.mkOption {
example = { l = null; ll = "ls -l"; };
example = {
l = null;
ll = "ls -l";
};
description = ''
An attribute set that maps aliases (the top level attribute names in
this option) to command strings or directly to build outputs. The
@ -151,7 +189,7 @@ in
};
environment.shells = lib.mkOption {
default = [];
default = [ ];
example = lib.literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
description = ''
A list of permissible login shells for user accounts.
@ -178,49 +216,46 @@ in
environment.shellAliases = lib.mapAttrs (name: lib.mkDefault) {
ls = "ls --color=tty";
ll = "ls -l";
l = "ls -alh";
l = "ls -alh";
};
environment.etc.shells.text =
''
${lib.concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
/bin/sh
'';
environment.etc.shells.text = ''
${lib.concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
/bin/sh
'';
# For resetting environment with `. /etc/set-environment` when needed
# and discoverability (see motivation of #30418).
environment.etc.set-environment.source = config.system.build.setEnvironment;
system.build.setEnvironment = pkgs.writeText "set-environment"
''
# DO NOT EDIT -- this file has been generated automatically.
system.build.setEnvironment = pkgs.writeText "set-environment" ''
# DO NOT EDIT -- this file has been generated automatically.
# Prevent this file from being sourced by child shells.
export __NIXOS_SET_ENVIRONMENT_DONE=1
# Prevent this file from being sourced by child shells.
export __NIXOS_SET_ENVIRONMENT_DONE=1
${exportedEnvVars}
${exportedEnvVars}
${cfg.extraInit}
${cfg.extraInit}
${lib.optionalString cfg.homeBinInPath ''
# ~/bin if it exists overrides other bin directories.
export PATH="$HOME/bin:$PATH"
''}
${lib.optionalString cfg.homeBinInPath ''
# ~/bin if it exists overrides other bin directories.
export PATH="$HOME/bin:$PATH"
''}
${lib.optionalString cfg.localBinInPath ''
export PATH="$HOME/.local/bin:$PATH"
''}
'';
${lib.optionalString cfg.localBinInPath ''
export PATH="$HOME/.local/bin:$PATH"
''}
'';
system.activationScripts.binsh = lib.stringAfter [ "stdio" ]
''
# Create the required /bin/sh symlink; otherwise lots of things
# (notably the system() function) won't work.
mkdir -p /bin
chmod 0755 /bin
ln -sfn "${cfg.binsh}" /bin/.sh.tmp
mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
'';
system.activationScripts.binsh = lib.stringAfter [ "stdio" ] ''
# Create the required /bin/sh symlink; otherwise lots of things
# (notably the system() function) won't work.
mkdir -p /bin
chmod 0755 /bin
ln -sfn "${cfg.binsh}" /bin/.sh.tmp
mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
'';
};

View File

@ -1,196 +1,212 @@
{ config, lib, pkgs, utils, ... }:
{
config,
lib,
pkgs,
utils,
...
}:
let
inherit (lib) mkIf mkOption types;
randomEncryptionCoerce = enable: { inherit enable; };
randomEncryptionOpts = { ... }: {
randomEncryptionOpts =
{ ... }:
{
options = {
options = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Encrypt swap device with a random key. This way you won't have a persistent swap device.
enable = mkOption {
default = false;
type = types.bool;
description = ''
Encrypt swap device with a random key. This way you won't have a persistent swap device.
WARNING: Don't try to hibernate when you have at least one swap partition with
this option enabled! We have no way to set the partition into which hibernation image
is saved, so if your image ends up on an encrypted one you would lose it!
WARNING: Don't try to hibernate when you have at least one swap partition with
this option enabled! We have no way to set the partition into which hibernation image
is saved, so if your image ends up on an encrypted one you would lose it!
WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
when using randomEncryption as the UUIDs and labels will get erased on every boot when
the partition is encrypted. Best to use /dev/disk/by-partuuid/
'';
};
cipher = mkOption {
default = "aes-xts-plain64";
example = "serpent-xts-plain64";
type = types.str;
description = ''
Use specified cipher for randomEncryption.
Hint: Run "cryptsetup benchmark" to see which one is fastest on your machine.
'';
};
keySize = mkOption {
default = null;
example = "512";
type = types.nullOr types.int;
description = ''
Set the encryption key size for the plain device.
If not specified, the amount of data to read from `source` will be
determined by cryptsetup.
See {manpage}`cryptsetup-open(8)` for details.
'';
};
sectorSize = mkOption {
default = null;
example = "4096";
type = types.nullOr types.int;
description = ''
Set the sector size for the plain encrypted device type.
If not specified, the default sector size is determined from the
underlying block device.
See {manpage}`cryptsetup-open(8)` for details.
'';
};
source = mkOption {
default = "/dev/urandom";
example = "/dev/random";
type = types.str;
description = ''
Define the source of randomness to obtain a random key for encryption.
'';
};
allowDiscards = mkOption {
default = false;
type = types.bool;
description = ''
Whether to allow TRIM requests to the underlying device. This option
has security implications; please read the LUKS documentation before
activating it.
'';
};
};
};
swapCfg = {config, options, ...}: {
options = {
device = mkOption {
example = "/dev/sda3";
type = types.nonEmptyStr;
description = "Path of the device or swap file.";
};
label = mkOption {
example = "swap";
type = types.str;
description = ''
Label of the device. Can be used instead of {var}`device`.
'';
};
size = mkOption {
default = null;
example = 2048;
type = types.nullOr types.int;
description = ''
If this option is set, device is interpreted as the
path of a swapfile that will be created automatically
with the indicated size (in megabytes).
'';
};
priority = mkOption {
default = null;
example = 2048;
type = types.nullOr types.int;
description = ''
Specify the priority of the swap device. Priority is a value between 0 and 32767.
Higher numbers indicate higher priority.
null lets the kernel choose a priority, which will show up as a negative value.
'';
};
randomEncryption = mkOption {
default = false;
example = {
enable = true;
cipher = "serpent-xts-plain64";
source = "/dev/random";
WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
when using randomEncryption as the UUIDs and labels will get erased on every boot when
the partition is encrypted. Best to use /dev/disk/by-partuuid/
'';
};
type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts);
description = ''
Encrypt swap device with a random key. This way you won't have a persistent swap device.
HINT: run "cryptsetup benchmark" to test cipher performance on your machine.
cipher = mkOption {
default = "aes-xts-plain64";
example = "serpent-xts-plain64";
type = types.str;
description = ''
Use specified cipher for randomEncryption.
WARNING: Don't try to hibernate when you have at least one swap partition with
this option enabled! We have no way to set the partition into which hibernation image
is saved, so if your image ends up on an encrypted one you would lose it!
Hint: Run "cryptsetup benchmark" to see which one is fastest on your machine.
'';
};
WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
when using randomEncryption as the UUIDs and labels will get erased on every boot when
the partition is encrypted. Best to use /dev/disk/by-partuuid/
'';
};
keySize = mkOption {
default = null;
example = "512";
type = types.nullOr types.int;
description = ''
Set the encryption key size for the plain device.
discardPolicy = mkOption {
default = null;
example = "once";
type = types.nullOr (types.enum ["once" "pages" "both" ]);
description = ''
Specify the discard policy for the swap device. If "once", then the
whole swap space is discarded at swapon invocation. If "pages",
asynchronous discard on freed pages is performed, before returning to
the available pages pool. With "both", both policies are activated.
See {manpage}`swapon(8)` for more information.
'';
};
If not specified, the amount of data to read from `source` will be
determined by cryptsetup.
options = mkOption {
default = [ "defaults" ];
example = [ "nofail" ];
type = types.listOf types.nonEmptyStr;
description = ''
Options used to mount the swap.
'';
};
See {manpage}`cryptsetup-open(8)` for details.
'';
};
deviceName = mkOption {
type = types.str;
internal = true;
};
sectorSize = mkOption {
default = null;
example = "4096";
type = types.nullOr types.int;
description = ''
Set the sector size for the plain encrypted device type.
realDevice = mkOption {
type = types.path;
internal = true;
If not specified, the default sector size is determined from the
underlying block device.
See {manpage}`cryptsetup-open(8)` for details.
'';
};
source = mkOption {
default = "/dev/urandom";
example = "/dev/random";
type = types.str;
description = ''
Define the source of randomness to obtain a random key for encryption.
'';
};
allowDiscards = mkOption {
default = false;
type = types.bool;
description = ''
Whether to allow TRIM requests to the underlying device. This option
has security implications; please read the LUKS documentation before
activating it.
'';
};
};
};
config = {
device = mkIf options.label.isDefined
"/dev/disk/by-label/${config.label}";
deviceName = lib.replaceStrings ["\\"] [""] (utils.escapeSystemdPath config.device);
realDevice = if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device;
};
swapCfg =
{ config, options, ... }:
{
};
options = {
device = mkOption {
example = "/dev/sda3";
type = types.nonEmptyStr;
description = "Path of the device or swap file.";
};
label = mkOption {
example = "swap";
type = types.str;
description = ''
Label of the device. Can be used instead of {var}`device`.
'';
};
size = mkOption {
default = null;
example = 2048;
type = types.nullOr types.int;
description = ''
If this option is set, device is interpreted as the
path of a swapfile that will be created automatically
with the indicated size (in megabytes).
'';
};
priority = mkOption {
default = null;
example = 2048;
type = types.nullOr types.int;
description = ''
Specify the priority of the swap device. Priority is a value between 0 and 32767.
Higher numbers indicate higher priority.
null lets the kernel choose a priority, which will show up as a negative value.
'';
};
randomEncryption = mkOption {
default = false;
example = {
enable = true;
cipher = "serpent-xts-plain64";
source = "/dev/random";
};
type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts);
description = ''
Encrypt swap device with a random key. This way you won't have a persistent swap device.
HINT: run "cryptsetup benchmark" to test cipher performance on your machine.
WARNING: Don't try to hibernate when you have at least one swap partition with
this option enabled! We have no way to set the partition into which hibernation image
is saved, so if your image ends up on an encrypted one you would lose it!
WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
when using randomEncryption as the UUIDs and labels will get erased on every boot when
the partition is encrypted. Best to use /dev/disk/by-partuuid/
'';
};
discardPolicy = mkOption {
default = null;
example = "once";
type = types.nullOr (
types.enum [
"once"
"pages"
"both"
]
);
description = ''
Specify the discard policy for the swap device. If "once", then the
whole swap space is discarded at swapon invocation. If "pages",
asynchronous discard on freed pages is performed, before returning to
the available pages pool. With "both", both policies are activated.
See {manpage}`swapon(8)` for more information.
'';
};
options = mkOption {
default = [ "defaults" ];
example = [ "nofail" ];
type = types.listOf types.nonEmptyStr;
description = ''
Options used to mount the swap.
'';
};
deviceName = mkOption {
type = types.str;
internal = true;
};
realDevice = mkOption {
type = types.path;
internal = true;
};
};
config = {
device = mkIf options.label.isDefined "/dev/disk/by-label/${config.label}";
deviceName = lib.replaceStrings [ "\\" ] [ "" ] (utils.escapeSystemdPath config.device);
realDevice =
if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device;
};
};
in
@ -201,7 +217,7 @@ in
options = {
swapDevices = mkOption {
default = [];
default = [ ];
example = [
{ device = "/dev/hda7"; }
{ device = "/var/swapfile"; }
@ -224,7 +240,8 @@ in
config = mkIf ((lib.length config.swapDevices) != 0) {
assertions = lib.map (sw: {
assertion = sw.randomEncryption.enable -> builtins.match "/dev/disk/by-(uuid|label)/.*" sw.device == null;
assertion =
sw.randomEncryption.enable -> builtins.match "/dev/disk/by-(uuid|label)/.*" sw.device == null;
message = ''
You cannot use swap device "${sw.device}" with randomEncryption enabled.
The UUIDs and labels will get erased on every boot when the partition is encrypted.
@ -232,12 +249,13 @@ in
'';
}) config.swapDevices;
warnings =
lib.concatMap (sw:
if sw.size != null && lib.hasPrefix "/dev/" sw.device
then [ "Setting the swap size of block device ${sw.device} has no effect" ]
else [ ])
config.swapDevices;
warnings = lib.concatMap (
sw:
if sw.size != null && lib.hasPrefix "/dev/" sw.device then
[ "Setting the swap size of block device ${sw.device} has no effect" ]
else
[ ]
) config.swapDevices;
system.requiredKernelConfig = [
(config.lib.kernelConfig.isYes "SWAP")
@ -246,47 +264,62 @@ in
# Create missing swapfiles.
systemd.services =
let
createSwapDevice = sw:
let realDevice' = utils.escapeSystemdPath sw.realDevice;
in lib.nameValuePair "mkswap-${sw.deviceName}"
{ description = "Initialisation of swap device ${sw.device}";
createSwapDevice =
sw:
let
realDevice' = utils.escapeSystemdPath sw.realDevice;
in
lib.nameValuePair "mkswap-${sw.deviceName}" {
description = "Initialisation of swap device ${sw.device}";
# The mkswap service fails for file-backed swap devices if the
# loop module has not been loaded before the service runs.
# We add an ordering constraint to run after systemd-modules-load to
# avoid this race condition.
after = [ "systemd-modules-load.service" ];
wantedBy = [ "${realDevice'}.swap" ];
before = [ "${realDevice'}.swap" "shutdown.target"];
before = [
"${realDevice'}.swap"
"shutdown.target"
];
conflicts = [ "shutdown.target" ];
path = [ pkgs.util-linux pkgs.e2fsprogs ]
++ lib.optional sw.randomEncryption.enable pkgs.cryptsetup;
path = [
pkgs.util-linux
pkgs.e2fsprogs
] ++ lib.optional sw.randomEncryption.enable pkgs.cryptsetup;
environment.DEVICE = sw.device;
script =
''
${lib.optionalString (sw.size != null) ''
currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 ))
if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then
# Disable CoW for CoW based filesystems like BTRFS.
truncate --size 0 "$DEVICE"
chattr +C "$DEVICE" 2>/dev/null || true
script = ''
${lib.optionalString (sw.size != null) ''
currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 ))
if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then
# Disable CoW for CoW based filesystems like BTRFS.
truncate --size 0 "$DEVICE"
chattr +C "$DEVICE" 2>/dev/null || true
echo "Creating swap file using dd and mkswap."
dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size} status=progress
${lib.optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
fi
''}
${lib.optionalString sw.randomEncryption.enable ''
cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \
${lib.concatStringsSep " \\\n" (lib.flatten [
(lib.optional (sw.randomEncryption.sectorSize != null) "--sector-size=${toString sw.randomEncryption.sectorSize}")
(lib.optional (sw.randomEncryption.keySize != null) "--key-size=${toString sw.randomEncryption.keySize}")
(lib.optional sw.randomEncryption.allowDiscards "--allow-discards")
])} ${sw.device} ${sw.deviceName}
mkswap ${sw.realDevice}
''}
'';
echo "Creating swap file using dd and mkswap."
dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size} status=progress
${lib.optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
fi
''}
${lib.optionalString sw.randomEncryption.enable ''
cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \
${
lib.concatStringsSep " \\\n" (
lib.flatten [
(lib.optional (
sw.randomEncryption.sectorSize != null
) "--sector-size=${toString sw.randomEncryption.sectorSize}")
(lib.optional (
sw.randomEncryption.keySize != null
) "--key-size=${toString sw.randomEncryption.keySize}")
(lib.optional sw.randomEncryption.allowDiscards "--allow-discards")
]
)
} ${sw.device} ${sw.deviceName}
mkswap ${sw.realDevice}
''}
'';
unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
unitConfig.DefaultDependencies = false; # needed to prevent a cycle
@ -299,7 +332,12 @@ in
restartIfChanged = false;
};
in lib.listToAttrs (lib.map createSwapDevice (lib.filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices));
in
lib.listToAttrs (
lib.map createSwapDevice (
lib.filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices
)
);
};

View File

@ -1,6 +1,12 @@
# This module defines a system-wide environment that will be
# initialised by pam_env (that is, not only in shells).
{ config, lib, options, pkgs, ... }:
{
config,
lib,
options,
pkgs,
...
}:
let
cfg = config.environment;
@ -12,7 +18,7 @@ in
options = {
environment.sessionVariables = lib.mkOption {
default = {};
default = { };
description = ''
A set of environment variables used in the global environment.
These variables will be set by PAM early in the login process.
@ -37,7 +43,13 @@ in
environment.profileRelativeSessionVariables = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
example = {
PATH = [ "/bin" ];
MANPATH = [
"/man"
"/share/man"
];
};
description = ''
Attribute set of environment variable used in the global
environment. These variables will be set by PAM early in the
@ -61,40 +73,40 @@ in
};
config = {
environment.etc."pam/environment".text = let
suffixedVariables =
lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (envVar: suffixes:
lib.flip lib.concatMap cfg.profiles (profile:
map (suffix: "${profile}${suffix}") suffixes
)
environment.etc."pam/environment".text =
let
suffixedVariables = lib.flip lib.mapAttrs cfg.profileRelativeSessionVariables (
envVar: suffixes:
lib.flip lib.concatMap cfg.profiles (profile: map (suffix: "${profile}${suffix}") suffixes)
);
# We're trying to use the same syntax for PAM variables and env variables.
# That means we need to map the env variables that people might use to their
# equivalent PAM variable.
replaceEnvVars = lib.replaceStrings ["$HOME" "$USER"] ["@{HOME}" "@{PAM_USER}"];
# We're trying to use the same syntax for PAM variables and env variables.
# That means we need to map the env variables that people might use to their
# equivalent PAM variable.
replaceEnvVars = lib.replaceStrings [ "$HOME" "$USER" ] [ "@{HOME}" "@{PAM_USER}" ];
pamVariable = n: v:
''${n} DEFAULT="${lib.concatStringsSep ":" (map replaceEnvVars (lib.toList v))}"'';
pamVariable =
n: v: ''${n} DEFAULT="${lib.concatStringsSep ":" (map replaceEnvVars (lib.toList v))}"'';
pamVariables =
lib.concatStringsSep "\n"
(lib.mapAttrsToList pamVariable
(lib.zipAttrsWith (n: lib.concatLists)
[
# Make sure security wrappers are prioritized without polluting
# shell environments with an extra entry. Sessions which depend on
# pam for its environment will otherwise have eg. broken sudo. In
# particular Gnome Shell sometimes fails to source a proper
# environment from a shell.
{ PATH = [ config.security.wrapperDir ]; }
pamVariables = lib.concatStringsSep "\n" (
lib.mapAttrsToList pamVariable (
lib.zipAttrsWith (n: lib.concatLists) [
# Make sure security wrappers are prioritized without polluting
# shell environments with an extra entry. Sessions which depend on
# pam for its environment will otherwise have eg. broken sudo. In
# particular Gnome Shell sometimes fails to source a proper
# environment from a shell.
{ PATH = [ config.security.wrapperDir ]; }
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
suffixedVariables
]));
in ''
${pamVariables}
'';
(lib.mapAttrs (n: lib.toList) cfg.sessionVariables)
suffixedVariables
]
)
);
in
''
${pamVariables}
'';
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,14 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.xdg.mime;
associationOptions = with lib.types; attrsOf (
coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str
);
associationOptions =
with lib.types;
attrsOf (coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str);
in
{
@ -24,10 +29,13 @@ in
xdg.mime.addedAssociations = lib.mkOption {
type = associationOptions;
default = {};
default = { };
example = {
"application/pdf" = "firefox.desktop";
"text/xml" = [ "nvim.desktop" "codium.desktop" ];
"text/xml" = [
"nvim.desktop"
"codium.desktop"
];
};
description = ''
Adds associations between mimetypes and applications. See the
@ -38,10 +46,13 @@ in
xdg.mime.defaultApplications = lib.mkOption {
type = associationOptions;
default = {};
default = { };
example = {
"application/pdf" = "firefox.desktop";
"image/png" = [ "sxiv.desktop" "gimp.desktop" ];
"image/png" = [
"sxiv.desktop"
"gimp.desktop"
];
};
description = ''
Sets the default applications for given mimetypes. See the
@ -52,9 +63,12 @@ in
xdg.mime.removedAssociations = lib.mkOption {
type = associationOptions;
default = {};
default = { };
example = {
"audio/mp3" = [ "mpv.desktop" "umpv.desktop" ];
"audio/mp3" = [
"mpv.desktop"
"umpv.desktop"
];
"inode/directory" = "codium.desktop";
};
description = ''
@ -66,17 +80,16 @@ in
};
config = lib.mkIf cfg.enable {
environment.etc."xdg/mimeapps.list" = lib.mkIf (
cfg.addedAssociations != {}
|| cfg.defaultApplications != {}
|| cfg.removedAssociations != {}
) {
text = lib.generators.toINI { } {
"Added Associations" = cfg.addedAssociations;
"Default Applications" = cfg.defaultApplications;
"Removed Associations" = cfg.removedAssociations;
};
};
environment.etc."xdg/mimeapps.list" =
lib.mkIf
(cfg.addedAssociations != { } || cfg.defaultApplications != { } || cfg.removedAssociations != { })
{
text = lib.generators.toINI { } {
"Added Associations" = cfg.addedAssociations;
"Default Applications" = cfg.defaultApplications;
"Removed Associations" = cfg.removedAssociations;
};
};
environment.pathsToLink = [ "/share/mime" ];

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
@ -10,7 +15,10 @@ in
{
imports = [
(lib.mkRemovedOptionModule [ "zramSwap" "numDevices" ] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported")
(lib.mkRemovedOptionModule [
"zramSwap"
"numDevices"
] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported")
];
###### interface
@ -73,7 +81,16 @@ in
algorithm = lib.mkOption {
default = "zstd";
example = "lz4";
type = with lib.types; either (enum [ "842" "lzo" "lzo-rle" "lz4" "lz4hc" "zstd" ]) str;
type =
with lib.types;
either (enum [
"842"
"lzo"
"lzo-rle"
"lz4"
"lz4hc"
"zstd"
]) str;
description = ''
Compression algorithm. `lzo` has good compression,
but is slow. `lz4` has bad compression, but is fast.
@ -107,23 +124,24 @@ in
services.zram-generator.enable = true;
services.zram-generator.settings = lib.listToAttrs
(builtins.map
(dev: {
name = dev;
value =
let
size = "${toString cfg.memoryPercent} / 100 * ram";
in
{
zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
compression-algorithm = cfg.algorithm;
swap-priority = cfg.priority;
} // lib.optionalAttrs (cfg.writebackDevice != null) {
writeback-device = cfg.writebackDevice;
};
})
devices);
services.zram-generator.settings = lib.listToAttrs (
builtins.map (dev: {
name = dev;
value =
let
size = "${toString cfg.memoryPercent} / 100 * ram";
in
{
zram-size =
if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
compression-algorithm = cfg.algorithm;
swap-priority = cfg.priority;
}
// lib.optionalAttrs (cfg.writebackDevice != null) {
writeback-device = cfg.writebackDevice;
};
}) devices
);
};

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.deviceTree;
@ -62,10 +67,12 @@ let
};
};
filterDTBs = src: if cfg.filter == null
then src
filterDTBs =
src:
if cfg.filter == null then
src
else
pkgs.runCommand "dtbs-filtered" {} ''
pkgs.runCommand "dtbs-filtered" { } ''
mkdir -p $out
cd ${src}
find . -type f -name '${cfg.filter}' -print0 \
@ -76,148 +83,169 @@ let
# Fill in `dtboFile` for each overlay if not set already.
# Existence of one of these is guarded by assertion below
withDTBOs = xs: lib.flip map xs (o: o // { dtboFile =
let
includePaths = ["${lib.getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes"] ++ cfg.dtboBuildExtraIncludePaths;
extraPreprocessorFlags = cfg.dtboBuildExtraPreprocessorFlags;
in
if o.dtboFile == null then
let
dtsFile = if o.dtsFile == null then (pkgs.writeText "dts" o.dtsText) else o.dtsFile;
in
pkgs.deviceTree.compileDTS {
name = "${o.name}-dtbo";
inherit includePaths extraPreprocessorFlags dtsFile;
withDTBOs =
xs:
lib.flip map xs (
o:
o
// {
dtboFile =
let
includePaths = [
"${lib.getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes"
] ++ cfg.dtboBuildExtraIncludePaths;
extraPreprocessorFlags = cfg.dtboBuildExtraPreprocessorFlags;
in
if o.dtboFile == null then
let
dtsFile = if o.dtsFile == null then (pkgs.writeText "dts" o.dtsText) else o.dtsFile;
in
pkgs.deviceTree.compileDTS {
name = "${o.name}-dtbo";
inherit includePaths extraPreprocessorFlags dtsFile;
}
else
o.dtboFile;
}
else o.dtboFile; } );
);
in
{
imports = [
(lib.mkRemovedOptionModule [ "hardware" "deviceTree" "base" ] "Use hardware.deviceTree.kernelPackage instead")
(lib.mkRemovedOptionModule [
"hardware"
"deviceTree"
"base"
] "Use hardware.deviceTree.kernelPackage instead")
];
options = {
hardware.deviceTree = {
enable = lib.mkOption {
default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
type = lib.types.bool;
description = ''
Build device tree files. These are used to describe the
non-discoverable hardware of a system.
'';
};
hardware.deviceTree = {
enable = lib.mkOption {
default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
type = lib.types.bool;
description = ''
Build device tree files. These are used to describe the
non-discoverable hardware of a system.
'';
};
kernelPackage = lib.mkOption {
default = config.boot.kernelPackages.kernel;
defaultText = lib.literalExpression "config.boot.kernelPackages.kernel";
example = lib.literalExpression "pkgs.linux_latest";
type = lib.types.path;
description = ''
Kernel package where device tree include directory is from. Also used as default source of dtb package to apply overlays to
'';
};
kernelPackage = lib.mkOption {
default = config.boot.kernelPackages.kernel;
defaultText = lib.literalExpression "config.boot.kernelPackages.kernel";
example = lib.literalExpression "pkgs.linux_latest";
type = lib.types.path;
description = ''
Kernel package where device tree include directory is from. Also used as default source of dtb package to apply overlays to
'';
};
dtboBuildExtraPreprocessorFlags = lib.mkOption {
default = [];
example = lib.literalExpression "[ \"-DMY_DTB_DEFINE\" ]";
type = lib.types.listOf lib.types.str;
description = ''
Additional flags to pass to the preprocessor during dtbo compilations
'';
};
dtboBuildExtraPreprocessorFlags = lib.mkOption {
default = [ ];
example = lib.literalExpression "[ \"-DMY_DTB_DEFINE\" ]";
type = lib.types.listOf lib.types.str;
description = ''
Additional flags to pass to the preprocessor during dtbo compilations
'';
};
dtboBuildExtraIncludePaths = lib.mkOption {
default = [];
example = lib.literalExpression ''
[
./my_custom_include_dir_1
./custom_include_dir_2
]
'';
type = lib.types.listOf lib.types.path;
description = ''
Additional include paths that will be passed to the preprocessor when creating the final .dts to compile into .dtbo
'';
};
dtboBuildExtraIncludePaths = lib.mkOption {
default = [ ];
example = lib.literalExpression ''
[
./my_custom_include_dir_1
./custom_include_dir_2
]
'';
type = lib.types.listOf lib.types.path;
description = ''
Additional include paths that will be passed to the preprocessor when creating the final .dts to compile into .dtbo
'';
};
dtbSource = lib.mkOption {
default = "${cfg.kernelPackage}/dtbs";
defaultText = lib.literalExpression "\${cfg.kernelPackage}/dtbs";
type = lib.types.path;
description = ''
Path to dtb directory that overlays and other processing will be applied to. Uses
device trees bundled with the Linux kernel by default.
'';
};
dtbSource = lib.mkOption {
default = "${cfg.kernelPackage}/dtbs";
defaultText = lib.literalExpression "\${cfg.kernelPackage}/dtbs";
type = lib.types.path;
description = ''
Path to dtb directory that overlays and other processing will be applied to. Uses
device trees bundled with the Linux kernel by default.
'';
};
name = lib.mkOption {
default = null;
example = "some-dtb.dtb";
type = lib.types.nullOr lib.types.str;
description = ''
The name of an explicit dtb to be loaded, relative to the dtb base.
Useful in extlinux scenarios if the bootloader doesn't pick the
right .dtb file from FDTDIR.
'';
};
name = lib.mkOption {
default = null;
example = "some-dtb.dtb";
type = lib.types.nullOr lib.types.str;
description = ''
The name of an explicit dtb to be loaded, relative to the dtb base.
Useful in extlinux scenarios if the bootloader doesn't pick the
right .dtb file from FDTDIR.
'';
};
filter = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "*rpi*.dtb";
description = ''
Only include .dtb files matching glob expression.
'';
};
filter = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "*rpi*.dtb";
description = ''
Only include .dtb files matching glob expression.
'';
};
overlays = lib.mkOption {
default = [];
example = lib.literalExpression ''
[
{ name = "pps"; dtsFile = ./dts/pps.dts; }
{ name = "spi";
dtsText = "...";
}
{ name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
]
'';
type = lib.types.listOf (lib.types.coercedTo lib.types.path (path: {
overlays = lib.mkOption {
default = [ ];
example = lib.literalExpression ''
[
{ name = "pps"; dtsFile = ./dts/pps.dts; }
{ name = "spi";
dtsText = "...";
}
{ name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
]
'';
type = lib.types.listOf (
lib.types.coercedTo lib.types.path (path: {
name = baseNameOf path;
filter = null;
dtboFile = path;
}) overlayType);
description = ''
List of overlays to apply to base device-tree (.dtb) files.
'';
};
package = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.path;
internal = true;
description = ''
A path containing the result of applying `overlays` to `kernelPackage`.
'';
};
}) overlayType
);
description = ''
List of overlays to apply to base device-tree (.dtb) files.
'';
};
package = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.path;
internal = true;
description = ''
A path containing the result of applying `overlays` to `kernelPackage`.
'';
};
};
};
config = lib.mkIf (cfg.enable) {
assertions = let
invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
in lib.singleton {
assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
message = ''
deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
Offending overlay(s):
${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
'';
};
assertions =
let
invalidOverlay = o: (o.dtsFile == null) && (o.dtsText == null) && (o.dtboFile == null);
in
lib.singleton {
assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
message = ''
deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
Offending overlay(s):
${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
'';
};
hardware.deviceTree.package = if (cfg.overlays != [])
then pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
else filteredDTBs;
hardware.deviceTree.package =
if (cfg.overlays != [ ]) then
pkgs.deviceTree.applyOverlays filteredDTBs (withDTBOs cfg.overlays)
else
filteredDTBs;
};
}

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.graphics;
@ -14,16 +19,35 @@ let
in
{
imports = [
(lib.mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "graphics" "extraPackages" ])
(lib.mkRemovedOptionModule [ "hardware" "opengl" "s3tcSupport" ] "S3TC support is now always enabled in Mesa.")
(lib.mkRemovedOptionModule [ "hardware" "opengl" "driSupport"] "The setting can be removed.")
(lib.mkRenamedOptionModule
[ "services" "xserver" "vaapiDrivers" ]
[ "hardware" "graphics" "extraPackages" ]
)
(lib.mkRemovedOptionModule [
"hardware"
"opengl"
"s3tcSupport"
] "S3TC support is now always enabled in Mesa.")
(lib.mkRemovedOptionModule [ "hardware" "opengl" "driSupport" ] "The setting can be removed.")
(lib.mkRenamedOptionModule [ "hardware" "opengl" "enable"] [ "hardware" "graphics" "enable" ])
(lib.mkRenamedOptionModule [ "hardware" "opengl" "driSupport32Bit"] [ "hardware" "graphics" "enable32Bit" ])
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package"] [ "hardware" "graphics" "package" ])
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package32"] [ "hardware" "graphics" "package32" ])
(lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages"] [ "hardware" "graphics" "extraPackages" ])
(lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages32"] [ "hardware" "graphics" "extraPackages32" ])
(lib.mkRenamedOptionModule [ "hardware" "opengl" "enable" ] [ "hardware" "graphics" "enable" ])
(lib.mkRenamedOptionModule
[ "hardware" "opengl" "driSupport32Bit" ]
[ "hardware" "graphics" "enable32Bit" ]
)
(lib.mkRenamedOptionModule [ "hardware" "opengl" "package" ] [ "hardware" "graphics" "package" ])
(lib.mkRenamedOptionModule
[ "hardware" "opengl" "package32" ]
[ "hardware" "graphics" "package32" ]
)
(lib.mkRenamedOptionModule
[ "hardware" "opengl" "extraPackages" ]
[ "hardware" "graphics" "extraPackages" ]
)
(lib.mkRenamedOptionModule
[ "hardware" "opengl" "extraPackages32" ]
[ "hardware" "graphics" "extraPackages32" ]
)
];
options.hardware.graphics = {
@ -78,7 +102,7 @@ in
:::
'';
type = lib.types.listOf lib.types.package;
default = [];
default = [ ];
example = lib.literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
};
@ -92,7 +116,7 @@ in
:::
'';
type = lib.types.listOf lib.types.package;
default = [];
default = [ ];
example = lib.literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
};
};
@ -117,7 +141,7 @@ in
else if cfg.enable32Bit then
{ "L+".argument = toString driversEnv32; }
else
{ "r" = {}; };
{ "r" = { }; };
};
hardware.graphics.package = lib.mkDefault pkgs.mesa;

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.hardware.steam-hardware;

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
{
options.hardware.wooting.enable = lib.mkEnableOption "support for Wooting keyboards";

View File

@ -1,12 +1,17 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
imcfg = config.i18n.inputMethod;
cfg = imcfg.ibus;
ibusPackage = pkgs.ibus-with-plugins.override { plugins = cfg.engines; };
ibusEngine = lib.types.mkOptionType {
name = "ibus-engine";
name = "ibus-engine";
inherit (lib.types.package) descriptionClass merge;
check = x: (lib.types.package.check x) && (lib.attrByPath ["meta" "isIbusEngine"] false x);
check = x: (lib.types.package.check x) && (lib.attrByPath [ "meta" "isIbusEngine" ] false x);
};
impanel = lib.optionalString (cfg.panel != null) "--panel=${cfg.panel}";
@ -26,21 +31,24 @@ let
in
{
imports = [
(lib.mkRenamedOptionModule [ "programs" "ibus" "plugins" ] [ "i18n" "inputMethod" "ibus" "engines" ])
(lib.mkRenamedOptionModule
[ "programs" "ibus" "plugins" ]
[ "i18n" "inputMethod" "ibus" "engines" ]
)
];
options = {
i18n.inputMethod.ibus = {
engines = lib.mkOption {
type = with lib.types; listOf ibusEngine;
default = [];
type = with lib.types; listOf ibusEngine;
default = [ ];
example = lib.literalExpression "with pkgs.ibus-engines; [ mozc hangul ]";
description =
let
enginesDrv = lib.filterAttrs (lib.const lib.isDerivation) pkgs.ibus-engines;
engines = lib.concatStringsSep ", "
(map (name: "`${name}`") (lib.attrNames enginesDrv));
in "Enabled IBus engines. Available engines are: ${engines}.";
engines = lib.concatStringsSep ", " (map (name: "`${name}`") (lib.attrNames enginesDrv));
in
"Enabled IBus engines. Available engines are: ${engines}.";
};
panel = lib.mkOption {
type = with lib.types; nullOr path;

View File

@ -1,82 +1,108 @@
# This is an expression meant to be called from `./repart.nix`, it is NOT a
# NixOS module that can be imported.
{ lib
, stdenvNoCC
, runCommand
, python3
, black
, ruff
, mypy
, systemd
, fakeroot
, util-linux
{
lib,
stdenvNoCC,
runCommand,
python3,
black,
ruff,
mypy,
systemd,
fakeroot,
util-linux,
# filesystem tools
, dosfstools
, mtools
, e2fsprogs
, squashfsTools
, erofs-utils
, btrfs-progs
, xfsprogs
dosfstools,
mtools,
e2fsprogs,
squashfsTools,
erofs-utils,
btrfs-progs,
xfsprogs,
# compression tools
, zstd
, xz
, zeekstd
zstd,
xz,
zeekstd,
# arguments
, name
, version
, imageFileBasename
, compression
, fileSystems
, finalPartitions
, split
, seed
, definitionsDirectory
, sectorSize
, mkfsEnv ? {}
, createEmpty ? true
name,
version,
imageFileBasename,
compression,
fileSystems,
finalPartitions,
split,
seed,
definitionsDirectory,
sectorSize,
mkfsEnv ? { },
createEmpty ? true,
}:
let
systemdArch = let
inherit (stdenvNoCC) hostPlatform;
in
if hostPlatform.isAarch32 then "arm"
else if hostPlatform.isAarch64 then "arm64"
else if hostPlatform.isx86_32 then "x86"
else if hostPlatform.isx86_64 then "x86-64"
else if hostPlatform.isMips32 then "mips-le"
else if hostPlatform.isMips64 then "mips64-le"
else if hostPlatform.isPower then "ppc"
else if hostPlatform.isPower64 then "ppc64"
else if hostPlatform.isRiscV32 then "riscv32"
else if hostPlatform.isRiscV64 then "riscv64"
else if hostPlatform.isS390 then "s390"
else if hostPlatform.isS390x then "s390x"
else if hostPlatform.isLoongArch64 then "loongarch64"
else if hostPlatform.isAlpha then "alpha"
else hostPlatform.parsed.cpu.name;
systemdArch =
let
inherit (stdenvNoCC) hostPlatform;
in
if hostPlatform.isAarch32 then
"arm"
else if hostPlatform.isAarch64 then
"arm64"
else if hostPlatform.isx86_32 then
"x86"
else if hostPlatform.isx86_64 then
"x86-64"
else if hostPlatform.isMips32 then
"mips-le"
else if hostPlatform.isMips64 then
"mips64-le"
else if hostPlatform.isPower then
"ppc"
else if hostPlatform.isPower64 then
"ppc64"
else if hostPlatform.isRiscV32 then
"riscv32"
else if hostPlatform.isRiscV64 then
"riscv64"
else if hostPlatform.isS390 then
"s390"
else if hostPlatform.isS390x then
"s390x"
else if hostPlatform.isLoongArch64 then
"loongarch64"
else if hostPlatform.isAlpha then
"alpha"
else
hostPlatform.parsed.cpu.name;
amendRepartDefinitions = runCommand "amend-repart-definitions.py"
{
# TODO: ruff does not splice properly in nativeBuildInputs
depsBuildBuild = [ ruff ];
nativeBuildInputs = [ python3 black mypy ];
} ''
install ${./amend-repart-definitions.py} $out
patchShebangs --build $out
amendRepartDefinitions =
runCommand "amend-repart-definitions.py"
{
# TODO: ruff does not splice properly in nativeBuildInputs
depsBuildBuild = [ ruff ];
nativeBuildInputs = [
python3
black
mypy
];
}
''
install ${./amend-repart-definitions.py} $out
patchShebangs --build $out
black --check --diff $out
ruff check --line-length 88 $out
mypy --strict $out
'';
black --check --diff $out
ruff check --line-length 88 $out
mypy --strict $out
'';
fileSystemToolMapping = {
"vfat" = [ dosfstools mtools ];
"vfat" = [
dosfstools
mtools
];
"ext4" = [ e2fsprogs.bin ];
"squashfs" = [ squashfsTools ];
"erofs" = [ erofs-utils ];
@ -87,108 +113,127 @@ let
fileSystemTools = builtins.concatMap (f: fileSystemToolMapping."${f}") fileSystems;
compressionPkg = {
"zstd" = zstd;
"xz" = xz;
"zstd-seekable" = zeekstd;
}."${compression.algorithm}";
compressionPkg =
{
"zstd" = zstd;
"xz" = xz;
"zstd-seekable" = zeekstd;
}
."${compression.algorithm}";
compressionCommand = {
"zstd" = "zstd --no-progress --threads=$NIX_BUILD_CORES -${toString compression.level}";
"xz" = "xz --keep --verbose --threads=$NIX_BUILD_CORES -${toString compression.level}";
"zstd-seekable" = "zeekstd --quiet --max-frame-size 2M --compression-level ${toString compression.level}";
}."${compression.algorithm}";
compressionCommand =
{
"zstd" = "zstd --no-progress --threads=$NIX_BUILD_CORES -${toString compression.level}";
"xz" = "xz --keep --verbose --threads=$NIX_BUILD_CORES -${toString compression.level}";
"zstd-seekable" =
"zeekstd --quiet --max-frame-size 2M --compression-level ${toString compression.level}";
}
."${compression.algorithm}";
in
stdenvNoCC.mkDerivation (finalAttrs:
(if (version != null)
then { pname = name; inherit version; }
else { inherit name; }
) // {
__structuredAttrs = true;
stdenvNoCC.mkDerivation (
finalAttrs:
(
if (version != null) then
{
pname = name;
inherit version;
}
else
{ inherit name; }
)
// {
__structuredAttrs = true;
# the image will be self-contained so we can drop references
# to the closure that was used to build it
unsafeDiscardReferences.out = true;
# the image will be self-contained so we can drop references
# to the closure that was used to build it
unsafeDiscardReferences.out = true;
nativeBuildInputs =
[
systemd
util-linux
fakeroot
]
++ lib.optionals (compression.enable) [
compressionPkg
]
++ fileSystemTools;
nativeBuildInputs = [
systemd
util-linux
fakeroot
] ++ lib.optionals (compression.enable) [
compressionPkg
] ++ fileSystemTools;
env = mkfsEnv;
env = mkfsEnv;
inherit finalPartitions definitionsDirectory;
inherit finalPartitions definitionsDirectory;
partitionsJSON = builtins.toJSON finalAttrs.finalPartitions;
partitionsJSON = builtins.toJSON finalAttrs.finalPartitions;
# relative path to the repart definitions that are read by systemd-repart
finalRepartDefinitions = "repart.d";
# relative path to the repart definitions that are read by systemd-repart
finalRepartDefinitions = "repart.d";
systemdRepartFlags =
[
"--architecture=${systemdArch}"
"--dry-run=no"
"--size=auto"
"--seed=${seed}"
"--definitions=${finalAttrs.finalRepartDefinitions}"
"--split=${lib.boolToString split}"
"--json=pretty"
]
++ lib.optionals createEmpty [
"--empty=create"
]
++ lib.optionals (sectorSize != null) [
"--sector-size=${toString sectorSize}"
];
systemdRepartFlags = [
"--architecture=${systemdArch}"
"--dry-run=no"
"--size=auto"
"--seed=${seed}"
"--definitions=${finalAttrs.finalRepartDefinitions}"
"--split=${lib.boolToString split}"
"--json=pretty"
] ++ lib.optionals createEmpty [
"--empty=create"
] ++ lib.optionals (sectorSize != null) [
"--sector-size=${toString sectorSize}"
];
dontUnpack = true;
dontConfigure = true;
doCheck = false;
dontUnpack = true;
dontConfigure = true;
doCheck = false;
patchPhase = ''
runHook prePatch
patchPhase = ''
runHook prePatch
amendedRepartDefinitionsDir=$(${amendRepartDefinitions} <(echo "$partitionsJSON") $definitionsDirectory)
ln -vs $amendedRepartDefinitionsDir $finalRepartDefinitions
amendedRepartDefinitionsDir=$(${amendRepartDefinitions} <(echo "$partitionsJSON") $definitionsDirectory)
ln -vs $amendedRepartDefinitionsDir $finalRepartDefinitions
runHook postPatch
'';
runHook postPatch
'';
buildPhase = ''
runHook preBuild
buildPhase = ''
runHook preBuild
echo "Building image with systemd-repart..."
unshare --map-root-user fakeroot systemd-repart \
''${systemdRepartFlags[@]} \
${imageFileBasename}.raw \
| tee repart-output.json
echo "Building image with systemd-repart..."
unshare --map-root-user fakeroot systemd-repart \
''${systemdRepartFlags[@]} \
${imageFileBasename}.raw \
| tee repart-output.json
runHook postBuild
'';
runHook postBuild
'';
installPhase =
''
runHook preInstall
installPhase = ''
runHook preInstall
mkdir -p $out
''
# Compression is implemented in the same derivation as opposed to in a
# separate derivation to allow users to save disk space. Disk images are
# already very space intensive so we want to allow users to mitigate this.
+ lib.optionalString compression.enable ''
for f in ${imageFileBasename}*; do
echo "Compressing $f with ${compression.algorithm}..."
# Keep the original file when compressing and only delete it afterwards
${compressionCommand} $f && rm $f
done
''
+ ''
mv -v repart-output.json ${imageFileBasename}* $out
mkdir -p $out
''
# Compression is implemented in the same derivation as opposed to in a
# separate derivation to allow users to save disk space. Disk images are
# already very space intensive so we want to allow users to mitigate this.
+ lib.optionalString compression.enable
''
for f in ${imageFileBasename}*; do
echo "Compressing $f with ${compression.algorithm}..."
# Keep the original file when compressing and only delete it afterwards
${compressionCommand} $f && rm $f
done
'' + ''
mv -v repart-output.json ${imageFileBasename}* $out
runHook postInstall
'';
runHook postInstall
'';
passthru = {
inherit amendRepartDefinitions;
};
})
passthru = {
inherit amendRepartDefinitions;
};
}
)

View File

@ -1,7 +1,13 @@
# This module exposes options to build a disk image with a GUID Partition Table
# (GPT). It uses systemd-repart to build the image.
{ config, pkgs, lib, utils, ... }:
{
config,
pkgs,
lib,
utils,
...
}:
let
cfg = config.image.repart;
@ -27,14 +33,16 @@ let
};
contents = lib.mkOption {
type = with lib.types; attrsOf (submodule {
options = {
source = lib.mkOption {
type = types.path;
description = "Path of the source file.";
type =
with lib.types;
attrsOf (submodule {
options = {
source = lib.mkOption {
type = types.path;
description = "Path of the source file.";
};
};
};
});
});
default = { };
example = lib.literalExpression ''
{
@ -48,7 +56,14 @@ let
};
repartConfig = lib.mkOption {
type = with lib.types; attrsOf (oneOf [ str int bool (listOf str) ]);
type =
with lib.types;
attrsOf (oneOf [
str
int
bool
(listOf str)
]);
example = {
Type = "home";
SizeMinBytes = "512M";
@ -63,10 +78,12 @@ let
};
};
mkfsOptionsToEnv = opts: lib.mapAttrs' (fsType: options: {
name = "SYSTEMD_REPART_MKFS_OPTIONS_${lib.toUpper fsType}";
value = builtins.concatStringsSep " " options;
}) opts;
mkfsOptionsToEnv =
opts:
lib.mapAttrs' (fsType: options: {
name = "SYSTEMD_REPART_MKFS_OPTIONS_${lib.toUpper fsType}";
value = builtins.concatStringsSep " " options;
}) opts;
in
{
imports = [
@ -113,7 +130,11 @@ in
enable = lib.mkEnableOption "Image compression";
algorithm = lib.mkOption {
type = lib.types.enum [ "zstd" "xz" "zstd-seekable" ];
type = lib.types.enum [
"zstd"
"xz"
"zstd-seekable"
];
default = "zstd";
description = "Compression algorithm";
};
@ -159,7 +180,10 @@ in
package = lib.mkPackageOption pkgs "systemd-repart" {
# We use buildPackages so that repart images are built with the build
# platform's systemd, allowing for cross-compiled systems to work.
default = [ "buildPackages" "systemd" ];
default = [
"buildPackages"
"systemd"
];
example = "pkgs.buildPackages.systemdMinimal.override { withCryptsetup = true; }";
};
@ -196,7 +220,7 @@ in
mkfsOptions = lib.mkOption {
type = with lib.types; attrsOf (listOf str);
default = {};
default = { };
example = lib.literalExpression ''
{
vfat = [ "-S 512" "-c" ];
@ -230,7 +254,8 @@ in
config = {
assertions = lib.mapAttrsToList (fileName: partitionConfig:
assertions = lib.mapAttrsToList (
fileName: partitionConfig:
let
inherit (partitionConfig) repartConfig;
labelLength = builtins.stringLength repartConfig.Label;
@ -240,52 +265,59 @@ in
message = ''
The partition label '${repartConfig.Label}'
defined for '${fileName}' is ${toString labelLength} characters long,
but the maximum label length supported by UEFI is ${toString
GPTMaxLabelLength}.
but the maximum label length supported by UEFI is ${toString GPTMaxLabelLength}.
'';
}
) cfg.partitions;
warnings = lib.filter (v: v != null) (lib.mapAttrsToList (fileName: partitionConfig:
let
inherit (partitionConfig) repartConfig;
suggestedMaxLabelLength = GPTMaxLabelLength - 2;
labelLength = builtins.stringLength repartConfig.Label;
in
if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then ''
The partition label '${repartConfig.Label}'
defined for '${fileName}' is ${toString labelLength} characters long.
The suggested maximum label length is ${toString
suggestedMaxLabelLength}.
warnings = lib.filter (v: v != null) (
lib.mapAttrsToList (
fileName: partitionConfig:
let
inherit (partitionConfig) repartConfig;
suggestedMaxLabelLength = GPTMaxLabelLength - 2;
labelLength = builtins.stringLength repartConfig.Label;
in
if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then
''
The partition label '${repartConfig.Label}'
defined for '${fileName}' is ${toString labelLength} characters long.
The suggested maximum label length is ${toString suggestedMaxLabelLength}.
If you use sytemd-sysupdate style A/B updates, this might
not leave enough space to increment the version number included in
the label in a future release. For example, if your label is
${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
you're at version 9, you cannot increment this to 10.
'' else null
) cfg.partitions);
If you use sytemd-sysupdate style A/B updates, this might
not leave enough space to increment the version number included in
the label in a future release. For example, if your label is
${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
you're at version 9, you cannot increment this to 10.
''
else
null
) cfg.partitions
);
image.repart =
let
version = config.image.repart.version;
versionInfix = if version != null then "_${version}" else "";
compressionSuffix = lib.optionalString cfg.compression.enable
{
"zstd" = ".zst";
"xz" = ".xz";
"zstd-seekable" = ".zst";
}."${cfg.compression.algorithm}";
compressionSuffix =
lib.optionalString cfg.compression.enable
{
"zstd" = ".zst";
"xz" = ".xz";
"zstd-seekable" = ".zst";
}
."${cfg.compression.algorithm}";
makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
# Add the closure of the provided Nix store paths to cfg.partitions so
# that amend-repart-definitions.py can read it.
addClosure = _name: partitionConfig: partitionConfig // (
lib.optionalAttrs
(partitionConfig.storePaths or [ ] != [ ])
{ closure = "${makeClosure partitionConfig.storePaths}/store-paths"; }
);
addClosure =
_name: partitionConfig:
partitionConfig
// (lib.optionalAttrs (partitionConfig.storePaths or [ ] != [ ]) {
closure = "${makeClosure partitionConfig.storePaths}/store-paths";
});
in
{
name = lib.mkIf (config.system.image.id != null) (lib.mkOptionDefault config.system.image.id);
@ -296,11 +328,14 @@ in
# Generally default to slightly faster than default compression
# levels under the assumption that most of the building will be done
# for development and release builds will be customized.
level = lib.mkOptionDefault {
"zstd" = 3;
"xz" = 3;
"zstd-seekable" = 3;
}."${cfg.compression.algorithm}";
level =
lib.mkOptionDefault
{
"zstd" = 3;
"xz" = 3;
"zstd-seekable" = 3;
}
."${cfg.compression.algorithm}";
};
finalPartitions = lib.mapAttrs addClosure cfg.partitions;
@ -308,27 +343,37 @@ in
system.build.image =
let
fileSystems = lib.filter
(f: f != null)
(lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions);
fileSystems = lib.filter (f: f != null) (
lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions
);
format = pkgs.formats.ini { listsAsDuplicateKeys = true; };
definitionsDirectory = utils.systemdUtils.lib.definitions
"repart.d"
format
(lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions);
definitionsDirectory = utils.systemdUtils.lib.definitions "repart.d" format (
lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions
);
mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions;
in
pkgs.callPackage ./repart-image.nix {
systemd = cfg.package;
inherit (cfg) name version imageFileBasename compression split seed sectorSize finalPartitions;
inherit (cfg)
name
version
imageFileBasename
compression
split
seed
sectorSize
finalPartitions
;
inherit fileSystems definitionsDirectory mkfsEnv;
};
meta.maintainers = with lib.maintainers; [ nikstur willibutz ];
meta.maintainers = with lib.maintainers; [
nikstur
willibutz
];
};
}

View File

@ -1,14 +1,20 @@
# This module contains the basic configuration for building a NixOS
# installation CD.
{ config, lib, options, pkgs, ... }:
{
imports =
[ ./iso-image.nix
config,
lib,
options,
pkgs,
...
}:
{
imports = [
./iso-image.nix
# Profiles of this basic installation CD.
../../profiles/base.nix
../../profiles/installation-device.nix
];
# Profiles of this basic installation CD.
../../profiles/base.nix
../../profiles/installation-device.nix
];
hardware.enableAllHardware = true;

View File

@ -1,7 +1,13 @@
# This module creates netboot media containing the given NixOS
# configuration.
{ config, lib, pkgs, modulesPath, ... }:
{
config,
lib,
pkgs,
modulesPath,
...
}:
with lib;
@ -36,43 +42,50 @@ with lib;
# here and it causes a cyclic dependency.
boot.loader.grub.enable = false;
fileSystems."/" = mkImageMediaOverride
{ fsType = "tmpfs";
options = [ "mode=0755" ];
};
fileSystems."/" = mkImageMediaOverride {
fsType = "tmpfs";
options = [ "mode=0755" ];
};
# In stage 1, mount a tmpfs on top of /nix/store (the squashfs
# image) to make this a live CD.
fileSystems."/nix/.ro-store" = mkImageMediaOverride
{ fsType = "squashfs";
device = "../nix-store.squashfs";
options = [ "loop" ] ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2") "threads=multi";
neededForBoot = true;
fileSystems."/nix/.ro-store" = mkImageMediaOverride {
fsType = "squashfs";
device = "../nix-store.squashfs";
options = [
"loop"
] ++ lib.optional (config.boot.kernelPackages.kernel.kernelAtLeast "6.2") "threads=multi";
neededForBoot = true;
};
fileSystems."/nix/.rw-store" = mkImageMediaOverride {
fsType = "tmpfs";
options = [ "mode=0755" ];
neededForBoot = true;
};
fileSystems."/nix/store" = mkImageMediaOverride {
overlay = {
lowerdir = [ "/nix/.ro-store" ];
upperdir = "/nix/.rw-store/store";
workdir = "/nix/.rw-store/work";
};
neededForBoot = true;
};
fileSystems."/nix/.rw-store" = mkImageMediaOverride
{ fsType = "tmpfs";
options = [ "mode=0755" ];
neededForBoot = true;
};
boot.initrd.availableKernelModules = [
"squashfs"
"overlay"
];
fileSystems."/nix/store" = mkImageMediaOverride
{ overlay = {
lowerdir = [ "/nix/.ro-store" ];
upperdir = "/nix/.rw-store/store";
workdir = "/nix/.rw-store/work";
};
neededForBoot = true;
};
boot.initrd.availableKernelModules = [ "squashfs" "overlay" ];
boot.initrd.kernelModules = [ "loop" "overlay" ];
boot.initrd.kernelModules = [
"loop"
"overlay"
];
# Closures to be copied to the Nix store, namely the init
# script and the top-level system configuration directory.
netboot.storeContents =
[ config.system.build.toplevel ];
netboot.storeContents = [ config.system.build.toplevel ];
# Create the squashfs image that contains the Nix store.
system.build.squashfsStore = pkgs.callPackage ../../../lib/make-squashfs.nix {
@ -80,17 +93,17 @@ with lib;
comp = config.netboot.squashfsCompression;
};
# Create the initrd
system.build.netbootRamdisk = pkgs.makeInitrdNG {
inherit (config.boot.initrd) compressor;
prepend = [ "${config.system.build.initialRamdisk}/initrd" ];
contents =
[ { source = config.system.build.squashfsStore;
target = "/nix-store.squashfs";
}
];
contents = [
{
source = config.system.build.squashfsStore;
target = "/nix-store.squashfs";
}
];
};
system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" ''
@ -137,16 +150,18 @@ with lib;
image.filePath = "tarball/${config.image.fileName}";
system.nixos.tags = [ "kexec" ];
system.build.image = config.system.build.kexecTarball;
system.build.kexecTarball = pkgs.callPackage "${toString modulesPath}/../lib/make-system-tarball.nix" {
fileName = config.image.baseName;
storeContents = [
system.build.kexecTarball =
pkgs.callPackage "${toString modulesPath}/../lib/make-system-tarball.nix"
{
object = config.system.build.kexecScript;
symlink = "/kexec_nixos";
}
];
contents = [];
};
fileName = config.image.baseName;
storeContents = [
{
object = config.system.build.kexecScript;
symlink = "/kexec_nixos";
}
];
contents = [ ];
};
boot.loader.timeout = 10;

View File

@ -1,6 +1,11 @@
# To build, use:
# nix-build nixos -I nixos-config=nixos/modules/installer/sd-card/sd-image-aarch64.nix -A config.system.build.sdImage
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
{
imports = [
@ -16,52 +21,58 @@
# The serial ports listed here are:
# - ttyS0: for Tegra (Jetson TX1)
# - ttyAMA0: for QEMU's -machine virt
boot.kernelParams = ["console=ttyS0,115200n8" "console=ttyAMA0,115200n8" "console=tty0"];
boot.kernelParams = [
"console=ttyS0,115200n8"
"console=ttyAMA0,115200n8"
"console=tty0"
];
sdImage = {
populateFirmwareCommands = let
configTxt = pkgs.writeText "config.txt" ''
[pi3]
kernel=u-boot-rpi3.bin
populateFirmwareCommands =
let
configTxt = pkgs.writeText "config.txt" ''
[pi3]
kernel=u-boot-rpi3.bin
# Otherwise the serial output will be garbled.
core_freq=250
# Otherwise the serial output will be garbled.
core_freq=250
[pi02]
kernel=u-boot-rpi3.bin
[pi02]
kernel=u-boot-rpi3.bin
[pi4]
kernel=u-boot-rpi4.bin
enable_gic=1
armstub=armstub8-gic.bin
[pi4]
kernel=u-boot-rpi4.bin
enable_gic=1
armstub=armstub8-gic.bin
# Otherwise the resolution will be weird in most cases, compared to
# what the pi3 firmware does by default.
disable_overscan=1
# Otherwise the resolution will be weird in most cases, compared to
# what the pi3 firmware does by default.
disable_overscan=1
# Supported in newer board revisions
arm_boost=1
# Supported in newer board revisions
arm_boost=1
[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[all]
# Boot in 64-bit mode.
arm_64bit=1
[all]
# Boot in 64-bit mode.
arm_64bit=1
# U-Boot needs this to work, regardless of whether UART is actually used or not.
# Look in arch/arm/mach-bcm283x/Kconfig in the U-Boot tree to see if this is still
# a requirement in the future.
enable_uart=1
# U-Boot needs this to work, regardless of whether UART is actually used or not.
# Look in arch/arm/mach-bcm283x/Kconfig in the U-Boot tree to see if this is still
# a requirement in the future.
enable_uart=1
# Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
# when attempting to show low-voltage or overtemperature warnings.
avoid_warnings=1
'';
in ''
# Prevent the firmware from smashing the framebuffer setup done by the mainline kernel
# when attempting to show low-voltage or overtemperature warnings.
avoid_warnings=1
'';
in
''
(cd ${pkgs.raspberrypifw}/share/raspberrypi/boot && cp bootcode.bin fixup*.dat start*.elf $NIX_BUILD_TOP/firmware/)
# Add the config

View File

@ -11,24 +11,36 @@
# The derivation for the SD image will be placed in
# config.system.build.sdImage
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
with lib;
let
rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({
inherit (config.sdImage) storePaths;
compressImage = config.sdImage.compressImage;
populateImageCommands = config.sdImage.populateRootCommands;
volumeLabel = "NIXOS_SD";
} // optionalAttrs (config.sdImage.rootPartitionUUID != null) {
uuid = config.sdImage.rootPartitionUUID;
});
rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix (
{
inherit (config.sdImage) storePaths;
compressImage = config.sdImage.compressImage;
populateImageCommands = config.sdImage.populateRootCommands;
volumeLabel = "NIXOS_SD";
}
// optionalAttrs (config.sdImage.rootPartitionUUID != null) {
uuid = config.sdImage.rootPartitionUUID;
}
);
in
{
imports = [
(mkRemovedOptionModule [ "sdImage" "bootPartitionID" ] "The FAT partition for SD image now only holds the Raspberry Pi firmware files. Use firmwarePartitionID to configure that partition's ID.")
(mkRemovedOptionModule [ "sdImage" "bootSize" ] "The boot files for SD image have been moved to the main ext4 partition. The FAT partition now only holds the Raspberry Pi firmware files. Changing its size may not be required.")
(mkRemovedOptionModule [ "sdImage" "bootPartitionID" ]
"The FAT partition for SD image now only holds the Raspberry Pi firmware files. Use firmwarePartitionID to configure that partition's ID."
)
(mkRemovedOptionModule [ "sdImage" "bootSize" ]
"The boot files for SD image have been moved to the main ext4 partition. The FAT partition now only holds the Raspberry Pi firmware files. Changing its size may not be required."
)
(lib.mkRenamedOptionModuleWith {
sinceRelease = 2505;
from = [
@ -180,7 +192,10 @@ in
# Alternatively, this could be removed from the configuration.
# The filesystem is not needed at runtime, it could be treated
# as an opaque blob instead of a discrete FAT32 filesystem.
options = [ "nofail" "noauto" ];
options = [
"nofail"
"noauto"
];
};
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";
@ -194,122 +209,139 @@ in
image.filePath = "sd-card/${config.image.fileName}";
system.nixos.tags = [ "sd-card" ];
system.build.image = config.system.build.sdImage;
system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs,
mtools, libfaketime, util-linux, zstd }: stdenv.mkDerivation {
name = config.image.fileName;
system.build.sdImage = pkgs.callPackage (
{
stdenv,
dosfstools,
e2fsprogs,
mtools,
libfaketime,
util-linux,
zstd,
}:
stdenv.mkDerivation {
name = config.image.fileName;
nativeBuildInputs = [ dosfstools e2fsprogs libfaketime mtools util-linux ]
++ lib.optional config.sdImage.compressImage zstd;
nativeBuildInputs = [
dosfstools
e2fsprogs
libfaketime
mtools
util-linux
] ++ lib.optional config.sdImage.compressImage zstd;
inherit (config.sdImage) compressImage;
inherit (config.sdImage) compressImage;
buildCommand = ''
mkdir -p $out/nix-support $out/sd-image
export img=$out/sd-image/${config.image.baseName}.img
buildCommand = ''
mkdir -p $out/nix-support $out/sd-image
export img=$out/sd-image/${config.image.baseName}.img
echo "${pkgs.stdenv.buildPlatform.system}" > $out/nix-support/system
if test -n "$compressImage"; then
echo "file sd-image $img.zst" >> $out/nix-support/hydra-build-products
else
echo "file sd-image $img" >> $out/nix-support/hydra-build-products
fi
echo "${pkgs.stdenv.buildPlatform.system}" > $out/nix-support/system
if test -n "$compressImage"; then
echo "file sd-image $img.zst" >> $out/nix-support/hydra-build-products
else
echo "file sd-image $img" >> $out/nix-support/hydra-build-products
fi
root_fs=${rootfsImage}
${lib.optionalString config.sdImage.compressImage ''
root_fs=./root-fs.img
echo "Decompressing rootfs image"
zstd -d --no-progress "${rootfsImage}" -o $root_fs
''}
root_fs=${rootfsImage}
${lib.optionalString config.sdImage.compressImage ''
root_fs=./root-fs.img
echo "Decompressing rootfs image"
zstd -d --no-progress "${rootfsImage}" -o $root_fs
''}
# Gap in front of the first partition, in MiB
gap=${toString config.sdImage.firmwarePartitionOffset}
# Gap in front of the first partition, in MiB
gap=${toString config.sdImage.firmwarePartitionOffset}
# Create the image file sized to fit /boot/firmware and /, plus slack for the gap.
rootSizeBlocks=$(du -B 512 --apparent-size $root_fs | awk '{ print $1 }')
firmwareSizeBlocks=$((${toString config.sdImage.firmwareSize} * 1024 * 1024 / 512))
imageSize=$((rootSizeBlocks * 512 + firmwareSizeBlocks * 512 + gap * 1024 * 1024))
truncate -s $imageSize $img
# Create the image file sized to fit /boot/firmware and /, plus slack for the gap.
rootSizeBlocks=$(du -B 512 --apparent-size $root_fs | awk '{ print $1 }')
firmwareSizeBlocks=$((${toString config.sdImage.firmwareSize} * 1024 * 1024 / 512))
imageSize=$((rootSizeBlocks * 512 + firmwareSizeBlocks * 512 + gap * 1024 * 1024))
truncate -s $imageSize $img
# type=b is 'W95 FAT32', type=83 is 'Linux'.
# The "bootable" partition is where u-boot will look file for the bootloader
# information (dtbs, extlinux.conf file).
sfdisk --no-reread --no-tell-kernel $img <<EOF
label: dos
label-id: ${config.sdImage.firmwarePartitionID}
# type=b is 'W95 FAT32', type=83 is 'Linux'.
# The "bootable" partition is where u-boot will look file for the bootloader
# information (dtbs, extlinux.conf file).
sfdisk --no-reread --no-tell-kernel $img <<EOF
label: dos
label-id: ${config.sdImage.firmwarePartitionID}
start=''${gap}M, size=$firmwareSizeBlocks, type=b
start=$((gap + ${toString config.sdImage.firmwareSize}))M, type=83, bootable
EOF
start=''${gap}M, size=$firmwareSizeBlocks, type=b
start=$((gap + ${toString config.sdImage.firmwareSize}))M, type=83, bootable
EOF
# Copy the rootfs into the SD image
eval $(partx $img -o START,SECTORS --nr 2 --pairs)
dd conv=notrunc if=$root_fs of=$img seek=$START count=$SECTORS
# Copy the rootfs into the SD image
eval $(partx $img -o START,SECTORS --nr 2 --pairs)
dd conv=notrunc if=$root_fs of=$img seek=$START count=$SECTORS
# Create a FAT32 /boot/firmware partition of suitable size into firmware_part.img
eval $(partx $img -o START,SECTORS --nr 1 --pairs)
truncate -s $((SECTORS * 512)) firmware_part.img
# Create a FAT32 /boot/firmware partition of suitable size into firmware_part.img
eval $(partx $img -o START,SECTORS --nr 1 --pairs)
truncate -s $((SECTORS * 512)) firmware_part.img
mkfs.vfat --invariant -i ${config.sdImage.firmwarePartitionID} -n ${config.sdImage.firmwarePartitionName} firmware_part.img
mkfs.vfat --invariant -i ${config.sdImage.firmwarePartitionID} -n ${config.sdImage.firmwarePartitionName} firmware_part.img
# Populate the files intended for /boot/firmware
mkdir firmware
${config.sdImage.populateFirmwareCommands}
# Populate the files intended for /boot/firmware
mkdir firmware
${config.sdImage.populateFirmwareCommands}
find firmware -exec touch --date=2000-01-01 {} +
# Copy the populated /boot/firmware into the SD image
cd firmware
# Force a fixed order in mcopy for better determinism, and avoid file globbing
for d in $(find . -type d -mindepth 1 | sort); do
faketime "2000-01-01 00:00:00" mmd -i ../firmware_part.img "::/$d"
done
for f in $(find . -type f | sort); do
mcopy -pvm -i ../firmware_part.img "$f" "::/$f"
done
cd ..
find firmware -exec touch --date=2000-01-01 {} +
# Copy the populated /boot/firmware into the SD image
cd firmware
# Force a fixed order in mcopy for better determinism, and avoid file globbing
for d in $(find . -type d -mindepth 1 | sort); do
faketime "2000-01-01 00:00:00" mmd -i ../firmware_part.img "::/$d"
done
for f in $(find . -type f | sort); do
mcopy -pvm -i ../firmware_part.img "$f" "::/$f"
done
cd ..
# Verify the FAT partition before copying it.
fsck.vfat -vn firmware_part.img
dd conv=notrunc if=firmware_part.img of=$img seek=$START count=$SECTORS
# Verify the FAT partition before copying it.
fsck.vfat -vn firmware_part.img
dd conv=notrunc if=firmware_part.img of=$img seek=$START count=$SECTORS
${config.sdImage.postBuildCommands}
${config.sdImage.postBuildCommands}
if test -n "$compressImage"; then
zstd -T$NIX_BUILD_CORES --rm $img
if test -n "$compressImage"; then
zstd -T$NIX_BUILD_CORES --rm $img
fi
'';
}
) { };
boot.postBootCommands =
let
expandOnBoot = lib.optionalString config.sdImage.expandOnBoot ''
# Figure out device names for the boot device and root filesystem.
rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /)
bootDevice=$(lsblk -npo PKNAME $rootPart)
partNum=$(lsblk -npo MAJ:MIN $rootPart | ${pkgs.gawk}/bin/awk -F: '{print $2}')
# Resize the root partition and the filesystem to fit the disk
echo ",+," | sfdisk -N$partNum --no-reread $bootDevice
${pkgs.parted}/bin/partprobe
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
'';
nixPathRegistrationFile = config.sdImage.nixPathRegistrationFile;
in
''
# On the first boot do some maintenance tasks
if [ -f ${nixPathRegistrationFile} ]; then
set -euo pipefail
set -x
${expandOnBoot}
# Register the contents of the initial Nix store
${config.nix.package.out}/bin/nix-store --load-db < ${nixPathRegistrationFile}
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
# Prevents this from running on later boots.
rm -f ${nixPathRegistrationFile}
fi
'';
}) {};
boot.postBootCommands = let
expandOnBoot = lib.optionalString config.sdImage.expandOnBoot ''
# Figure out device names for the boot device and root filesystem.
rootPart=$(${pkgs.util-linux}/bin/findmnt -n -o SOURCE /)
bootDevice=$(lsblk -npo PKNAME $rootPart)
partNum=$(lsblk -npo MAJ:MIN $rootPart | ${pkgs.gawk}/bin/awk -F: '{print $2}')
# Resize the root partition and the filesystem to fit the disk
echo ",+," | sfdisk -N$partNum --no-reread $bootDevice
${pkgs.parted}/bin/partprobe
${pkgs.e2fsprogs}/bin/resize2fs $rootPart
'';
nixPathRegistrationFile = config.sdImage.nixPathRegistrationFile;
in ''
# On the first boot do some maintenance tasks
if [ -f ${nixPathRegistrationFile} ]; then
set -euo pipefail
set -x
${expandOnBoot}
# Register the contents of the initial Nix store
${config.nix.package.out}/bin/nix-store --load-db < ${nixPathRegistrationFile}
# nixos-rebuild also requires a "system" profile and an /etc/NIXOS tag.
touch /etc/NIXOS
${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
# Prevents this from running on later boots.
rm -f ${nixPathRegistrationFile}
fi
'';
};
}

View File

@ -1,4 +1,16 @@
{ config, options, lib, pkgs, utils, modules, baseModules, extraModules, modulesPath, specialArgs, ... }:
{
config,
options,
lib,
pkgs,
utils,
modules,
baseModules,
extraModules,
modulesPath,
specialArgs,
...
}:
let
inherit (lib)
@ -31,25 +43,26 @@ let
cfg = config.documentation;
allOpts = options;
canCacheDocs = m:
canCacheDocs =
m:
let
f = import m;
instance = f (mapAttrs (n: _: abort "evaluating ${n} for `meta` failed") (functionArgs f));
in
cfg.nixos.options.splitBuild
&& isPath m
&& isFunction f
&& instance ? options
&& instance.meta.buildDocsInSandbox or true;
cfg.nixos.options.splitBuild
&& isPath m
&& isFunction f
&& instance ? options
&& instance.meta.buildDocsInSandbox or true;
docModules =
let
p = partition canCacheDocs (baseModules ++ cfg.nixos.extraModules);
in
{
lazy = p.right;
eager = p.wrong ++ optionals cfg.nixos.includeAllModules (extraModules ++ modules);
};
{
lazy = p.right;
eager = p.wrong ++ optionals cfg.nixos.includeAllModules (extraModules ++ modules);
};
manual = import ../../doc/manual rec {
inherit pkgs config;
@ -59,9 +72,11 @@ let
options =
let
scrubbedEval = evalModules {
modules = [ {
_module.check = false;
} ] ++ docModules.eager;
modules = [
{
_module.check = false;
}
] ++ docModules.eager;
class = "nixos";
specialArgs = specialArgs // {
pkgs = scrubDerivations "pkgs" pkgs;
@ -71,33 +86,37 @@ let
inherit modulesPath utils;
};
};
scrubDerivations = namePrefix: pkgSet: mapAttrs
(name: value:
scrubDerivations =
namePrefix: pkgSet:
mapAttrs (
name: value:
let
wholeName = "${namePrefix}.${name}";
guard = warn "Attempt to evaluate package ${wholeName} in option documentation; this is not supported and will eventually be an error. Use `mkPackageOption{,MD}` or `literalExpression` instead.";
in if isAttrs value then
in
if isAttrs value then
scrubDerivations wholeName value
// optionalAttrs (isDerivation value) {
outPath = guard "\${${wholeName}}";
drvPath = guard value.drvPath;
}
else value
)
pkgSet;
in scrubbedEval.options;
else
value
) pkgSet;
in
scrubbedEval.options;
baseOptionsJSON =
let
filter =
builtins.filterSource
(n: t:
cleanSourceFilter n t
&& (t == "directory" -> baseNameOf n != "tests")
&& (t == "file" -> hasSuffix ".nix" n)
);
filter = builtins.filterSource (
n: t:
cleanSourceFilter n t
&& (t == "directory" -> baseNameOf n != "tests")
&& (t == "file" -> hasSuffix ".nix" n)
);
in
pkgs.runCommand "lazy-options.json" {
pkgs.runCommand "lazy-options.json"
{
libPath = filter (pkgs.path + "/lib");
pkgsLibPath = filter (pkgs.path + "/pkgs/pkgs-lib");
nixosPath = filter (pkgs.path + "/nixos");
@ -107,7 +126,8 @@ let
+ concatMapStringsSep " " (p: ''"${removePrefix "${modulesPath}/" (toString p)}"'') docModules.lazy
+ " ]";
passAsFile = [ "modules" ];
} ''
}
''
export NIX_STORE_DIR=$TMPDIR/store
export NIX_STATE_DIR=$TMPDIR/state
${pkgs.buildPackages.nix}/bin/nix-instantiate \
@ -139,36 +159,37 @@ let
inherit (cfg.nixos.options) warningsAreErrors;
};
nixos-help = let
helpScript = pkgs.writeShellScriptBin "nixos-help" ''
# Finds first executable browser in a colon-separated list.
# (see how xdg-open defines BROWSER)
browser="$(
IFS=: ; for b in $BROWSER; do
[ -n "$(type -P "$b" || true)" ] && echo "$b" && break
done
)"
if [ -z "$browser" ]; then
browser="$(type -P xdg-open || true)"
nixos-help =
let
helpScript = pkgs.writeShellScriptBin "nixos-help" ''
# Finds first executable browser in a colon-separated list.
# (see how xdg-open defines BROWSER)
browser="$(
IFS=: ; for b in $BROWSER; do
[ -n "$(type -P "$b" || true)" ] && echo "$b" && break
done
)"
if [ -z "$browser" ]; then
browser="${pkgs.w3m-nographics}/bin/w3m"
browser="$(type -P xdg-open || true)"
if [ -z "$browser" ]; then
browser="${pkgs.w3m-nographics}/bin/w3m"
fi
fi
fi
exec "$browser" ${manual.manualHTMLIndex}
'';
exec "$browser" ${manual.manualHTMLIndex}
'';
desktopItem = pkgs.makeDesktopItem {
name = "nixos-manual";
desktopName = "NixOS Manual";
genericName = "System Manual";
comment = "View NixOS documentation in a web browser";
icon = "nix-snowflake";
exec = "nixos-help";
categories = ["System"];
};
desktopItem = pkgs.makeDesktopItem {
name = "nixos-manual";
desktopName = "NixOS Manual";
genericName = "System Manual";
comment = "View NixOS documentation in a web browser";
icon = "nix-snowflake";
exec = "nixos-help";
categories = [ "System" ];
};
in pkgs.symlinkJoin {
in
pkgs.symlinkJoin {
name = "nixos-help";
paths = [
helpScript
@ -187,11 +208,14 @@ in
../config/system-path.nix
../system/etc/etc.nix
(mkRenamedOptionModule [ "programs" "info" "enable" ] [ "documentation" "info" "enable" ])
(mkRenamedOptionModule [ "programs" "man" "enable" ] [ "documentation" "man" "enable" ])
(mkRenamedOptionModule [ "programs" "man" "enable" ] [ "documentation" "man" "enable" ])
(mkRenamedOptionModule [ "services" "nixosManual" "enable" ] [ "documentation" "nixos" "enable" ])
(mkRemovedOptionModule
[ "documentation" "nixos" "options" "allowDocBook" ]
"DocBook option documentation is no longer supported")
(mkRemovedOptionModule [
"documentation"
"nixos"
"options"
"allowDocBook"
] "DocBook option documentation is no longer supported")
];
options = {
@ -280,7 +304,7 @@ in
nixos.extraModules = mkOption {
type = types.listOf types.raw;
default = [];
default = [ ];
description = ''
Modules for which to show options even when not imported.
'';
@ -380,9 +404,13 @@ in
(mkIf cfg.nixos.enable {
system.build.manual = manual;
environment.systemPackages = []
environment.systemPackages =
[ ]
++ optional cfg.man.enable manual.nixos-configuration-reference-manpage
++ optionals cfg.doc.enable [ manual.manualHTML nixos-help ];
++ optionals cfg.doc.enable [
manual.manualHTML
nixos-help
];
})
]);

View File

@ -1,23 +1,28 @@
# Provide a basic configuration for installation devices like CDs.
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
with lib;
{
imports =
[ # Enable devices which are usually scanned, because we don't know the
# target system.
../installer/scan/detected.nix
../installer/scan/not-detected.nix
imports = [
# Enable devices which are usually scanned, because we don't know the
# target system.
../installer/scan/detected.nix
../installer/scan/not-detected.nix
# Allow "nixos-rebuild" to work properly by providing
# /etc/nixos/configuration.nix.
./clone-config.nix
# Allow "nixos-rebuild" to work properly by providing
# /etc/nixos/configuration.nix.
./clone-config.nix
# Include a copy of Nixpkgs so that nixos-install works out of
# the box.
../installer/cd-dvd/channel.nix
];
# Include a copy of Nixpkgs so that nixos-install works out of
# the box.
../installer/cd-dvd/channel.nix
];
config = {
system.nixos.variant_id = lib.mkDefault "installer";
@ -31,7 +36,11 @@ with lib;
# Use less privileged nixos user
users.users.nixos = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" "video" ];
extraGroups = [
"wheel"
"networkmanager"
"video"
];
# Allow the graphical user to login without password
initialHashedPassword = "";
};
@ -52,21 +61,23 @@ with lib;
services.getty.autologinUser = "nixos";
# Some more help text.
services.getty.helpLine = ''
The "nixos" and "root" accounts have empty passwords.
services.getty.helpLine =
''
The "nixos" and "root" accounts have empty passwords.
To log in over ssh you must set a password for either "nixos" or "root"
with `passwd` (prefix with `sudo` for "root"), or add your public key to
/home/nixos/.ssh/authorized_keys or /root/.ssh/authorized_keys.
To log in over ssh you must set a password for either "nixos" or "root"
with `passwd` (prefix with `sudo` for "root"), or add your public key to
/home/nixos/.ssh/authorized_keys or /root/.ssh/authorized_keys.
If you need a wireless connection, type
`sudo systemctl start wpa_supplicant` and configure a
network using `wpa_cli`. See the NixOS manual for details.
'' + optionalString config.services.xserver.enable ''
If you need a wireless connection, type
`sudo systemctl start wpa_supplicant` and configure a
network using `wpa_cli`. See the NixOS manual for details.
''
+ optionalString config.services.xserver.enable ''
Type `sudo systemctl start display-manager' to
start the graphical user interface.
'';
Type `sudo systemctl start display-manager' to
start the graphical user interface.
'';
# We run sshd by default. Login is only possible after adding a
# password via "passwd" or by adding a ssh key to ~/.ssh/authorized_keys.
@ -81,7 +92,7 @@ with lib;
# Enable wpa_supplicant, but don't start it by default.
networking.wireless.enable = mkDefault true;
networking.wireless.userControlled.enable = true;
systemd.services.wpa_supplicant.wantedBy = mkOverride 50 [];
systemd.services.wpa_supplicant.wantedBy = mkOverride 50 [ ];
# Tell the Nix evaluator to garbage collect more aggressively.
# This is desirable in memory-constrained environments that don't
@ -97,7 +108,8 @@ with lib;
# To speed up installation a little bit, include the complete
# stdenv in the Nix store on the CD.
system.extraDependencies = with pkgs;
system.extraDependencies =
with pkgs;
[
stdenv
stdenvNoCC # for runCommand

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
@ -7,13 +12,13 @@ let
cfg = config.programs.fish;
fishAbbrs = lib.concatStringsSep "\n" (
lib.mapAttrsToList (k: v: "abbr -ag ${k} ${lib.escapeShellArg v}")
cfg.shellAbbrs
lib.mapAttrsToList (k: v: "abbr -ag ${k} ${lib.escapeShellArg v}") cfg.shellAbbrs
);
fishAliases = lib.concatStringsSep "\n" (
lib.mapAttrsToList (k: v: "alias ${k} ${lib.escapeShellArg v}")
(lib.filterAttrs (k: v: v != null) cfg.shellAliases)
lib.mapAttrsToList (k: v: "alias ${k} ${lib.escapeShellArg v}") (
lib.filterAttrs (k: v: v != null) cfg.shellAliases
)
);
envShellInit = pkgs.writeText "shellInit" cfge.shellInit;
@ -22,17 +27,19 @@ let
envInteractiveShellInit = pkgs.writeText "interactiveShellInit" cfge.interactiveShellInit;
sourceEnv = file:
if cfg.useBabelfish then
"source /etc/fish/${file}.fish"
else
''
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $fish_function_path
fenv source /etc/fish/foreign-env/${file} > /dev/null
set -e fish_function_path[1]
'';
sourceEnv =
file:
if cfg.useBabelfish then
"source /etc/fish/${file}.fish"
else
''
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $fish_function_path
fenv source /etc/fish/foreign-env/${file} > /dev/null
set -e fish_function_path[1]
'';
babelfishTranslate = path: name:
babelfishTranslate =
path: name:
pkgs.runCommand "${name}.fish" {
preferLocalBuild = true;
nativeBuildInputs = [ pkgs.babelfish ];
@ -90,7 +97,7 @@ in
};
shellAbbrs = lib.mkOption {
default = {};
default = { };
example = {
gco = "git checkout";
npu = "nix-prefetch-url";
@ -102,7 +109,7 @@ in
};
shellAliases = lib.mkOption {
default = {};
default = { };
description = ''
Set of aliases for fish shell, which overrides {option}`environment.shellAliases`.
See {option}`environment.shellAliases` for an option format description.
@ -154,16 +161,16 @@ in
documentation.man.generateCaches = lib.mkDefault true;
environment = lib.mkMerge [
(lib.mkIf cfg.useBabelfish
{
etc."fish/setEnvironment.fish".source = babelfishTranslate config.system.build.setEnvironment "setEnvironment";
(lib.mkIf cfg.useBabelfish {
etc."fish/setEnvironment.fish".source =
babelfishTranslate config.system.build.setEnvironment "setEnvironment";
etc."fish/shellInit.fish".source = babelfishTranslate envShellInit "shellInit";
etc."fish/loginShellInit.fish".source = babelfishTranslate envLoginShellInit "loginShellInit";
etc."fish/interactiveShellInit.fish".source = babelfishTranslate envInteractiveShellInit "interactiveShellInit";
})
etc."fish/interactiveShellInit.fish".source =
babelfishTranslate envInteractiveShellInit "interactiveShellInit";
})
(lib.mkIf (!cfg.useBabelfish)
{
(lib.mkIf (!cfg.useBabelfish) {
etc."fish/foreign-env/shellInit".source = envShellInit;
etc."fish/foreign-env/loginShellInit".source = envLoginShellInit;
etc."fish/foreign-env/interactiveShellInit".source = envInteractiveShellInit;
@ -171,110 +178,120 @@ in
{
etc."fish/nixos-env-preinit.fish".text =
if cfg.useBabelfish
then ''
# source the NixOS environment config
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
source /etc/fish/setEnvironment.fish
end
''
else ''
# This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
# unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions
if cfg.useBabelfish then
''
# source the NixOS environment config
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
source /etc/fish/setEnvironment.fish
end
''
else
''
# This happens before $__fish_datadir/config.fish sets fish_function_path, so it is currently
# unset. We set it and then completely erase it, leaving its configuration to $__fish_datadir/config.fish
set fish_function_path ${pkgs.fishPlugins.foreign-env}/share/fish/vendor_functions.d $__fish_datadir/functions
# source the NixOS environment config
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
fenv source ${config.system.build.setEnvironment}
end
# source the NixOS environment config
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]
fenv source ${config.system.build.setEnvironment}
end
# clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
set -e fish_function_path
'';
# clear fish_function_path so that it will be correctly set when we return to $__fish_datadir/config.fish
set -e fish_function_path
'';
}
{
etc."fish/config.fish".text = ''
# /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
# /etc/fish/config.fish: DO NOT EDIT -- this file has been generated automatically.
# if we haven't sourced the general config, do it
if not set -q __fish_nixos_general_config_sourced
${sourceEnv "shellInit"}
# if we haven't sourced the general config, do it
if not set -q __fish_nixos_general_config_sourced
${sourceEnv "shellInit"}
${cfg.shellInit}
${cfg.shellInit}
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew)
set -g __fish_nixos_general_config_sourced 1
end
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew)
set -g __fish_nixos_general_config_sourced 1
end
# if we haven't sourced the login config, do it
status is-login; and not set -q __fish_nixos_login_config_sourced
and begin
${sourceEnv "loginShellInit"}
# if we haven't sourced the login config, do it
status is-login; and not set -q __fish_nixos_login_config_sourced
and begin
${sourceEnv "loginShellInit"}
${cfg.loginShellInit}
${cfg.loginShellInit}
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew)
set -g __fish_nixos_login_config_sourced 1
end
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew)
set -g __fish_nixos_login_config_sourced 1
end
# if we haven't sourced the interactive config, do it
status is-interactive; and not set -q __fish_nixos_interactive_config_sourced
and begin
${fishAbbrs}
${fishAliases}
# if we haven't sourced the interactive config, do it
status is-interactive; and not set -q __fish_nixos_interactive_config_sourced
and begin
${fishAbbrs}
${fishAliases}
${sourceEnv "interactiveShellInit"}
${sourceEnv "interactiveShellInit"}
${cfg.promptInit}
${cfg.interactiveShellInit}
${cfg.promptInit}
${cfg.interactiveShellInit}
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew,
# allowing configuration changes in, e.g, aliases, to propagate)
set -g __fish_nixos_interactive_config_sourced 1
end
'';
# and leave a note so we don't source this config section again from
# this very shell (children will source the general config anew,
# allowing configuration changes in, e.g, aliases, to propagate)
set -g __fish_nixos_interactive_config_sourced 1
end
'';
}
{
etc."fish/generated_completions".source =
let
patchedGenerator = pkgs.stdenv.mkDerivation {
name = "fish_patched-completion-generator";
srcs = [
"${cfg.package}/share/fish/tools/create_manpage_completions.py"
"${cfg.package}/share/fish/tools/deroff.py"
];
unpackCmd = "cp $curSrc $(basename $curSrc)";
sourceRoot = ".";
patches = [ ./fish_completion-generator.patch ]; # to prevent collisions of identical completion files
dontBuild = true;
installPhase = ''
mkdir -p $out
cp * $out/
'';
preferLocalBuild = true;
allowSubstitutes = false;
};
generateCompletions = package: pkgs.runCommand
( with lib.strings; let
storeLength = stringLength storeDir + 34; # Nix' StorePath::HashLen + 2 for the separating slash and dash
pathName = substring storeLength (stringLength package - storeLength) package;
in (package.name or pathName) + "_fish-completions")
( { inherit package;
preferLocalBuild = true;
} //
lib.optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; })
''
mkdir -p $out
if [ -d $package/share/man ]; then
find $package/share/man -type f | xargs ${pkgs.python3.pythonOnBuildForHost.interpreter} ${patchedGenerator}/create_manpage_completions.py --directory $out >/dev/null
fi
'';
in
let
patchedGenerator = pkgs.stdenv.mkDerivation {
name = "fish_patched-completion-generator";
srcs = [
"${cfg.package}/share/fish/tools/create_manpage_completions.py"
"${cfg.package}/share/fish/tools/deroff.py"
];
unpackCmd = "cp $curSrc $(basename $curSrc)";
sourceRoot = ".";
patches = [ ./fish_completion-generator.patch ]; # to prevent collisions of identical completion files
dontBuild = true;
installPhase = ''
mkdir -p $out
cp * $out/
'';
preferLocalBuild = true;
allowSubstitutes = false;
};
generateCompletions =
package:
pkgs.runCommand
(
with lib.strings;
let
storeLength = stringLength storeDir + 34; # Nix' StorePath::HashLen + 2 for the separating slash and dash
pathName = substring storeLength (stringLength package - storeLength) package;
in
(package.name or pathName) + "_fish-completions"
)
(
{
inherit package;
preferLocalBuild = true;
}
// lib.optionalAttrs (package ? meta.priority) { meta.priority = package.meta.priority; }
)
''
mkdir -p $out
if [ -d $package/share/man ]; then
find $package/share/man -type f | xargs ${pkgs.python3.pythonOnBuildForHost.interpreter} ${patchedGenerator}/create_manpage_completions.py --directory $out >/dev/null
fi
'';
in
pkgs.buildEnv {
name = "system_fish-completions";
ignoreCollisions = true;
@ -284,10 +301,11 @@ in
# include programs that bring their own completions
{
pathsToLink = []
++ lib.optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
++ lib.optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
++ lib.optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
pathsToLink =
[ ]
++ lib.optional cfg.vendor.config.enable "/share/fish/vendor_conf.d"
++ lib.optional cfg.vendor.completions.enable "/share/fish/vendor_completions.d"
++ lib.optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
}
{ systemPackages = [ cfg.package ]; }

View File

@ -1,4 +1,9 @@
{ pkgs, config, lib, ... }:
{
pkgs,
config,
lib,
...
}:
let
cfg = config.programs.fzf;
@ -16,19 +21,24 @@ in
programs = {
# load after programs.bash.completion.enable
bash.promptPluginInit = lib.mkAfter (lib.optionalString cfg.fuzzyCompletion ''
source ${pkgs.fzf}/share/fzf/completion.bash
'' + lib.optionalString cfg.keybindings ''
source ${pkgs.fzf}/share/fzf/key-bindings.bash
'');
bash.promptPluginInit = lib.mkAfter (
lib.optionalString cfg.fuzzyCompletion ''
source ${pkgs.fzf}/share/fzf/completion.bash
''
+ lib.optionalString cfg.keybindings ''
source ${pkgs.fzf}/share/fzf/key-bindings.bash
''
);
zsh = {
interactiveShellInit = lib.optionalString (!config.programs.zsh.ohMyZsh.enable)
(lib.optionalString cfg.fuzzyCompletion ''
source ${pkgs.fzf}/share/fzf/completion.zsh
'' + lib.optionalString cfg.keybindings ''
source ${pkgs.fzf}/share/fzf/key-bindings.zsh
'');
interactiveShellInit = lib.optionalString (!config.programs.zsh.ohMyZsh.enable) (
lib.optionalString cfg.fuzzyCompletion ''
source ${pkgs.fzf}/share/fzf/completion.zsh
''
+ lib.optionalString cfg.keybindings ''
source ${pkgs.fzf}/share/fzf/key-bindings.zsh
''
);
ohMyZsh.plugins = lib.mkIf config.programs.zsh.ohMyZsh.enable [ "fzf" ];
};

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.git;
@ -19,27 +24,43 @@ in
let
gitini = attrsOf (attrsOf anything);
in
either gitini (listOf gitini) // {
merge = loc: defs:
either gitini (listOf gitini)
// {
merge =
loc: defs:
let
config = builtins.foldl'
(acc: { value, ... }@x: acc // (if builtins.isList value then {
ordered = acc.ordered ++ value;
} else {
unordered = acc.unordered ++ [ x ];
}))
{
ordered = [ ];
unordered = [ ];
}
defs;
config =
builtins.foldl'
(
acc:
{ value, ... }@x:
acc
// (
if builtins.isList value then
{
ordered = acc.ordered ++ value;
}
else
{
unordered = acc.unordered ++ [ x ];
}
)
)
{
ordered = [ ];
unordered = [ ];
}
defs;
in
[ (gitini.merge loc config.unordered) ] ++ config.ordered;
};
default = [ ];
example = {
init.defaultBranch = "main";
url."https://github.com/".insteadOf = [ "gh:" "github:" ];
url."https://github.com/".insteadOf = [
"gh:"
"github:"
];
};
description = ''
Configuration to write to /etc/gitconfig. A list can also be

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
{
meta.maintainers = [ lib.maintainers.league ];
@ -23,6 +28,6 @@
config = lib.mkIf config.programs.gphoto2.enable {
services.udev.packages = [ pkgs.libgphoto2 ];
environment.systemPackages = [ pkgs.gphoto2 ];
users.groups.camera = {};
users.groups.camera = { };
};
}

View File

@ -1,8 +1,14 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
cfg = config.programs.iotop;
in {
in
{
options = {
programs.iotop.enable = lib.mkEnableOption "iotop + setcap wrapper";
};

View File

@ -2,7 +2,10 @@
{
imports = [
(lib.mkRemovedOptionModule [ "programs" "k3b" "enable" ]
"Please add kdePackages.k3b to environment.systemPackages instead")
(lib.mkRemovedOptionModule [
"programs"
"k3b"
"enable"
] "Please add kdePackages.k3b to environment.systemPackages instead")
];
}

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.nano;
@ -37,11 +42,13 @@ in
config = lib.mkIf cfg.enable {
environment = {
etc.nanorc.text = (lib.optionalString cfg.syntaxHighlight ''
# load syntax highlighting files
include "${cfg.package}/share/nano/*.nanorc"
include "${cfg.package}/share/nano/extra/*.nanorc"
'') + cfg.nanorc;
etc.nanorc.text =
(lib.optionalString cfg.syntaxHighlight ''
# load syntax highlighting files
include "${cfg.package}/share/nano/*.nanorc"
include "${cfg.package}/share/nano/extra/*.nanorc"
'')
+ cfg.nanorc;
systemPackages = [ cfg.package ];
pathsToLink = [ "/share/nano" ];
};

View File

@ -14,7 +14,7 @@ in
programs.qgroundcontrol = {
enable = lib.mkEnableOption "qgroundcontrol";
package = lib.mkPackageOption pkgs "qgroundcontrol" {};
package = lib.mkPackageOption pkgs "qgroundcontrol" { };
blacklistModemManagerFromTTYUSB = lib.mkOption {
type = lib.types.bool;

View File

@ -1,5 +1,11 @@
# Configuration for the pwdutils suite of tools: passwd, useradd, etc.
{ config, lib, utils, pkgs, ... }:
{
config,
lib,
utils,
pkgs,
...
}:
let
cfg = config.security.loginDefs;
in
@ -35,27 +41,37 @@ in
'';
type = lib.types.submodule {
freeformType = (pkgs.formats.keyValue { }).type;
/* There are three different sources for user/group id ranges, each of which gets
used by different programs:
- The login.defs file, used by the useradd, groupadd and newusers commands
- The update-users-groups.pl file, used by NixOS in the activation phase to
decide on which ids to use for declaratively defined users without a static
id
- Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
by systemd for features like ConditionUser=@system and systemd-sysusers
*/
/*
There are three different sources for user/group id ranges, each of which gets
used by different programs:
- The login.defs file, used by the useradd, groupadd and newusers commands
- The update-users-groups.pl file, used by NixOS in the activation phase to
decide on which ids to use for declaratively defined users without a static
id
- Systemd compile time options -Dsystem-uid-max= and -Dsystem-gid-max=, used
by systemd for features like ConditionUser=@system and systemd-sysusers
*/
options = {
DEFAULT_HOME = lib.mkOption {
description = "Indicate if login is allowed if we can't cd to the home directory.";
default = "yes";
type = lib.types.enum [ "yes" "no" ];
type = lib.types.enum [
"yes"
"no"
];
};
ENCRYPT_METHOD = lib.mkOption {
description = "This defines the system default encryption algorithm for encrypting passwords.";
# The default crypt() method, keep in sync with the PAM default
default = "YESCRYPT";
type = lib.types.enum [ "YESCRYPT" "SHA512" "SHA256" "MD5" "DES"];
type = lib.types.enum [
"YESCRYPT"
"SHA512"
"SHA256"
"MD5"
"DES"
];
};
SYS_UID_MIN = lib.mkOption {
@ -180,7 +196,8 @@ in
security.loginDefs.settings.CHFN_RESTRICT = lib.mkIf (cfg.chfnRestrict != null) cfg.chfnRestrict;
environment.systemPackages = lib.optional config.users.mutableUsers cfg.package
environment.systemPackages =
lib.optional config.users.mutableUsers cfg.package
++ lib.optional (lib.types.shellPackage.check config.users.defaultUserShell) config.users.defaultUserShell
++ lib.optional (cfg.chfnRestrict != null) pkgs.util-linux;
@ -191,7 +208,8 @@ in
toKeyValue = lib.generators.toKeyValue {
mkKeyValue = lib.generators.mkKeyValueDefault { } " ";
};
in {
in
{
# /etc/login.defs: global configuration for pwdutils.
# You cannot login without it!
"login.defs".source = pkgs.writeText "login.defs" (toKeyValue cfg.settings);
@ -241,17 +259,17 @@ in
inherit source;
};
in
{
su = mkSetuidRoot "${cfg.package.su}/bin/su";
sg = mkSetuidRoot "${cfg.package.out}/bin/sg";
newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp";
newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap";
newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap";
}
// lib.optionalAttrs config.users.mutableUsers {
chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";
passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd";
};
{
su = mkSetuidRoot "${cfg.package.su}/bin/su";
sg = mkSetuidRoot "${cfg.package.out}/bin/sg";
newgrp = mkSetuidRoot "${cfg.package.out}/bin/newgrp";
newuidmap = mkSetuidRoot "${cfg.package.out}/bin/newuidmap";
newgidmap = mkSetuidRoot "${cfg.package.out}/bin/newgidmap";
}
// lib.optionalAttrs config.users.mutableUsers {
chsh = mkSetuidRoot "${cfg.package.out}/bin/chsh";
passwd = mkSetuidRoot "${cfg.package.out}/bin/passwd";
};
})
];
}

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.steam;
@ -6,9 +11,12 @@ let
extraCompatPaths = lib.makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages;
steam-gamescope = let
exports = builtins.attrValues (builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env);
in
steam-gamescope =
let
exports = builtins.attrValues (
builtins.mapAttrs (n: v: "export ${n}=${v}") cfg.gamescopeSession.env
);
in
pkgs.writeShellScriptBin "steam-gamescope" ''
${builtins.concatStringsSep "\n" exports}
gamescope --steam ${builtins.toString cfg.gamescopeSession.args} -- steam ${builtins.toString cfg.gamescopeSession.steamArgs}
@ -21,8 +29,12 @@ let
Comment=A digital distribution platform
Exec=${steam-gamescope}/bin/steam-gamescope
Type=Application
'').overrideAttrs (_: { passthru.providedSessions = [ "steam" ]; });
in {
'').overrideAttrs
(_: {
passthru.providedSessions = [ "steam" ];
});
in
{
options.programs.steam = {
enable = lib.mkEnableOption "steam";
@ -42,27 +54,40 @@ in {
];
}
'';
apply = steam: steam.override (prev: {
extraEnv = (lib.optionalAttrs (cfg.extraCompatPackages != [ ]) {
STEAM_EXTRA_COMPAT_TOOLS_PATHS = extraCompatPaths;
}) // (lib.optionalAttrs cfg.extest.enable {
LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so";
}) // (prev.extraEnv or {});
extraLibraries = pkgs: let
prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
additionalLibs = with config.hardware.graphics;
if pkgs.stdenv.hostPlatform.is64bit
then [ package ] ++ extraPackages
else [ package32 ] ++ extraPackages32;
in prevLibs ++ additionalLibs;
extraPkgs = p: (cfg.extraPackages ++ lib.optionals (prev ? extraPkgs) (prev.extraPkgs p));
} // lib.optionalAttrs (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice)
{
buildFHSEnv = pkgs.buildFHSEnv.override {
# use the setuid wrapped bubblewrap
bubblewrap = "${config.security.wrapperDir}/..";
};
});
apply =
steam:
steam.override (
prev:
{
extraEnv =
(lib.optionalAttrs (cfg.extraCompatPackages != [ ]) {
STEAM_EXTRA_COMPAT_TOOLS_PATHS = extraCompatPaths;
})
// (lib.optionalAttrs cfg.extest.enable {
LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so";
})
// (prev.extraEnv or { });
extraLibraries =
pkgs:
let
prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
additionalLibs =
with config.hardware.graphics;
if pkgs.stdenv.hostPlatform.is64bit then
[ package ] ++ extraPackages
else
[ package32 ] ++ extraPackages32;
in
prevLibs ++ additionalLibs;
extraPkgs = p: (cfg.extraPackages ++ lib.optionals (prev ? extraPkgs) (prev.extraPkgs p));
}
// lib.optionalAttrs (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
buildFHSEnv = pkgs.buildFHSEnv.override {
# use the setuid wrapped bubblewrap
bubblewrap = "${config.security.wrapperDir}/..";
};
}
);
description = ''
The Steam package to use. Additional libraries are added from the system
configuration to ensure graphics work properly.
@ -141,7 +166,7 @@ in {
gamescopeSession = lib.mkOption {
description = "Run a GameScope driven Steam session from your display-manager";
default = {};
default = { };
type = lib.types.submodule {
options = {
enable = lib.mkEnableOption "GameScope Session";
@ -187,7 +212,8 @@ in {
};
config = lib.mkIf cfg.enable {
hardware.graphics = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
hardware.graphics = {
# this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
enable = true;
enable32Bit = true;
};
@ -205,7 +231,9 @@ in {
programs.steam.extraPackages = cfg.fontPackages;
programs.gamescope.enable = lib.mkDefault cfg.gamescopeSession.enable;
services.displayManager.sessionPackages = lib.mkIf cfg.gamescopeSession.enable [ gamescopeSessionFile ];
services.displayManager.sessionPackages = lib.mkIf cfg.gamescopeSession.enable [
gamescopeSessionFile
];
# enable 32bit pulseaudio/pipewire support if needed
services.pulseaudio.support32Bit = config.services.pulseaudio.enable;
@ -213,11 +241,15 @@ in {
hardware.steam-hardware.enable = true;
environment.systemPackages = [
cfg.package
cfg.package.run
] ++ lib.optional cfg.gamescopeSession.enable steam-gamescope
++ lib.optional cfg.protontricks.enable (cfg.protontricks.package.override { inherit extraCompatPaths; });
environment.systemPackages =
[
cfg.package
cfg.package.run
]
++ lib.optional cfg.gamescopeSession.enable steam-gamescope
++ lib.optional cfg.protontricks.enable (
cfg.protontricks.package.override { inherit extraCompatPaths; }
);
networking.firewall = lib.mkMerge [
(lib.mkIf (cfg.remotePlay.openFirewall || cfg.localNetworkGameTransfers.openFirewall) {
@ -226,7 +258,12 @@ in {
(lib.mkIf cfg.remotePlay.openFirewall {
allowedTCPPorts = [ 27036 ];
allowedUDPPortRanges = [ { from = 27031; to = 27035; } ];
allowedUDPPortRanges = [
{
from = 27031;
to = 27035;
}
];
})
(lib.mkIf cfg.dedicatedServer.openFirewall {

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.programs.sway;
@ -14,33 +19,43 @@ in
<https://github.com/swaywm/sway/wiki> and
"man 5 sway" for more information'';
package = lib.mkPackageOption pkgs "sway" {
nullable = true;
extraDescription = ''
If the package is not overridable with `extraSessionCommands`, `extraOptions`,
`withBaseWrapper`, `withGtkWrapper`, `enableXWayland` and `isNixOS`,
then the module options {option}`wrapperFeatures`, {option}`extraSessionCommands`,
{option}`extraOptions` and {option}`xwayland` will have no effect.
package =
lib.mkPackageOption pkgs "sway" {
nullable = true;
extraDescription = ''
If the package is not overridable with `extraSessionCommands`, `extraOptions`,
`withBaseWrapper`, `withGtkWrapper`, `enableXWayland` and `isNixOS`,
then the module options {option}`wrapperFeatures`, {option}`extraSessionCommands`,
{option}`extraOptions` and {option}`xwayland` will have no effect.
Set to `null` to not add any Sway package to your path.
This should be done if you want to use the Home Manager Sway module to install Sway.
'';
} // {
apply = p: if p == null then null else
wayland-lib.genFinalPackage p {
extraSessionCommands = cfg.extraSessionCommands;
extraOptions = cfg.extraOptions;
withBaseWrapper = cfg.wrapperFeatures.base;
withGtkWrapper = cfg.wrapperFeatures.gtk;
enableXWayland = cfg.xwayland.enable;
isNixOS = true;
};
};
Set to `null` to not add any Sway package to your path.
This should be done if you want to use the Home Manager Sway module to install Sway.
'';
}
// {
apply =
p:
if p == null then
null
else
wayland-lib.genFinalPackage p {
extraSessionCommands = cfg.extraSessionCommands;
extraOptions = cfg.extraOptions;
withBaseWrapper = cfg.wrapperFeatures.base;
withGtkWrapper = cfg.wrapperFeatures.gtk;
enableXWayland = cfg.xwayland.enable;
isNixOS = true;
};
};
wrapperFeatures = {
base = lib.mkEnableOption ''
the base wrapper to execute extra session commands and prepend a
dbus-run-session to the sway command'' // { default = true; };
base =
lib.mkEnableOption ''
the base wrapper to execute extra session commands and prepend a
dbus-run-session to the sway command''
// {
default = true;
};
gtk = lib.mkEnableOption ''
the wrapGAppsHook wrapper to execute sway with required environment
variables for GTK applications'';
@ -69,7 +84,7 @@ in
extraOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
default = [ ];
example = [
"--verbose"
"--debug"
@ -81,12 +96,22 @@ in
'';
};
xwayland.enable = lib.mkEnableOption "XWayland" // { default = true; };
xwayland.enable = lib.mkEnableOption "XWayland" // {
default = true;
};
extraPackages = lib.mkOption {
type = with lib.types; listOf package;
# Packages used in default config
default = with pkgs; [ brightnessctl foot grim pulseaudio swayidle swaylock wmenu ];
default = with pkgs; [
brightnessctl
foot
grim
pulseaudio
swayidle
swaylock
wmenu
];
defaultText = lib.literalExpression ''
with pkgs; [ brightnessctl foot grim pulseaudio swayidle swaylock wmenu ];
'';
@ -102,18 +127,19 @@ in
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
assertions = [
{
assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
message = ''
The extraSessionCommands for Sway will not be run if wrapperFeatures.base is disabled.
'';
}
];
config = lib.mkIf cfg.enable (
lib.mkMerge [
{
assertions = [
{
assertion = cfg.extraSessionCommands != "" -> cfg.wrapperFeatures.base;
message = ''
The extraSessionCommands for Sway will not be run if wrapperFeatures.base is disabled.
'';
}
];
warnings =
warnings =
lib.mkIf
(
(lib.elem "nvidia" config.services.xserver.videoDrivers)
@ -123,58 +149,64 @@ in
"Using Sway with Nvidia driver version <= 550 may result in a broken system. Configure hardware.nvidia.package to use a newer version."
];
environment = {
systemPackages = lib.optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
environment = {
systemPackages = lib.optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
# Needed for the default wallpaper:
pathsToLink = lib.optional (cfg.package != null) "/share/backgrounds/sway";
# Needed for the default wallpaper:
pathsToLink = lib.optional (cfg.package != null) "/share/backgrounds/sway";
etc = {
"sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
# Import the most important environment variables into the D-Bus and systemd
# user environments (e.g. required for screen sharing and Pinentry prompts):
exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
# enable systemd-integration
exec "systemctl --user import-environment {,WAYLAND_}DISPLAY SWAYSOCK; systemctl --user start sway-session.target"
exec swaymsg -t subscribe '["shutdown"]' && systemctl --user stop sway-session.target
'';
} // lib.optionalAttrs (cfg.package != null) {
"sway/config".source = lib.mkOptionDefault "${cfg.package}/etc/sway/config";
etc =
{
"sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
# Import the most important environment variables into the D-Bus and systemd
# user environments (e.g. required for screen sharing and Pinentry prompts):
exec dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY SWAYSOCK XDG_CURRENT_DESKTOP
# enable systemd-integration
exec "systemctl --user import-environment {,WAYLAND_}DISPLAY SWAYSOCK; systemctl --user start sway-session.target"
exec swaymsg -t subscribe '["shutdown"]' && systemctl --user stop sway-session.target
'';
}
// lib.optionalAttrs (cfg.package != null) {
"sway/config".source = lib.mkOptionDefault "${cfg.package}/etc/sway/config";
};
};
};
systemd.user.targets.sway-session = {
description = "sway compositor session";
documentation = [ "man:systemd.special(7)" ];
bindsTo = [ "graphical-session.target" ];
wants = [ "graphical-session-pre.target" ];
after = [ "graphical-session-pre.target" ];
};
systemd.user.targets.sway-session = {
description = "sway compositor session";
documentation = [ "man:systemd.special(7)" ];
bindsTo = [ "graphical-session.target" ];
wants = [ "graphical-session-pre.target" ];
after = [ "graphical-session-pre.target" ];
};
# To make a Sway session available if a display manager like SDDM is enabled:
services.displayManager.sessionPackages = lib.optional (cfg.package != null) cfg.package;
# To make a Sway session available if a display manager like SDDM is enabled:
services.displayManager.sessionPackages = lib.optional (cfg.package != null) cfg.package;
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
# https://github.com/emersion/xdg-desktop-portal-wlr/blob/master/contrib/wlroots-portals.conf
# https://github.com/emersion/xdg-desktop-portal-wlr/pull/315
xdg.portal.config.sway = {
# Use xdg-desktop-portal-gtk for every portal interface...
default = [ "gtk" ];
# ... except for the ScreenCast, Screenshot and Secret
"org.freedesktop.impl.portal.ScreenCast" = "wlr";
"org.freedesktop.impl.portal.Screenshot" = "wlr";
# ignore inhibit bc gtk portal always returns as success,
# despite sway/the wlr portal not having an implementation,
# stopping firefox from using wayland idle-inhibit
"org.freedesktop.impl.portal.Inhibit" = "none";
};
}
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
# https://github.com/emersion/xdg-desktop-portal-wlr/blob/master/contrib/wlroots-portals.conf
# https://github.com/emersion/xdg-desktop-portal-wlr/pull/315
xdg.portal.config.sway = {
# Use xdg-desktop-portal-gtk for every portal interface...
default = [ "gtk" ];
# ... except for the ScreenCast, Screenshot and Secret
"org.freedesktop.impl.portal.ScreenCast" = "wlr";
"org.freedesktop.impl.portal.Screenshot" = "wlr";
# ignore inhibit bc gtk portal always returns as success,
# despite sway/the wlr portal not having an implementation,
# stopping firefox from using wayland idle-inhibit
"org.freedesktop.impl.portal.Inhibit" = "none";
};
}
(import ./wayland-session.nix {
inherit lib pkgs;
enableXWayland = cfg.xwayland.enable;
})
]);
(import ./wayland-session.nix {
inherit lib pkgs;
enableXWayland = cfg.xwayland.enable;
})
]
);
meta.maintainers = with lib.maintainers; [ primeos colemickens ];
meta.maintainers = with lib.maintainers; [
primeos
colemickens
];
}

View File

@ -1,6 +1,11 @@
# This module defines global configuration for the xonsh.
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
@ -45,7 +50,9 @@ in
example = lib.literalExpression ''
ps: with ps; [ numpy xonsh.xontribs.xontrib-vox ]
'';
type = with lib.types; coercedTo (listOf lib.types.package) (v: (_: v)) (functionTo (listOf lib.types.package));
type =
with lib.types;
coercedTo (listOf lib.types.package) (v: (_: v)) (functionTo (listOf lib.types.package));
description = ''
Xontribs and extra Python packages to be available in xonsh.
'';

View File

@ -1,6 +1,12 @@
# This module defines global configuration for the zshell.
{ config, lib, options, pkgs, ... }:
{
config,
lib,
options,
pkgs,
...
}:
let
@ -10,8 +16,9 @@ let
opt = options.programs.zsh;
zshAliases = builtins.concatStringsSep "\n" (
lib.mapAttrsToList (k: v: "alias -- ${k}=${lib.escapeShellArg v}")
(lib.filterAttrs (k: v: v != null) cfg.shellAliases)
lib.mapAttrsToList (k: v: "alias -- ${k}=${lib.escapeShellArg v}") (
lib.filterAttrs (k: v: v != null) cfg.shellAliases
)
);
zshStartupNotes = ''
@ -121,7 +128,10 @@ in
"SHARE_HISTORY"
"HIST_FCNTL_LOCK"
];
example = [ "EXTENDED_HISTORY" "RM_STAR_WAIT" ];
example = [
"EXTENDED_HISTORY"
"RM_STAR_WAIT"
];
description = ''
Configure zsh options. See
{manpage}`zshoptions(1)`.
@ -173,144 +183,141 @@ in
programs.zsh.shellAliases = builtins.mapAttrs (name: lib.mkDefault) cfge.shellAliases;
environment.etc.zshenv.text =
''
# /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
# This file is read for all shells.
environment.etc.zshenv.text = ''
# /etc/zshenv: DO NOT EDIT -- this file has been generated automatically.
# This file is read for all shells.
# Only execute this file once per shell.
if [ -n "''${__ETC_ZSHENV_SOURCED-}" ]; then return; fi
__ETC_ZSHENV_SOURCED=1
# Only execute this file once per shell.
if [ -n "''${__ETC_ZSHENV_SOURCED-}" ]; then return; fi
__ETC_ZSHENV_SOURCED=1
if [ -z "''${__NIXOS_SET_ENVIRONMENT_DONE-}" ]; then
. ${config.system.build.setEnvironment}
fi
if [ -z "''${__NIXOS_SET_ENVIRONMENT_DONE-}" ]; then
. ${config.system.build.setEnvironment}
fi
HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
HELPDIR="${pkgs.zsh}/share/zsh/$ZSH_VERSION/help"
# Tell zsh how to find installed completions.
for p in ''${(z)NIX_PROFILES}; do
fpath=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions $fpath)
done
# Tell zsh how to find installed completions.
for p in ''${(z)NIX_PROFILES}; do
fpath=($p/share/zsh/site-functions $p/share/zsh/$ZSH_VERSION/functions $p/share/zsh/vendor-completions $fpath)
done
# Setup custom shell init stuff.
${cfge.shellInit}
# Setup custom shell init stuff.
${cfge.shellInit}
${cfg.shellInit}
${cfg.shellInit}
# Read system-wide modifications.
if test -f /etc/zshenv.local; then
. /etc/zshenv.local
fi
'';
# Read system-wide modifications.
if test -f /etc/zshenv.local; then
. /etc/zshenv.local
fi
'';
environment.etc.zprofile.text =
''
# /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
# This file is read for login shells.
#
${zshStartupNotes}
environment.etc.zprofile.text = ''
# /etc/zprofile: DO NOT EDIT -- this file has been generated automatically.
# This file is read for login shells.
#
${zshStartupNotes}
# Only execute this file once per shell.
if [ -n "''${__ETC_ZPROFILE_SOURCED-}" ]; then return; fi
__ETC_ZPROFILE_SOURCED=1
# Only execute this file once per shell.
if [ -n "''${__ETC_ZPROFILE_SOURCED-}" ]; then return; fi
__ETC_ZPROFILE_SOURCED=1
# Setup custom login shell init stuff.
${cfge.loginShellInit}
# Setup custom login shell init stuff.
${cfge.loginShellInit}
${cfg.loginShellInit}
${cfg.loginShellInit}
# Read system-wide modifications.
if test -f /etc/zprofile.local; then
. /etc/zprofile.local
fi
'';
# Read system-wide modifications.
if test -f /etc/zprofile.local; then
. /etc/zprofile.local
fi
'';
environment.etc.zshrc.text =
''
# /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
# This file is read for interactive shells.
#
${zshStartupNotes}
environment.etc.zshrc.text = ''
# /etc/zshrc: DO NOT EDIT -- this file has been generated automatically.
# This file is read for interactive shells.
#
${zshStartupNotes}
# Only execute this file once per shell.
if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
__ETC_ZSHRC_SOURCED=1
# Only execute this file once per shell.
if [ -n "$__ETC_ZSHRC_SOURCED" -o -n "$NOSYSZSHRC" ]; then return; fi
__ETC_ZSHRC_SOURCED=1
${lib.optionalString (cfg.setOptions != []) ''
# Set zsh options.
setopt ${builtins.concatStringsSep " " cfg.setOptions}
''}
${lib.optionalString (cfg.setOptions != [ ]) ''
# Set zsh options.
setopt ${builtins.concatStringsSep " " cfg.setOptions}
''}
# Alternative method of determining short and full hostname.
HOST=${config.networking.fqdnOrHostName}
# Alternative method of determining short and full hostname.
HOST=${config.networking.fqdnOrHostName}
# Setup command line history.
# Don't export these, otherwise other shells (bash) will try to use same HISTFILE.
SAVEHIST=${builtins.toString cfg.histSize}
HISTSIZE=${builtins.toString cfg.histSize}
HISTFILE=${cfg.histFile}
# Setup command line history.
# Don't export these, otherwise other shells (bash) will try to use same HISTFILE.
SAVEHIST=${builtins.toString cfg.histSize}
HISTSIZE=${builtins.toString cfg.histSize}
HISTFILE=${cfg.histFile}
# Configure sane keyboard defaults.
. /etc/zinputrc
# Configure sane keyboard defaults.
. /etc/zinputrc
${lib.optionalString cfg.enableGlobalCompInit ''
# Enable autocompletion.
autoload -U compinit && compinit
''}
${lib.optionalString cfg.enableGlobalCompInit ''
# Enable autocompletion.
autoload -U compinit && compinit
''}
${lib.optionalString cfg.enableBashCompletion ''
# Enable compatibility with bash's completion system.
autoload -U bashcompinit && bashcompinit
''}
${lib.optionalString cfg.enableBashCompletion ''
# Enable compatibility with bash's completion system.
autoload -U bashcompinit && bashcompinit
''}
# Setup custom interactive shell init stuff.
${cfge.interactiveShellInit}
# Setup custom interactive shell init stuff.
${cfge.interactiveShellInit}
${cfg.interactiveShellInit}
${cfg.interactiveShellInit}
${lib.optionalString cfg.enableLsColors ''
# Extra colors for directory listings.
eval "$(${pkgs.coreutils}/bin/dircolors -b)"
''}
${lib.optionalString cfg.enableLsColors ''
# Extra colors for directory listings.
eval "$(${pkgs.coreutils}/bin/dircolors -b)"
''}
# Setup aliases.
${zshAliases}
# Setup aliases.
${zshAliases}
# Setup prompt.
${cfg.promptInit}
# Setup prompt.
${cfg.promptInit}
# Disable some features to support TRAMP.
if [ "$TERM" = dumb ]; then
unsetopt zle prompt_cr prompt_subst
unset RPS1 RPROMPT
PS1='$ '
PROMPT='$ '
fi
# Disable some features to support TRAMP.
if [ "$TERM" = dumb ]; then
unsetopt zle prompt_cr prompt_subst
unset RPS1 RPROMPT
PS1='$ '
PROMPT='$ '
fi
# Read system-wide modifications.
if test -f /etc/zshrc.local; then
. /etc/zshrc.local
fi
'';
# Read system-wide modifications.
if test -f /etc/zshrc.local; then
. /etc/zshrc.local
fi
'';
# Bug in nix flakes:
# If we use `.source` here the path is garbage collected also we point to it with a symlink
# see https://github.com/NixOS/nixpkgs/issues/132732
environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
environment.systemPackages = [ pkgs.zsh ]
++ lib.optional cfg.enableCompletion pkgs.nix-zsh-completions;
environment.systemPackages = [
pkgs.zsh
] ++ lib.optional cfg.enableCompletion pkgs.nix-zsh-completions;
environment.pathsToLink = lib.optional cfg.enableCompletion "/share/zsh";
#users.defaultUserShell = lib.mkDefault "/run/current-system/sw/bin/zsh";
environment.shells =
[
"/run/current-system/sw/bin/zsh"
"${pkgs.zsh}/bin/zsh"
];
environment.shells = [
"/run/current-system/sw/bin/zsh"
"${pkgs.zsh}/bin/zsh"
];
};

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,30 @@
lib:
{ cert, groups, services }:
{
cert,
groups,
services,
}:
let
catSep = builtins.concatStringsSep;
svcUser = svc: svc.serviceConfig.User or "root";
svcGroups = svc:
svcGroups =
svc:
(lib.optional (svc.serviceConfig ? Group) svc.serviceConfig.Group)
++ lib.toList (svc.serviceConfig.SupplementaryGroups or [ ]);
in
{
assertion = builtins.all (svc:
assertion = builtins.all (
svc:
svcUser svc == "root"
|| builtins.elem (svcUser svc) groups.${cert.group}.members
|| builtins.elem cert.group (svcGroups svc)
) services;
message = "Certificate ${cert.domain} (group=${cert.group}) must be readable by service(s) ${
catSep ", " (map (svc: "${svc.name} (user=${svcUser svc} groups=${catSep "," (svcGroups svc)})") services)
catSep ", " (
map (svc: "${svc.name} (user=${svcUser svc} groups=${catSep "," (svcGroups svc)})") services
)
}";
}

View File

@ -4,12 +4,10 @@
pkgs,
...
}:
with lib; let
with lib;
let
cfg = config.security.ipa;
pyBool = x:
if x
then "True"
else "False";
pyBool = x: if x then "True" else "False";
ldapConf = pkgs.writeText "ldap.conf" ''
# Turning this off breaks GSSAPI used with krb5 when rdns = false
@ -21,14 +19,16 @@ with lib; let
'';
nssDb =
pkgs.runCommand "ipa-nssdb"
{
nativeBuildInputs = [pkgs.nss.tools];
} ''
mkdir -p $out
certutil -d $out -N --empty-password
certutil -d $out -A --empty-password -n "${cfg.realm} IPA CA" -t CT,C,C -i ${cfg.certificate}
'';
in {
{
nativeBuildInputs = [ pkgs.nss.tools ];
}
''
mkdir -p $out
certutil -d $out -N --empty-password
certutil -d $out -A --empty-password -n "${cfg.realm} IPA CA" -t CT,C,C -i ${cfg.certificate}
'';
in
{
options = {
security.ipa = {
enable = mkEnableOption "FreeIPA domain integration";
@ -88,8 +88,11 @@ in {
ipaHostname = mkOption {
type = types.str;
example = "myworkstation.example.com";
default = if config.networking.domain != null then config.networking.fqdn
else "${config.networking.hostName}.${cfg.domain}";
default =
if config.networking.domain != null then
config.networking.fqdn
else
"${config.networking.hostName}.${cfg.domain}";
defaultText = literalExpression ''
if config.networking.domain != null then config.networking.fqdn
else "''${networking.hostName}.''${security.ipa.domain}"
@ -99,7 +102,7 @@ in {
ifpAllowedUids = mkOption {
type = types.listOf types.str;
default = ["root"];
default = [ "root" ];
description = "A list of users allowed to access the ifp dbus interface.";
};
@ -138,7 +141,10 @@ in {
}
];
environment.systemPackages = with pkgs; [krb5Full freeipa];
environment.systemPackages = with pkgs; [
krb5Full
freeipa
];
environment.etc = {
"ipa/default.conf".text = ''
@ -195,7 +201,10 @@ in {
systemd.services."ipa-activation" = {
wantedBy = [ "sysinit.target" ];
before = [ "sysinit.target" "shutdown.target" ];
before = [
"sysinit.target"
"shutdown.target"
];
conflicts = [ "shutdown.target" ];
unitConfig.DefaultDependencies = false;
serviceConfig.Type = "oneshot";
@ -234,8 +243,7 @@ in {
cache_credentials = ${pyBool cfg.cacheCredentials}
krb5_store_password_if_offline = ${pyBool cfg.offlinePasswords}
${optionalString ((toLower cfg.domain) != (toLower cfg.realm))
"krb5_realm = ${cfg.realm}"}
${optionalString ((toLower cfg.domain) != (toLower cfg.realm)) "krb5_realm = ${cfg.realm}"}
dyndns_update = ${pyBool cfg.dyndns.enable}
dyndns_iface = ${cfg.dyndns.interface}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,13 @@
# A module for rtkit, a DBus system service that hands out realtime
# scheduling priority to processes that ask for it.
{ config, lib, pkgs, utils, ... }:
{
config,
lib,
pkgs,
utils,
...
}:
with lib;
@ -9,7 +15,8 @@ let
cfg = config.security.rtkit;
package = pkgs.rtkit;
in {
in
{
options = {
@ -26,7 +33,7 @@ in {
security.rtkit.args = mkOption {
type = types.listOf types.str;
default = [];
default = [ ];
description = ''
Command-line options for `rtkit-daemon`.
'';
@ -38,7 +45,6 @@ in {
};
config = mkIf cfg.enable {
security.polkit.enable = true;
@ -52,18 +58,17 @@ in {
systemd.services.rtkit-daemon = {
serviceConfig.ExecStart = [
"" # Resets command from upstream unit.
"" # Resets command from upstream unit.
"${package}/libexec/rtkit-daemon ${utils.escapeSystemdExecArgs cfg.args}"
];
};
users.users.rtkit =
{
isSystemUser = true;
group = "rtkit";
description = "RealtimeKit daemon";
};
users.groups.rtkit = {};
users.users.rtkit = {
isSystemUser = true;
group = "rtkit";
description = "RealtimeKit daemon";
};
users.groups.rtkit = { };
};

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (config.security) wrapperDir;
@ -11,26 +16,30 @@ let
# musl is security-focused and generally more minimal, so it's a better choice here.
# The dynamic linker is still a fairly complex piece of code, and the wrappers are
# quite small, so linking it statically is more appropriate.
securityWrapper = sourceProg : pkgs.pkgsStatic.callPackage ./wrapper.nix {
inherit sourceProg;
securityWrapper =
sourceProg:
pkgs.pkgsStatic.callPackage ./wrapper.nix {
inherit sourceProg;
# glibc definitions of insecure environment variables
#
# We extract the single header file we need into its own derivation,
# so that we don't have to pull full glibc sources to build wrappers.
#
# They're taken from pkgs.glibc so that we don't have to keep as close
# an eye on glibc changes. Not every relevant variable is in this header,
# so we maintain a slightly stricter list in wrapper.c itself as well.
unsecvars = lib.overrideDerivation (pkgs.srcOnly pkgs.glibc)
({ name, ... }: {
name = "${name}-unsecvars";
installPhase = ''
mkdir $out
cp sysdeps/generic/unsecvars.h $out
'';
});
};
# glibc definitions of insecure environment variables
#
# We extract the single header file we need into its own derivation,
# so that we don't have to pull full glibc sources to build wrappers.
#
# They're taken from pkgs.glibc so that we don't have to keep as close
# an eye on glibc changes. Not every relevant variable is in this header,
# so we maintain a slightly stricter list in wrapper.c itself as well.
unsecvars = lib.overrideDerivation (pkgs.srcOnly pkgs.glibc) (
{ name, ... }:
{
name = "${name}-unsecvars";
installPhase = ''
mkdir $out
cp sysdeps/generic/unsecvars.h $out
'';
}
);
};
fileModeType =
let
@ -39,45 +48,46 @@ let
numeric = "[-+=]?[0-7]{0,4}";
mode = "((${symbolic})(,${symbolic})*)|(${numeric})";
in
lib.types.strMatching mode
// { description = "file mode string"; };
lib.types.strMatching mode // { description = "file mode string"; };
wrapperType = lib.types.submodule ({ name, config, ... }: {
options.enable = lib.mkOption
{ type = lib.types.bool;
wrapperType = lib.types.submodule (
{ name, config, ... }:
{
options.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to enable the wrapper.";
};
options.source = lib.mkOption
{ type = lib.types.path;
options.source = lib.mkOption {
type = lib.types.path;
description = "The absolute path to the program to be wrapped.";
};
options.program = lib.mkOption
{ type = with lib.types; nullOr str;
options.program = lib.mkOption {
type = with lib.types; nullOr str;
default = name;
description = ''
The name of the wrapper program. Defaults to the attribute name.
'';
};
options.owner = lib.mkOption
{ type = lib.types.str;
options.owner = lib.mkOption {
type = lib.types.str;
description = "The owner of the wrapper program.";
};
options.group = lib.mkOption
{ type = lib.types.str;
options.group = lib.mkOption {
type = lib.types.str;
description = "The group of the wrapper program.";
};
options.permissions = lib.mkOption
{ type = fileModeType;
default = "u+rx,g+x,o+x";
options.permissions = lib.mkOption {
type = fileModeType;
default = "u+rx,g+x,o+x";
example = "a+rx";
description = ''
The permissions of the wrapper program. The format is that of a
symbolic or numeric file mode understood by {command}`chmod`.
'';
};
options.capabilities = lib.mkOption
{ type = lib.types.commas;
options.capabilities = lib.mkOption {
type = lib.types.commas;
default = "";
description = ''
A comma-separated list of capability clauses to be given to the
@ -96,27 +106,29 @@ let
:::
'';
};
options.setuid = lib.mkOption
{ type = lib.types.bool;
options.setuid = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to add the setuid bit the wrapper program.";
};
options.setgid = lib.mkOption
{ type = lib.types.bool;
options.setgid = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to add the setgid bit the wrapper program.";
};
});
}
);
###### Activation script for the setcap wrappers
mkSetcapProgram =
{ program
, capabilities
, source
, owner
, group
, permissions
, ...
{
program,
capabilities,
source,
owner,
group,
permissions,
...
}:
''
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
@ -136,14 +148,15 @@ let
###### Activation script for the setuid wrappers
mkSetuidProgram =
{ program
, source
, owner
, group
, setuid
, setgid
, permissions
, ...
{
program,
source,
owner,
group,
setuid,
setgid,
permissions,
...
}:
''
cp ${securityWrapper source}/bin/security-wrapper "$wrapperDir/${program}"
@ -155,13 +168,9 @@ let
chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" "$wrapperDir/${program}"
'';
mkWrappedPrograms =
builtins.map
(opts:
if opts.capabilities != ""
then mkSetcapProgram opts
else mkSetuidProgram opts
) (lib.attrValues wrappers);
mkWrappedPrograms = builtins.map (
opts: if opts.capabilities != "" then mkSetcapProgram opts else mkSetuidProgram opts
) (lib.attrValues wrappers);
in
{
imports = [
@ -178,35 +187,34 @@ in
security.wrappers = lib.mkOption {
type = lib.types.attrsOf wrapperType;
default = {};
example = lib.literalExpression
''
{
# a setuid root program
doas =
{ setuid = true;
owner = "root";
group = "root";
source = "''${pkgs.doas}/bin/doas";
};
default = { };
example = lib.literalExpression ''
{
# a setuid root program
doas =
{ setuid = true;
owner = "root";
group = "root";
source = "''${pkgs.doas}/bin/doas";
};
# a setgid program
locate =
{ setgid = true;
owner = "root";
group = "mlocate";
source = "''${pkgs.locate}/bin/locate";
};
# a setgid program
locate =
{ setgid = true;
owner = "root";
group = "mlocate";
source = "''${pkgs.locate}/bin/locate";
};
# a program with the CAP_NET_RAW capability
ping =
{ owner = "root";
group = "root";
capabilities = "cap_net_raw+ep";
source = "''${pkgs.iputils.out}/bin/ping";
};
}
'';
# a program with the CAP_NET_RAW capability
ping =
{ owner = "root";
group = "root";
capabilities = "cap_net_raw+ep";
source = "''${pkgs.iputils.out}/bin/ping";
};
}
'';
description = ''
This option effectively allows adding setuid/setgid bits, capabilities,
changing file ownership and permissions of a program without directly
@ -226,9 +234,9 @@ in
};
security.wrapperDir = lib.mkOption {
type = lib.types.path;
default = "/run/wrappers/bin";
internal = true;
type = lib.types.path;
default = "/run/wrappers/bin";
internal = true;
description = ''
This option defines the path to the wrapper programs. It
should not be overridden.
@ -239,29 +247,28 @@ in
###### implementation
config = lib.mkIf config.security.enableWrappers {
assertions = lib.mapAttrsToList
(name: opts:
{ assertion = opts.setuid || opts.setgid -> opts.capabilities == "";
message = ''
The security.wrappers.${name} wrapper is not valid:
setuid/setgid and capabilities are mutually exclusive.
'';
}
) wrappers;
assertions = lib.mapAttrsToList (name: opts: {
assertion = opts.setuid || opts.setgid -> opts.capabilities == "";
message = ''
The security.wrappers.${name} wrapper is not valid:
setuid/setgid and capabilities are mutually exclusive.
'';
}) wrappers;
security.wrappers =
let
mkSetuidRoot = source:
{ setuid = true;
owner = "root";
group = "root";
inherit source;
};
mkSetuidRoot = source: {
setuid = true;
owner = "root";
group = "root";
inherit source;
};
in
{ # These are mount related wrappers that require the +s permission.
fusermount = mkSetuidRoot "${lib.getBin pkgs.fuse}/bin/fusermount";
{
# These are mount related wrappers that require the +s permission.
fusermount = mkSetuidRoot "${lib.getBin pkgs.fuse}/bin/fusermount";
fusermount3 = mkSetuidRoot "${lib.getBin pkgs.fuse3}/bin/fusermount3";
mount = mkSetuidRoot "${lib.getBin pkgs.util-linux}/bin/mount";
mount = mkSetuidRoot "${lib.getBin pkgs.util-linux}/bin/mount";
umount = mkSetuidRoot "${lib.getBin pkgs.util-linux}/bin/umount";
};
@ -272,33 +279,45 @@ in
export PATH="${wrapperDir}:$PATH"
'';
security.apparmor.includes = lib.mapAttrs' (wrapName: wrap: lib.nameValuePair
"nixos/security.wrappers/${wrapName}" ''
include "${pkgs.apparmorRulesFromClosure { name="security.wrappers.${wrapName}"; } [
(securityWrapper wrap.source)
]}"
mrpx ${wrap.source},
'') wrappers;
security.apparmor.includes = lib.mapAttrs' (
wrapName: wrap:
lib.nameValuePair "nixos/security.wrappers/${wrapName}" ''
include "${
pkgs.apparmorRulesFromClosure { name = "security.wrappers.${wrapName}"; } [
(securityWrapper wrap.source)
]
}"
mrpx ${wrap.source},
''
) wrappers;
systemd.mounts = [{
where = parentWrapperDir;
what = "tmpfs";
type = "tmpfs";
options = lib.concatStringsSep "," ([
"nodev"
"mode=755"
"size=${config.security.wrapperDirSize}"
]);
}];
systemd.mounts = [
{
where = parentWrapperDir;
what = "tmpfs";
type = "tmpfs";
options = lib.concatStringsSep "," ([
"nodev"
"mode=755"
"size=${config.security.wrapperDirSize}"
]);
}
];
systemd.services.suid-sgid-wrappers = {
description = "Create SUID/SGID Wrappers";
wantedBy = [ "sysinit.target" ];
before = [ "sysinit.target" "shutdown.target" ];
before = [
"sysinit.target"
"shutdown.target"
];
conflicts = [ "shutdown.target" ];
after = [ "systemd-sysusers.service" ];
unitConfig.DefaultDependencies = false;
unitConfig.RequiresMountsFor = [ "/nix/store" "/run/wrappers" ];
unitConfig.RequiresMountsFor = [
"/nix/store"
"/run/wrappers"
];
serviceConfig.Type = "oneshot";
script = ''
chmod 755 "${parentWrapperDir}"
@ -327,31 +346,34 @@ in
};
###### wrappers consistency checks
system.checks = lib.singleton (pkgs.runCommand "ensure-all-wrappers-paths-exist" {
preferLocalBuild = true;
} ''
# make sure we produce output
mkdir -p $out
system.checks = lib.singleton (
pkgs.runCommand "ensure-all-wrappers-paths-exist"
{
preferLocalBuild = true;
}
''
# make sure we produce output
mkdir -p $out
echo -n "Checking that Nix store paths of all wrapped programs exist... "
echo -n "Checking that Nix store paths of all wrapped programs exist... "
declare -A wrappers
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v:
"wrappers['${n}']='${v.source}'") wrappers)}
declare -A wrappers
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "wrappers['${n}']='${v.source}'") wrappers)}
for name in "''${!wrappers[@]}"; do
path="''${wrappers[$name]}"
if [[ "$path" =~ /nix/store ]] && [ ! -e "$path" ]; then
test -t 1 && echo -ne '\033[1;31m'
echo "FAIL"
echo "The path $path does not exist!"
echo 'Please, check the value of `security.wrappers."'$name'".source`.'
test -t 1 && echo -ne '\033[0m'
exit 1
fi
done
for name in "''${!wrappers[@]}"; do
path="''${wrappers[$name]}"
if [[ "$path" =~ /nix/store ]] && [ ! -e "$path" ]; then
test -t 1 && echo -ne '\033[1;31m'
echo "FAIL"
echo "The path $path does not exist!"
echo 'Please, check the value of `security.wrappers."'$name'".source`.'
test -t 1 && echo -ne '\033[0m'
exit 1
fi
done
echo "OK"
'');
echo "OK"
''
);
};
}

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.gonic;
settingsFormat = pkgs.formats.keyValue {
@ -42,7 +47,9 @@ in
ExecStart =
let
# these values are null by default but should not appear in the final config
filteredSettings = lib.filterAttrs (n: v: !((n == "tls-cert" || n == "tls-key") && v == null)) cfg.settings;
filteredSettings = lib.filterAttrs (
n: v: !((n == "tls-cert" || n == "tls-key") && v == null)
) cfg.settings;
in
"${pkgs.gonic}/bin/gonic -config-path ${settingsFormat.generate "gonic" filteredSettings}";
DynamicUser = true;
@ -56,16 +63,22 @@ in
cfg.settings.playlists-path
cfg.settings.podcast-path
];
BindReadOnlyPaths = [
# gonic can access scrobbling services
"-/etc/resolv.conf"
"${config.security.pki.caBundle}:/etc/ssl/certs/ca-certificates.crt"
builtins.storeDir
] ++ cfg.settings.music-path
++ lib.optional (cfg.settings.tls-cert != null) cfg.settings.tls-cert
++ lib.optional (cfg.settings.tls-key != null) cfg.settings.tls-key;
BindReadOnlyPaths =
[
# gonic can access scrobbling services
"-/etc/resolv.conf"
"${config.security.pki.caBundle}:/etc/ssl/certs/ca-certificates.crt"
builtins.storeDir
]
++ cfg.settings.music-path
++ lib.optional (cfg.settings.tls-cert != null) cfg.settings.tls-cert
++ lib.optional (cfg.settings.tls-key != null) cfg.settings.tls-key;
CapabilityBoundingSet = "";
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
PrivateDevices = true;
PrivateUsers = true;
@ -76,7 +89,10 @@ in
ProtectKernelModules = true;
ProtectKernelTunables = true;
SystemCallArchitectures = "native";
SystemCallFilter = [ "@system-service" "~@privileged" ];
SystemCallFilter = [
"@system-service"
"~@privileged"
];
RestrictRealtime = true;
LockPersonality = true;
MemoryDenyWriteExecute = true;

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
name = "mpd";
@ -7,13 +12,17 @@ let
gid = config.ids.gids.mpd;
cfg = config.services.mpd;
credentialsPlaceholder = (creds:
credentialsPlaceholder = (
creds:
let
placeholders = (lib.imap0
(i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"'')
creds);
placeholders = (
lib.imap0 (
i: c: ''password "{{password-${toString i}}}@${lib.concatStringsSep "," c.permissions}"''
) creds
);
in
lib.concatStringsSep "\n" placeholders);
lib.concatStringsSep "\n" placeholders
);
mpdConf = pkgs.writeText "mpd.conf" ''
# This file was automatically generated by NixOS. Edit mpd's configuration
@ -28,8 +37,10 @@ let
state_file "${cfg.dataDir}/state"
sticker_file "${cfg.dataDir}/sticker.sql"
${lib.optionalString (cfg.network.listenAddress != "any") ''bind_to_address "${cfg.network.listenAddress}"''}
${lib.optionalString (cfg.network.port != 6600) ''port "${toString cfg.network.port}"''}
${lib.optionalString (
cfg.network.listenAddress != "any"
) ''bind_to_address "${cfg.network.listenAddress}"''}
${lib.optionalString (cfg.network.port != 6600) ''port "${toString cfg.network.port}"''}
${lib.optionalString (cfg.fluidsynth) ''
decoder {
plugin "fluidsynth"
@ -37,12 +48,13 @@ let
}
''}
${lib.optionalString (cfg.credentials != []) (credentialsPlaceholder cfg.credentials)}
${lib.optionalString (cfg.credentials != [ ]) (credentialsPlaceholder cfg.credentials)}
${cfg.extraConfig}
'';
in {
in
{
###### interface
@ -160,33 +172,53 @@ in {
};
credentials = lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options = {
passwordFile = lib.mkOption {
type = lib.types.path;
description = ''
Path to file containing the password.
'';
type = lib.types.listOf (
lib.types.submodule {
options = {
passwordFile = lib.mkOption {
type = lib.types.path;
description = ''
Path to file containing the password.
'';
};
permissions =
let
perms = [
"read"
"add"
"control"
"admin"
];
in
lib.mkOption {
type = lib.types.listOf (lib.types.enum perms);
default = [ "read" ];
description = ''
List of permissions that are granted with this password.
Permissions can be "${lib.concatStringsSep "\", \"" perms}".
'';
};
};
permissions = let
perms = ["read" "add" "control" "admin"];
in lib.mkOption {
type = lib.types.listOf (lib.types.enum perms);
default = [ "read" ];
description = ''
List of permissions that are granted with this password.
Permissions can be "${lib.concatStringsSep "\", \"" perms}".
'';
};
};
});
}
);
description = ''
Credentials and permissions for accessing the mpd server.
'';
default = [];
default = [ ];
example = [
{passwordFile = "/var/lib/secrets/mpd_readonly_password"; permissions = [ "read" ];}
{passwordFile = "/var/lib/secrets/mpd_admin_password"; permissions = ["read" "add" "control" "admin"];}
{
passwordFile = "/var/lib/secrets/mpd_readonly_password";
permissions = [ "read" ];
}
{
passwordFile = "/var/lib/secrets/mpd_admin_password";
permissions = [
"read"
"add"
"control"
"admin"
];
}
];
};
@ -201,7 +233,6 @@ in {
};
###### implementation
config = lib.mkIf cfg.enable {
@ -212,10 +243,15 @@ in {
systemd.sockets.mpd = lib.mkIf cfg.startWhenNeeded {
wantedBy = [ "sockets.target" ];
listenStreams = [
"" # Note: this is needed to override the upstream unit
(if pkgs.lib.hasPrefix "/" cfg.network.listenAddress
then cfg.network.listenAddress
else "${lib.optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}")
"" # Note: this is needed to override the upstream unit
(
if pkgs.lib.hasPrefix "/" cfg.network.listenAddress then
cfg.network.listenAddress
else
"${
lib.optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"
}${toString cfg.network.port}"
)
];
};
@ -226,23 +262,36 @@ in {
''
set -euo pipefail
install -m 600 ${mpdConf} /run/mpd/mpd.conf
'' + lib.optionalString (cfg.credentials != [])
(lib.concatStringsSep "\n"
(lib.imap0
(i: c: ''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf'')
cfg.credentials));
''
+ lib.optionalString (cfg.credentials != [ ]) (
lib.concatStringsSep "\n" (
lib.imap0 (
i: c:
''${pkgs.replace-secret}/bin/replace-secret '{{password-${toString i}}}' '${c.passwordFile}' /run/mpd/mpd.conf''
) cfg.credentials
)
);
serviceConfig =
{
User = "${cfg.user}";
# Note: the first "" overrides the ExecStart from the upstream unit
ExecStart = [ "" "${pkgs.mpd}/bin/mpd --systemd /run/mpd/mpd.conf" ];
RuntimeDirectory = "mpd";
StateDirectory = []
++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ]
++ lib.optionals (cfg.playlistDirectory == "/var/lib/${name}/playlists") [ name "${name}/playlists" ]
++ lib.optionals (cfg.musicDirectory == "/var/lib/${name}/music") [ name "${name}/music" ];
};
serviceConfig = {
User = "${cfg.user}";
# Note: the first "" overrides the ExecStart from the upstream unit
ExecStart = [
""
"${pkgs.mpd}/bin/mpd --systemd /run/mpd/mpd.conf"
];
RuntimeDirectory = "mpd";
StateDirectory =
[ ]
++ lib.optionals (cfg.dataDir == "/var/lib/${name}") [ name ]
++ lib.optionals (cfg.playlistDirectory == "/var/lib/${name}/playlists") [
name
"${name}/playlists"
]
++ lib.optionals (cfg.musicDirectory == "/var/lib/${name}/music") [
name
"${name}/music"
];
};
};
users.users = lib.optionalAttrs (cfg.user == name) {

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,23 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.sanoid;
datasetSettingsType = with lib.types;
(attrsOf (nullOr (oneOf [ str int bool (listOf str) ]))) // {
datasetSettingsType =
with lib.types;
(attrsOf (
nullOr (oneOf [
str
int
bool
(listOf str)
])
))
// {
description = "dataset/template options";
};
@ -48,10 +62,13 @@ let
datasetOptions = rec {
use_template = lib.mkOption {
description = "Names of the templates to use for this dataset.";
type = lib.types.listOf (lib.types.str // {
check = (lib.types.enum (lib.attrNames cfg.templates)).check;
description = "configured template name";
});
type = lib.types.listOf (
lib.types.str
// {
check = (lib.types.enum (lib.attrNames cfg.templates)).check;
description = "configured template name";
}
);
default = [ ];
};
useTemplate = use_template;
@ -63,7 +80,12 @@ let
recursively in an atomic way without the possibility to
override settings for child datasets.
'';
type = with lib.types; oneOf [ bool (enum [ "zfs" ]) ];
type =
with lib.types;
oneOf [
bool
(enum [ "zfs" ])
];
default = false;
};
@ -80,26 +102,32 @@ let
# Function to build "zfs allow" and "zfs unallow" commands for the
# filesystems we've delegated permissions to.
buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
"-+/run/booted-system/sw/bin/zfs"
zfsAction
"sanoid"
(lib.concatStringsSep "," permissions)
dataset
];
buildAllowCommand =
zfsAction: permissions: dataset:
lib.escapeShellArgs [
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
"-+/run/booted-system/sw/bin/zfs"
zfsAction
"sanoid"
(lib.concatStringsSep "," permissions)
dataset
];
configFile =
let
mkValueString = v:
if lib.isList v then lib.concatStringsSep "," v
else lib.generators.mkValueStringDefault { } v;
mkValueString =
v: if lib.isList v then lib.concatStringsSep "," v else lib.generators.mkValueStringDefault { } v;
mkKeyValue = k: v:
if v == null then ""
else if k == "processChildrenOnly" then ""
else if k == "useTemplate" then ""
else lib.generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
mkKeyValue =
k: v:
if v == null then
""
else if k == "processChildrenOnly" then
""
else if k == "useTemplate" then
""
else
lib.generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
in
lib.generators.toINI { inherit mkKeyValue; } cfg.settings;
@ -111,7 +139,7 @@ in
options.services.sanoid = {
enable = lib.mkEnableOption "Sanoid ZFS snapshotting service";
package = lib.mkPackageOption pkgs "sanoid" {};
package = lib.mkPackageOption pkgs "sanoid" { };
interval = lib.mkOption {
type = lib.types.str;
@ -126,21 +154,32 @@ in
};
datasets = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ config, options, ... }: {
freeformType = datasetSettingsType;
options = commonOptions // datasetOptions;
config.use_template = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (options.useTemplate or { });
config.process_children_only = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (options.processChildrenOnly or { });
}));
type = lib.types.attrsOf (
lib.types.submodule (
{ config, options, ... }:
{
freeformType = datasetSettingsType;
options = commonOptions // datasetOptions;
config.use_template = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (
options.useTemplate or { }
);
config.process_children_only = lib.modules.mkAliasAndWrapDefsWithPriority lib.id (
options.processChildrenOnly or { }
);
}
)
);
default = { };
description = "Datasets to snapshot.";
};
templates = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule {
freeformType = datasetSettingsType;
options = commonOptions;
});
type = lib.types.attrsOf (
lib.types.submodule {
freeformType = datasetSettingsType;
options = commonOptions;
}
);
default = { };
description = "Templates for datasets.";
};
@ -157,7 +196,11 @@ in
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "--verbose" "--readonly" "--debug" ];
example = [
"--verbose"
"--readonly"
"--debug"
];
description = ''
Extra arguments to pass to sanoid. See
<https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options>
@ -177,14 +220,29 @@ in
systemd.services.sanoid = {
description = "Sanoid snapshot service";
serviceConfig = {
ExecStartPre = (map (buildAllowCommand "allow" [ "snapshot" "mount" "destroy" ]) datasets);
ExecStopPost = (map (buildAllowCommand "unallow" [ "snapshot" "mount" "destroy" ]) datasets);
ExecStart = lib.escapeShellArgs ([
"${cfg.package}/bin/sanoid"
"--cron"
"--configdir"
(pkgs.writeTextDir "sanoid.conf" configFile)
] ++ cfg.extraArgs);
ExecStartPre = (
map (buildAllowCommand "allow" [
"snapshot"
"mount"
"destroy"
]) datasets
);
ExecStopPost = (
map (buildAllowCommand "unallow" [
"snapshot"
"mount"
"destroy"
]) datasets
);
ExecStart = lib.escapeShellArgs (
[
"${cfg.package}/bin/sanoid"
"--cron"
"--configdir"
(pkgs.writeTextDir "sanoid.conf" configFile)
]
++ cfg.extraArgs
);
User = "sanoid";
Group = "sanoid";
DynamicUser = true;

View File

@ -1,54 +1,69 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.syncoid;
# Extract local dasaset names (so no datasets containing "@")
localDatasetName = d: lib.optionals (d != null) (
let m = builtins.match "([^/@]+[^@]*)" d; in
lib.optionals (m != null) m
);
localDatasetName =
d:
lib.optionals (d != null) (
let
m = builtins.match "([^/@]+[^@]*)" d;
in
lib.optionals (m != null) m
);
# Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
escapeUnitName = name:
lib.concatMapStrings (s: if lib.isList s then "-" else s)
(builtins.split "[^a-zA-Z0-9_.\\-]+" name);
escapeUnitName =
name:
lib.concatMapStrings (s: if lib.isList s then "-" else s) (
builtins.split "[^a-zA-Z0-9_.\\-]+" name
);
# Function to build "zfs allow" commands for the filesystems we've delegated
# permissions to. It also checks if the target dataset exists before
# delegating permissions, if it doesn't exist we delegate it to the parent
# dataset (if it exists). This should solve the case of provisoning new
# datasets.
buildAllowCommand = permissions: dataset: (
"-+${pkgs.writeShellScript "zfs-allow-${dataset}" ''
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
buildAllowCommand =
permissions: dataset:
(
"-+${pkgs.writeShellScript "zfs-allow-${dataset}" ''
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
# Run a ZFS list on the dataset to check if it exists
if ${lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"list"
dataset
]} 2> /dev/null; then
${lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"allow"
cfg.user
(lib.concatStringsSep "," permissions)
dataset
]}
${lib.optionalString ((builtins.dirOf dataset) != ".") ''
else
# Run a ZFS list on the dataset to check if it exists
if ${
lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"list"
dataset
]
} 2> /dev/null; then
${lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"allow"
cfg.user
(lib.concatStringsSep "," permissions)
# Remove the last part of the path
(builtins.dirOf dataset)
dataset
]}
''}
fi
''}"
);
${lib.optionalString ((builtins.dirOf dataset) != ".") ''
else
${lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"allow"
cfg.user
(lib.concatStringsSep "," permissions)
# Remove the last part of the path
(builtins.dirOf dataset)
]}
''}
fi
''}"
);
# Function to build "zfs unallow" commands for the filesystems we've
# delegated permissions to. Here we unallow both the target but also
@ -56,26 +71,30 @@ let
# knowing if the allow command did execute on the parent dataset or
# not in the pre-hook. We can't run the same if in the post hook
# since the dataset should have been created at this point.
buildUnallowCommand = permissions: dataset: (
"-+${pkgs.writeShellScript "zfs-unallow-${dataset}" ''
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
${lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"unallow"
cfg.user
(lib.concatStringsSep "," permissions)
dataset
]}
${lib.optionalString ((builtins.dirOf dataset) != ".") (lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"unallow"
cfg.user
(lib.concatStringsSep "," permissions)
# Remove the last part of the path
(builtins.dirOf dataset)
])}
''}"
);
buildUnallowCommand =
permissions: dataset:
(
"-+${pkgs.writeShellScript "zfs-unallow-${dataset}" ''
# Here we explicitly use the booted system to guarantee the stable API needed by ZFS
${lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"unallow"
cfg.user
(lib.concatStringsSep "," permissions)
dataset
]}
${lib.optionalString ((builtins.dirOf dataset) != ".") (
lib.escapeShellArgs [
"/run/booted-system/sw/bin/zfs"
"unallow"
cfg.user
(lib.concatStringsSep "," permissions)
# Remove the last part of the path
(builtins.dirOf dataset)
]
)}
''}"
);
in
{
@ -84,7 +103,7 @@ in
options.services.syncoid = {
enable = lib.mkEnableOption "Syncoid ZFS synchronization service";
package = lib.mkPackageOption pkgs "sanoid" {};
package = lib.mkPackageOption pkgs "sanoid" { };
interval = lib.mkOption {
type = lib.types.str;
@ -131,7 +150,14 @@ in
localSourceAllow = lib.mkOption {
type = lib.types.listOf lib.types.str;
# Permissions snapshot and destroy are in case --no-sync-snap is not used
default = [ "bookmark" "hold" "send" "snapshot" "destroy" "mount" ];
default = [
"bookmark"
"hold"
"send"
"snapshot"
"destroy"
"mount"
];
description = ''
Permissions granted for the {option}`services.syncoid.user` user
for local source datasets. See
@ -142,8 +168,21 @@ in
localTargetAllow = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "change-key" "compression" "create" "mount" "mountpoint" "receive" "rollback" ];
example = [ "create" "mount" "receive" "rollback" ];
default = [
"change-key"
"compression"
"create"
"mount"
"mountpoint"
"receive"
"rollback"
];
example = [
"create"
"mount"
"receive"
"rollback"
];
description = ''
Permissions granted for the {option}`services.syncoid.user` user
for local target datasets. See
@ -176,111 +215,116 @@ in
};
commands = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
options = {
source = lib.mkOption {
type = lib.types.str;
example = "pool/dataset";
description = ''
Source ZFS dataset. Can be either local or remote. Defaults to
the attribute name.
'';
};
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = {
source = lib.mkOption {
type = lib.types.str;
example = "pool/dataset";
description = ''
Source ZFS dataset. Can be either local or remote. Defaults to
the attribute name.
'';
};
target = lib.mkOption {
type = lib.types.str;
example = "user@server:pool/dataset";
description = ''
Target ZFS dataset. Can be either local
(«pool/dataset») or remote
(«user@server:pool/dataset»).
'';
};
target = lib.mkOption {
type = lib.types.str;
example = "user@server:pool/dataset";
description = ''
Target ZFS dataset. Can be either local
(«pool/dataset») or remote
(«user@server:pool/dataset»).
'';
};
recursive = lib.mkEnableOption ''the transfer of child datasets'';
recursive = lib.mkEnableOption ''the transfer of child datasets'';
sshKey = lib.mkOption {
type = with lib.types; nullOr (coercedTo path toString str);
description = ''
SSH private key file to use to login to the remote system.
Defaults to {option}`services.syncoid.sshKey` option.
'';
};
sshKey = lib.mkOption {
type = with lib.types; nullOr (coercedTo path toString str);
description = ''
SSH private key file to use to login to the remote system.
Defaults to {option}`services.syncoid.sshKey` option.
'';
};
localSourceAllow = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
Permissions granted for the {option}`services.syncoid.user` user
for local source datasets. See
<https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html>
for available permissions.
Defaults to {option}`services.syncoid.localSourceAllow` option.
'';
};
localSourceAllow = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
Permissions granted for the {option}`services.syncoid.user` user
for local source datasets. See
<https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html>
for available permissions.
Defaults to {option}`services.syncoid.localSourceAllow` option.
'';
};
localTargetAllow = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
Permissions granted for the {option}`services.syncoid.user` user
for local target datasets. See
<https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html>
for available permissions.
Make sure to include the `change-key` permission if you send raw encrypted datasets,
the `compression` permission if you send raw compressed datasets, and so on.
For remote target datasets you'll have to set your remote user permissions by yourself.
'';
};
localTargetAllow = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
Permissions granted for the {option}`services.syncoid.user` user
for local target datasets. See
<https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html>
for available permissions.
Make sure to include the `change-key` permission if you send raw encrypted datasets,
the `compression` permission if you send raw compressed datasets, and so on.
For remote target datasets you'll have to set your remote user permissions by yourself.
'';
};
sendOptions = lib.mkOption {
type = lib.types.separatedString " ";
default = "";
example = "Lc e";
description = ''
Advanced options to pass to zfs send. Options are specified
without their leading dashes and separated by spaces.
'';
};
sendOptions = lib.mkOption {
type = lib.types.separatedString " ";
default = "";
example = "Lc e";
description = ''
Advanced options to pass to zfs send. Options are specified
without their leading dashes and separated by spaces.
'';
};
recvOptions = lib.mkOption {
type = lib.types.separatedString " ";
default = "";
example = "ux recordsize o compression=lz4";
description = ''
Advanced options to pass to zfs recv. Options are specified
without their leading dashes and separated by spaces.
'';
};
recvOptions = lib.mkOption {
type = lib.types.separatedString " ";
default = "";
example = "ux recordsize o compression=lz4";
description = ''
Advanced options to pass to zfs recv. Options are specified
without their leading dashes and separated by spaces.
'';
};
useCommonArgs = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to add the configured common arguments to this command.
'';
};
useCommonArgs = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to add the configured common arguments to this command.
'';
};
service = lib.mkOption {
type = lib.types.attrs;
default = { };
description = ''
Systemd configuration specific to this syncoid service.
'';
};
service = lib.mkOption {
type = lib.types.attrs;
default = { };
description = ''
Systemd configuration specific to this syncoid service.
'';
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "--sshport 2222" ];
description = "Extra syncoid arguments for this command.";
};
};
config = {
source = lib.mkDefault name;
sshKey = lib.mkDefault cfg.sshKey;
localSourceAllow = lib.mkDefault cfg.localSourceAllow;
localTargetAllow = lib.mkDefault cfg.localTargetAllow;
};
}));
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "--sshport 2222" ];
description = "Extra syncoid arguments for this command.";
};
};
config = {
source = lib.mkDefault name;
sshKey = lib.mkDefault cfg.sshKey;
localSourceAllow = lib.mkDefault cfg.localSourceAllow;
localTargetAllow = lib.mkDefault cfg.localTargetAllow;
};
}
)
);
default = { };
example = lib.literalExpression ''
{
@ -310,9 +354,10 @@ in
};
};
systemd.services = lib.mapAttrs'
(name: c:
lib.nameValuePair "syncoid-${escapeUnitName name}" (lib.mkMerge [
systemd.services = lib.mapAttrs' (
name: c:
lib.nameValuePair "syncoid-${escapeUnitName name}" (
lib.mkMerge [
{
description = "Syncoid ZFS synchronization from ${c.source} to ${c.target}";
after = [ "zfs.target" ];
@ -321,25 +366,30 @@ in
path = [ "/run/booted-system/sw/bin/" ];
serviceConfig = {
ExecStartPre =
(map (buildAllowCommand c.localSourceAllow) (localDatasetName c.source)) ++
(map (buildAllowCommand c.localTargetAllow) (localDatasetName c.target));
(map (buildAllowCommand c.localSourceAllow) (localDatasetName c.source))
++ (map (buildAllowCommand c.localTargetAllow) (localDatasetName c.target));
ExecStopPost =
(map (buildUnallowCommand c.localSourceAllow) (localDatasetName c.source)) ++
(map (buildUnallowCommand c.localTargetAllow) (localDatasetName c.target));
ExecStart = lib.escapeShellArgs ([ "${cfg.package}/bin/syncoid" ]
(map (buildUnallowCommand c.localSourceAllow) (localDatasetName c.source))
++ (map (buildUnallowCommand c.localTargetAllow) (localDatasetName c.target));
ExecStart = lib.escapeShellArgs (
[ "${cfg.package}/bin/syncoid" ]
++ lib.optionals c.useCommonArgs cfg.commonArgs
++ lib.optional c.recursive "-r"
++ lib.optionals (c.sshKey != null) [ "--sshkey" c.sshKey ]
++ lib.optionals (c.sshKey != null) [
"--sshkey"
c.sshKey
]
++ c.extraArgs
++ [
"--sendoptions"
c.sendOptions
"--recvoptions"
c.recvOptions
"--no-privilege-elevation"
c.source
c.target
]);
"--sendoptions"
c.sendOptions
"--recvoptions"
c.recvOptions
"--no-privilege-elevation"
c.source
c.target
]
);
User = cfg.user;
Group = cfg.group;
StateDirectory = [ "syncoid" ];
@ -372,14 +422,23 @@ in
ProtectKernelTunables = true;
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
RestrictAddressFamilies = [
"AF_UNIX"
"AF_INET"
"AF_INET6"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RootDirectory = "/run/syncoid/${escapeUnitName name}";
RootDirectoryStartOnly = true;
BindPaths = [ "/dev/zfs" ];
BindReadOnlyPaths = [ builtins.storeDir "/etc" "/run" "/bin/sh" ];
BindReadOnlyPaths = [
builtins.storeDir
"/etc"
"/run"
"/bin/sh"
];
# Avoid useless mounting of RootDirectory= in the own RootDirectory= of ExecStart='s mount namespace.
InaccessiblePaths = [ "-+/run/syncoid/${escapeUnitName name}" ];
MountAPIVFS = true;
@ -409,9 +468,13 @@ in
}
cfg.service
c.service
]))
cfg.commands;
]
)
) cfg.commands;
};
meta.maintainers = with lib.maintainers; [ julm lopsided98 ];
meta.maintainers = with lib.maintainers; [
julm
lopsided98
];
}

View File

@ -1,4 +1,10 @@
{ config, options, pkgs, lib, ... }:
{
config,
options,
pkgs,
lib,
...
}:
let
version = "1.10.1";
cfg = config.services.kubernetes.addons.dns;
@ -7,7 +13,8 @@ let
health = 10054;
metrics = 10055;
};
in {
in
{
options.services.kubernetes.addons.dns = {
enable = lib.mkEnableOption "kubernetes dns addon";
@ -15,11 +22,11 @@ in {
description = "Dns addon clusterIP";
# this default is also what kubernetes users
default = (
lib.concatStringsSep "." (
lib.take 3 (lib.splitString "." config.services.kubernetes.apiserver.serviceClusterIpRange
default =
(lib.concatStringsSep "." (
lib.take 3 (lib.splitString "." config.services.kubernetes.apiserver.serviceClusterIpRange)
))
) + ".254";
+ ".254";
defaultText = lib.literalMD ''
The `x.y.z.254` IP of
`config.${options.services.kubernetes.apiserver.serviceClusterIpRange}`.
@ -48,7 +55,10 @@ in {
See: <https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/addon-manager/README.md>.
'';
default = "Reconcile";
type = lib.types.enum [ "Reconcile" "EnsureExists" ];
type = lib.types.enum [
"Reconcile"
"EnsureExists"
];
};
coredns = lib.mkOption {
@ -106,8 +116,9 @@ in {
};
config = lib.mkIf cfg.enable {
services.kubernetes.kubelet.seedDockerImages =
lib.singleton (pkgs.dockerTools.pullImage cfg.coredns);
services.kubernetes.kubelet.seedDockerImages = lib.singleton (
pkgs.dockerTools.pullImage cfg.coredns
);
services.kubernetes.addonManager.bootstrapAddons = {
coredns-cr = {
@ -125,8 +136,16 @@ in {
rules = [
{
apiGroups = [ "" ];
resources = [ "endpoints" "services" "pods" "namespaces" ];
verbs = [ "list" "watch" ];
resources = [
"endpoints"
"services"
"pods"
"namespaces"
];
verbs = [
"list"
"watch"
];
}
{
apiGroups = [ "" ];
@ -136,7 +155,10 @@ in {
{
apiGroups = [ "discovery.k8s.io" ];
resources = [ "endpointslices" ];
verbs = [ "list" "watch" ];
verbs = [
"list"
"watch"
];
}
];
};
@ -219,10 +241,14 @@ in {
spec = {
replicas = cfg.replicas;
selector = {
matchLabels = { k8s-app = "kube-dns"; };
matchLabels = {
k8s-app = "kube-dns";
};
};
strategy = {
rollingUpdate = { maxUnavailable = 1; };
rollingUpdate = {
maxUnavailable = 1;
};
type = "RollingUpdate";
};
template = {
@ -234,7 +260,10 @@ in {
spec = {
containers = [
{
args = [ "-conf" "/etc/coredns/Corefile" ];
args = [
"-conf"
"/etc/coredns/Corefile"
];
image = with cfg.coredns; "${imageName}:${finalImageTag}";
imagePullPolicy = "Never";
livenessProbe = {
@ -358,7 +387,9 @@ in {
protocol = "TCP";
}
];
selector = { k8s-app = "kube-dns"; };
selector = {
k8s-app = "kube-dns";
};
};
};
};

View File

@ -1,4 +1,10 @@
{ config, lib, options, pkgs, ... }:
{
config,
lib,
options,
pkgs,
...
}:
let
top = config.services.kubernetes;
otop = options.services.kubernetes;
@ -6,22 +12,40 @@ let
isRBACEnabled = lib.elem "RBAC" cfg.authorizationMode;
apiserverServiceIP = (lib.concatStringsSep "." (
lib.take 3 (lib.splitString "." cfg.serviceClusterIpRange
)) + ".1");
apiserverServiceIP = (
lib.concatStringsSep "." (lib.take 3 (lib.splitString "." cfg.serviceClusterIpRange)) + ".1"
);
in
{
imports = [
(lib.mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "admissionControl" ] [ "services" "kubernetes" "apiserver" "enableAdmissionPlugins" ])
(lib.mkRenamedOptionModule [ "services" "kubernetes" "apiserver" "address" ] ["services" "kubernetes" "apiserver" "bindAddress"])
(lib.mkRenamedOptionModule
[ "services" "kubernetes" "apiserver" "admissionControl" ]
[ "services" "kubernetes" "apiserver" "enableAdmissionPlugins" ]
)
(lib.mkRenamedOptionModule
[ "services" "kubernetes" "apiserver" "address" ]
[ "services" "kubernetes" "apiserver" "bindAddress" ]
)
(lib.mkRemovedOptionModule [ "services" "kubernetes" "apiserver" "insecureBindAddress" ] "")
(lib.mkRemovedOptionModule [ "services" "kubernetes" "apiserver" "insecurePort" ] "")
(lib.mkRemovedOptionModule [ "services" "kubernetes" "apiserver" "publicAddress" ] "")
(lib.mkRenamedOptionModule [ "services" "kubernetes" "etcd" "servers" ] [ "services" "kubernetes" "apiserver" "etcd" "servers" ])
(lib.mkRenamedOptionModule [ "services" "kubernetes" "etcd" "keyFile" ] [ "services" "kubernetes" "apiserver" "etcd" "keyFile" ])
(lib.mkRenamedOptionModule [ "services" "kubernetes" "etcd" "certFile" ] [ "services" "kubernetes" "apiserver" "etcd" "certFile" ])
(lib.mkRenamedOptionModule [ "services" "kubernetes" "etcd" "caFile" ] [ "services" "kubernetes" "apiserver" "etcd" "caFile" ])
(lib.mkRenamedOptionModule
[ "services" "kubernetes" "etcd" "servers" ]
[ "services" "kubernetes" "apiserver" "etcd" "servers" ]
)
(lib.mkRenamedOptionModule
[ "services" "kubernetes" "etcd" "keyFile" ]
[ "services" "kubernetes" "apiserver" "etcd" "keyFile" ]
)
(lib.mkRenamedOptionModule
[ "services" "kubernetes" "etcd" "certFile" ]
[ "services" "kubernetes" "apiserver" "etcd" "certFile" ]
)
(lib.mkRenamedOptionModule
[ "services" "kubernetes" "etcd" "caFile" ]
[ "services" "kubernetes" "apiserver" "etcd" "caFile" ]
)
];
###### interface
@ -48,8 +72,18 @@ in
Kubernetes apiserver authorization mode (AlwaysAllow/AlwaysDeny/ABAC/Webhook/RBAC/Node). See
<https://kubernetes.io/docs/reference/access-authn-authz/authorization/>
'';
default = ["RBAC" "Node"]; # Enabling RBAC by default, although kubernetes default is AllowAllow
type = listOf (enum ["AlwaysAllow" "AlwaysDeny" "ABAC" "Webhook" "RBAC" "Node"]);
default = [
"RBAC"
"Node"
]; # Enabling RBAC by default, although kubernetes default is AllowAllow
type = listOf (enum [
"AlwaysAllow"
"AlwaysDeny"
"ABAC"
"Webhook"
"RBAC"
"Node"
]);
};
authorizationPolicy = lib.mkOption {
@ -57,7 +91,7 @@ in
Kubernetes apiserver authorization policy file. See
<https://kubernetes.io/docs/reference/access-authn-authz/authorization/>
'';
default = [];
default = [ ];
type = listOf attrs;
};
@ -92,7 +126,7 @@ in
Kubernetes admission control plugins to disable. See
<https://kubernetes.io/docs/admin/admission-controllers/>
'';
default = [];
default = [ ];
type = listOf str;
};
@ -104,14 +138,24 @@ in
<https://kubernetes.io/docs/admin/admission-controllers/>
'';
default = [
"NamespaceLifecycle" "LimitRanger" "ServiceAccount"
"ResourceQuota" "DefaultStorageClass" "DefaultTolerationSeconds"
"NamespaceLifecycle"
"LimitRanger"
"ServiceAccount"
"ResourceQuota"
"DefaultStorageClass"
"DefaultTolerationSeconds"
"NodeRestriction"
];
example = [
"NamespaceLifecycle" "NamespaceExists" "LimitRanger"
"SecurityContextDeny" "ServiceAccount" "ResourceQuota"
"PodSecurityPolicy" "NodeRestriction" "DefaultStorageClass"
"NamespaceLifecycle"
"NamespaceExists"
"LimitRanger"
"SecurityContextDeny"
"ServiceAccount"
"ResourceQuota"
"PodSecurityPolicy"
"NodeRestriction"
"DefaultStorageClass"
];
type = listOf str;
};
@ -119,7 +163,7 @@ in
etcd = {
servers = lib.mkOption {
description = "List of etcd servers.";
default = ["http://127.0.0.1:2379"];
default = [ "http://127.0.0.1:2379" ];
type = types.listOf types.str;
};
@ -151,7 +195,7 @@ in
extraSANs = lib.mkOption {
description = "Extra x509 Subject Alternative Names to be added to the kubernetes apiserver tls cert.";
default = [];
default = [ ];
type = listOf str;
};
@ -214,7 +258,10 @@ in
Kubernetes apiserver storage backend.
'';
default = "etcd3";
type = enum ["etcd2" "etcd3"];
type = enum [
"etcd2"
"etcd3"
];
};
securePort = lib.mkOption {
@ -309,135 +356,143 @@ in
};
###### implementation
config = lib.mkMerge [
(lib.mkIf cfg.enable {
systemd.services.kube-apiserver = {
description = "Kubernetes APIServer Service";
wantedBy = [ "kubernetes.target" ];
after = [ "network.target" ];
serviceConfig = {
Slice = "kubernetes.slice";
ExecStart = ''
${top.package}/bin/kube-apiserver \
--allow-privileged=${lib.boolToString cfg.allowPrivileged} \
--authorization-mode=${lib.concatStringsSep "," cfg.authorizationMode} \
${lib.optionalString (lib.elem "ABAC" cfg.authorizationMode)
"--authorization-policy-file=${
pkgs.writeText "kube-auth-policy.jsonl"
(lib.concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.authorizationPolicy)
}"
} \
${lib.optionalString (lib.elem "Webhook" cfg.authorizationMode)
"--authorization-webhook-config-file=${cfg.webhookConfig}"
} \
--bind-address=${cfg.bindAddress} \
${lib.optionalString (cfg.advertiseAddress != null)
"--advertise-address=${cfg.advertiseAddress}"} \
${lib.optionalString (cfg.clientCaFile != null)
"--client-ca-file=${cfg.clientCaFile}"} \
--disable-admission-plugins=${lib.concatStringsSep "," cfg.disableAdmissionPlugins} \
--enable-admission-plugins=${lib.concatStringsSep "," cfg.enableAdmissionPlugins} \
--etcd-servers=${lib.concatStringsSep "," cfg.etcd.servers} \
${lib.optionalString (cfg.etcd.caFile != null)
"--etcd-cafile=${cfg.etcd.caFile}"} \
${lib.optionalString (cfg.etcd.certFile != null)
"--etcd-certfile=${cfg.etcd.certFile}"} \
${lib.optionalString (cfg.etcd.keyFile != null)
"--etcd-keyfile=${cfg.etcd.keyFile}"} \
${lib.optionalString (cfg.featureGates != {})
"--feature-gates=${(lib.concatStringsSep "," (builtins.attrValues (lib.mapAttrs (n: v: "${n}=${lib.trivial.boolToString v}") cfg.featureGates)))}"} \
${lib.optionalString (cfg.basicAuthFile != null)
"--basic-auth-file=${cfg.basicAuthFile}"} \
${lib.optionalString (cfg.kubeletClientCaFile != null)
"--kubelet-certificate-authority=${cfg.kubeletClientCaFile}"} \
${lib.optionalString (cfg.kubeletClientCertFile != null)
"--kubelet-client-certificate=${cfg.kubeletClientCertFile}"} \
${lib.optionalString (cfg.kubeletClientKeyFile != null)
"--kubelet-client-key=${cfg.kubeletClientKeyFile}"} \
${lib.optionalString (cfg.preferredAddressTypes != null)
"--kubelet-preferred-address-types=${cfg.preferredAddressTypes}"} \
${lib.optionalString (cfg.proxyClientCertFile != null)
"--proxy-client-cert-file=${cfg.proxyClientCertFile}"} \
${lib.optionalString (cfg.proxyClientKeyFile != null)
"--proxy-client-key-file=${cfg.proxyClientKeyFile}"} \
${lib.optionalString (cfg.runtimeConfig != "")
"--runtime-config=${cfg.runtimeConfig}"} \
--secure-port=${toString cfg.securePort} \
--api-audiences=${toString cfg.apiAudiences} \
--service-account-issuer=${toString cfg.serviceAccountIssuer} \
--service-account-signing-key-file=${cfg.serviceAccountSigningKeyFile} \
--service-account-key-file=${cfg.serviceAccountKeyFile} \
--service-cluster-ip-range=${cfg.serviceClusterIpRange} \
--storage-backend=${cfg.storageBackend} \
${lib.optionalString (cfg.tlsCertFile != null)
"--tls-cert-file=${cfg.tlsCertFile}"} \
${lib.optionalString (cfg.tlsKeyFile != null)
"--tls-private-key-file=${cfg.tlsKeyFile}"} \
${lib.optionalString (cfg.tokenAuthFile != null)
"--token-auth-file=${cfg.tokenAuthFile}"} \
${lib.optionalString (cfg.verbosity != null) "--v=${toString cfg.verbosity}"} \
${cfg.extraOpts}
'';
WorkingDirectory = top.dataDir;
User = "kubernetes";
Group = "kubernetes";
AmbientCapabilities = "cap_net_bind_service";
Restart = "on-failure";
RestartSec = 5;
};
unitConfig = {
StartLimitIntervalSec = 0;
};
systemd.services.kube-apiserver = {
description = "Kubernetes APIServer Service";
wantedBy = [ "kubernetes.target" ];
after = [ "network.target" ];
serviceConfig = {
Slice = "kubernetes.slice";
ExecStart = ''
${top.package}/bin/kube-apiserver \
--allow-privileged=${lib.boolToString cfg.allowPrivileged} \
--authorization-mode=${lib.concatStringsSep "," cfg.authorizationMode} \
${lib.optionalString (lib.elem "ABAC" cfg.authorizationMode) "--authorization-policy-file=${pkgs.writeText "kube-auth-policy.jsonl" (lib.concatMapStringsSep "\n" (l: builtins.toJSON l) cfg.authorizationPolicy)}"} \
${lib.optionalString (lib.elem "Webhook" cfg.authorizationMode) "--authorization-webhook-config-file=${cfg.webhookConfig}"} \
--bind-address=${cfg.bindAddress} \
${lib.optionalString (cfg.advertiseAddress != null) "--advertise-address=${cfg.advertiseAddress}"} \
${lib.optionalString (cfg.clientCaFile != null) "--client-ca-file=${cfg.clientCaFile}"} \
--disable-admission-plugins=${lib.concatStringsSep "," cfg.disableAdmissionPlugins} \
--enable-admission-plugins=${lib.concatStringsSep "," cfg.enableAdmissionPlugins} \
--etcd-servers=${lib.concatStringsSep "," cfg.etcd.servers} \
${lib.optionalString (cfg.etcd.caFile != null) "--etcd-cafile=${cfg.etcd.caFile}"} \
${lib.optionalString (cfg.etcd.certFile != null) "--etcd-certfile=${cfg.etcd.certFile}"} \
${lib.optionalString (cfg.etcd.keyFile != null) "--etcd-keyfile=${cfg.etcd.keyFile}"} \
${
lib.optionalString (cfg.featureGates != { })
"--feature-gates=${
(lib.concatStringsSep "," (
builtins.attrValues (lib.mapAttrs (n: v: "${n}=${lib.trivial.boolToString v}") cfg.featureGates)
))
}"
} \
${lib.optionalString (cfg.basicAuthFile != null) "--basic-auth-file=${cfg.basicAuthFile}"} \
${
lib.optionalString (
cfg.kubeletClientCaFile != null
) "--kubelet-certificate-authority=${cfg.kubeletClientCaFile}"
} \
${
lib.optionalString (
cfg.kubeletClientCertFile != null
) "--kubelet-client-certificate=${cfg.kubeletClientCertFile}"
} \
${
lib.optionalString (
cfg.kubeletClientKeyFile != null
) "--kubelet-client-key=${cfg.kubeletClientKeyFile}"
} \
${
lib.optionalString (
cfg.preferredAddressTypes != null
) "--kubelet-preferred-address-types=${cfg.preferredAddressTypes}"
} \
${
lib.optionalString (
cfg.proxyClientCertFile != null
) "--proxy-client-cert-file=${cfg.proxyClientCertFile}"
} \
${
lib.optionalString (
cfg.proxyClientKeyFile != null
) "--proxy-client-key-file=${cfg.proxyClientKeyFile}"
} \
${lib.optionalString (cfg.runtimeConfig != "") "--runtime-config=${cfg.runtimeConfig}"} \
--secure-port=${toString cfg.securePort} \
--api-audiences=${toString cfg.apiAudiences} \
--service-account-issuer=${toString cfg.serviceAccountIssuer} \
--service-account-signing-key-file=${cfg.serviceAccountSigningKeyFile} \
--service-account-key-file=${cfg.serviceAccountKeyFile} \
--service-cluster-ip-range=${cfg.serviceClusterIpRange} \
--storage-backend=${cfg.storageBackend} \
${lib.optionalString (cfg.tlsCertFile != null) "--tls-cert-file=${cfg.tlsCertFile}"} \
${lib.optionalString (cfg.tlsKeyFile != null) "--tls-private-key-file=${cfg.tlsKeyFile}"} \
${lib.optionalString (cfg.tokenAuthFile != null) "--token-auth-file=${cfg.tokenAuthFile}"} \
${lib.optionalString (cfg.verbosity != null) "--v=${toString cfg.verbosity}"} \
${cfg.extraOpts}
'';
WorkingDirectory = top.dataDir;
User = "kubernetes";
Group = "kubernetes";
AmbientCapabilities = "cap_net_bind_service";
Restart = "on-failure";
RestartSec = 5;
};
services.etcd = {
clientCertAuth = lib.mkDefault true;
peerClientCertAuth = lib.mkDefault true;
listenClientUrls = lib.mkDefault ["https://0.0.0.0:2379"];
listenPeerUrls = lib.mkDefault ["https://0.0.0.0:2380"];
advertiseClientUrls = lib.mkDefault ["https://${top.masterAddress}:2379"];
initialCluster = lib.mkDefault ["${top.masterAddress}=https://${top.masterAddress}:2380"];
name = lib.mkDefault top.masterAddress;
initialAdvertisePeerUrls = lib.mkDefault ["https://${top.masterAddress}:2380"];
unitConfig = {
StartLimitIntervalSec = 0;
};
};
services.kubernetes.addonManager.bootstrapAddons = lib.mkIf isRBACEnabled {
services.etcd = {
clientCertAuth = lib.mkDefault true;
peerClientCertAuth = lib.mkDefault true;
listenClientUrls = lib.mkDefault [ "https://0.0.0.0:2379" ];
listenPeerUrls = lib.mkDefault [ "https://0.0.0.0:2380" ];
advertiseClientUrls = lib.mkDefault [ "https://${top.masterAddress}:2379" ];
initialCluster = lib.mkDefault [ "${top.masterAddress}=https://${top.masterAddress}:2380" ];
name = lib.mkDefault top.masterAddress;
initialAdvertisePeerUrls = lib.mkDefault [ "https://${top.masterAddress}:2380" ];
};
apiserver-kubelet-api-admin-crb = {
apiVersion = "rbac.authorization.k8s.io/v1";
kind = "ClusterRoleBinding";
metadata = {
name = "system:kube-apiserver:kubelet-api-admin";
};
roleRef = {
apiGroup = "rbac.authorization.k8s.io";
kind = "ClusterRole";
name = "system:kubelet-api-admin";
};
subjects = [{
services.kubernetes.addonManager.bootstrapAddons = lib.mkIf isRBACEnabled {
apiserver-kubelet-api-admin-crb = {
apiVersion = "rbac.authorization.k8s.io/v1";
kind = "ClusterRoleBinding";
metadata = {
name = "system:kube-apiserver:kubelet-api-admin";
};
roleRef = {
apiGroup = "rbac.authorization.k8s.io";
kind = "ClusterRole";
name = "system:kubelet-api-admin";
};
subjects = [
{
kind = "User";
name = "system:kube-apiserver";
}];
};
}
];
};
};
services.kubernetes.pki.certs = with top.lib; {
apiServer = mkCert {
name = "kube-apiserver";
CN = "kubernetes";
hosts = [
"kubernetes.default.svc"
"kubernetes.default.svc.${top.addons.dns.clusterDomain}"
cfg.advertiseAddress
top.masterAddress
apiserverServiceIP
"127.0.0.1"
] ++ cfg.extraSANs;
"kubernetes.default.svc"
"kubernetes.default.svc.${top.addons.dns.clusterDomain}"
cfg.advertiseAddress
top.masterAddress
apiserverServiceIP
"127.0.0.1"
] ++ cfg.extraSANs;
action = "systemctl restart kube-apiserver.service";
};
apiserverProxyClient = mkCert {
@ -467,11 +522,11 @@ in
name = "etcd";
CN = top.masterAddress;
hosts = [
"etcd.local"
"etcd.${top.addons.dns.clusterDomain}"
top.masterAddress
cfg.advertiseAddress
];
"etcd.local"
"etcd.${top.addons.dns.clusterDomain}"
top.masterAddress
cfg.advertiseAddress
];
privateKeyOwner = "etcd";
action = "systemctl restart etcd.service";
};

View File

@ -1,4 +1,10 @@
{ config, lib, options, pkgs, ... }:
{
config,
lib,
options,
pkgs,
...
}:
with lib;
@ -9,7 +15,10 @@ let
in
{
imports = [
(mkRenamedOptionModule [ "services" "kubernetes" "proxy" "address" ] ["services" "kubernetes" "proxy" "bindAddress"])
(mkRenamedOptionModule
[ "services" "kubernetes" "proxy" "address" ]
[ "services" "kubernetes" "proxy" "bindAddress" ]
)
];
###### interface
@ -62,16 +71,24 @@ in
description = "Kubernetes Proxy Service";
wantedBy = [ "kubernetes.target" ];
after = [ "kube-apiserver.service" ];
path = with pkgs; [ iptables conntrack-tools ];
path = with pkgs; [
iptables
conntrack-tools
];
serviceConfig = {
Slice = "kubernetes.slice";
ExecStart = ''
${top.package}/bin/kube-proxy \
--bind-address=${cfg.bindAddress} \
${optionalString (top.clusterCidr!=null)
"--cluster-cidr=${top.clusterCidr}"} \
${optionalString (cfg.featureGates != {})
"--feature-gates=${concatStringsSep "," (builtins.attrValues (mapAttrs (n: v: "${n}=${trivial.boolToString v}") cfg.featureGates))}"} \
${optionalString (top.clusterCidr != null) "--cluster-cidr=${top.clusterCidr}"} \
${
optionalString (cfg.featureGates != { })
"--feature-gates=${
concatStringsSep "," (
builtins.attrValues (mapAttrs (n: v: "${n}=${trivial.boolToString v}") cfg.featureGates)
)
}"
} \
--hostname-override=${cfg.hostname} \
--kubeconfig=${top.lib.mkKubeConfig "kube-proxy" cfg.kubeconfig} \
${optionalString (cfg.verbosity != null) "--v=${toString cfg.verbosity}"} \

View File

@ -1,4 +1,10 @@
{ config, lib, options, pkgs, ... }:
{
config,
lib,
options,
pkgs,
...
}:
let
cfg = config.services.slurm;
@ -7,51 +13,51 @@ let
defaultUser = "slurm";
configFile = pkgs.writeTextDir "slurm.conf"
''
ClusterName=${cfg.clusterName}
StateSaveLocation=${cfg.stateSaveLocation}
SlurmUser=${cfg.user}
${lib.optionalString (cfg.controlMachine != null) "controlMachine=${cfg.controlMachine}"}
${lib.optionalString (cfg.controlAddr != null) "controlAddr=${cfg.controlAddr}"}
${toString (map (x: "NodeName=${x}\n") cfg.nodeName)}
${toString (map (x: "PartitionName=${x}\n") cfg.partitionName)}
PlugStackConfig=${plugStackConfig}/plugstack.conf
ProctrackType=${cfg.procTrackType}
${cfg.extraConfig}
'';
configFile = pkgs.writeTextDir "slurm.conf" ''
ClusterName=${cfg.clusterName}
StateSaveLocation=${cfg.stateSaveLocation}
SlurmUser=${cfg.user}
${lib.optionalString (cfg.controlMachine != null) "controlMachine=${cfg.controlMachine}"}
${lib.optionalString (cfg.controlAddr != null) "controlAddr=${cfg.controlAddr}"}
${toString (map (x: "NodeName=${x}\n") cfg.nodeName)}
${toString (map (x: "PartitionName=${x}\n") cfg.partitionName)}
PlugStackConfig=${plugStackConfig}/plugstack.conf
ProctrackType=${cfg.procTrackType}
${cfg.extraConfig}
'';
plugStackConfig = pkgs.writeTextDir "plugstack.conf"
''
${lib.optionalString cfg.enableSrunX11 "optional ${pkgs.slurm-spank-x11}/lib/x11.so"}
${cfg.extraPlugstackConfig}
'';
plugStackConfig = pkgs.writeTextDir "plugstack.conf" ''
${lib.optionalString cfg.enableSrunX11 "optional ${pkgs.slurm-spank-x11}/lib/x11.so"}
${cfg.extraPlugstackConfig}
'';
cgroupConfig = pkgs.writeTextDir "cgroup.conf"
''
${cfg.extraCgroupConfig}
'';
cgroupConfig = pkgs.writeTextDir "cgroup.conf" ''
${cfg.extraCgroupConfig}
'';
mpiConf = pkgs.writeTextDir "mpi.conf"
''
PMIxCliTmpDirBase=${cfg.mpi.PmixCliTmpDirBase}
${cfg.mpi.extraMpiConfig}
'';
mpiConf = pkgs.writeTextDir "mpi.conf" ''
PMIxCliTmpDirBase=${cfg.mpi.PmixCliTmpDirBase}
${cfg.mpi.extraMpiConfig}
'';
slurmdbdConf = pkgs.writeText "slurmdbd.conf"
''
DbdHost=${cfg.dbdserver.dbdHost}
SlurmUser=${cfg.user}
StorageType=accounting_storage/mysql
StorageUser=${cfg.dbdserver.storageUser}
${cfg.dbdserver.extraConfig}
'';
slurmdbdConf = pkgs.writeText "slurmdbd.conf" ''
DbdHost=${cfg.dbdserver.dbdHost}
SlurmUser=${cfg.user}
StorageType=accounting_storage/mysql
StorageUser=${cfg.dbdserver.storageUser}
${cfg.dbdserver.extraConfig}
'';
# slurm expects some additional config files to be
# in the same directory as slurm.conf
etcSlurm = pkgs.symlinkJoin {
name = "etc-slurm";
paths = [ configFile cgroupConfig plugStackConfig mpiConf ] ++ cfg.extraConfigPaths;
paths = [
configFile
cgroupConfig
plugStackConfig
mpiConf
] ++ cfg.extraConfigPaths;
};
in
@ -134,11 +140,13 @@ in
'';
};
package = lib.mkPackageOption pkgs "slurm" {
example = "slurm-full";
} // {
default = pkgs.slurm.override { enableX11 = ! cfg.enableSrunX11; };
};
package =
lib.mkPackageOption pkgs "slurm" {
example = "slurm-full";
}
// {
default = pkgs.slurm.override { enableX11 = !cfg.enableSrunX11; };
};
controlMachine = lib.mkOption {
type = lib.types.nullOr lib.types.str;
@ -173,7 +181,7 @@ in
nodeName = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
default = [ ];
example = lib.literalExpression ''[ "linux[1-32] CPUs=1 State=UNKNOWN" ];'';
description = ''
Name that SLURM uses to refer to a node (or base partition for BlueGene
@ -184,7 +192,7 @@ in
partitionName = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
default = [ ];
example = lib.literalExpression ''[ "debug Nodes=linux[1-32] Default=YES MaxTime=INFINITE State=UP" ];'';
description = ''
Name by which the partition may be referenced. Note that now you have
@ -285,7 +293,7 @@ in
extraConfigPaths = lib.mkOption {
type = with lib.types; listOf path;
default = [];
default = [ ];
description = ''
Slurm expects config files for plugins in the same path
as `slurm.conf`. Add extra nix store
@ -353,107 +361,132 @@ in
'';
};
in lib.mkIf ( cfg.enableStools ||
cfg.client.enable ||
cfg.server.enable ||
cfg.dbdserver.enable ) {
in
lib.mkIf (cfg.enableStools || cfg.client.enable || cfg.server.enable || cfg.dbdserver.enable) {
environment.systemPackages = [ wrappedSlurm ];
environment.systemPackages = [ wrappedSlurm ];
services.munge.enable = lib.mkDefault true;
services.munge.enable = lib.mkDefault true;
# use a static uid as default to ensure it is the same on all nodes
users.users.slurm = lib.mkIf (cfg.user == defaultUser) {
name = defaultUser;
group = "slurm";
uid = config.ids.uids.slurm;
};
# use a static uid as default to ensure it is the same on all nodes
users.users.slurm = lib.mkIf (cfg.user == defaultUser) {
name = defaultUser;
group = "slurm";
uid = config.ids.uids.slurm;
};
users.groups.slurm.gid = config.ids.uids.slurm;
users.groups.slurm.gid = config.ids.uids.slurm;
systemd.services.slurmd = lib.mkIf (cfg.client.enable) {
path = with pkgs; [ wrappedSlurm coreutils ]
++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
systemd.services.slurmd = lib.mkIf (cfg.client.enable) {
path =
with pkgs;
[
wrappedSlurm
coreutils
]
++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
wantedBy = [ "multi-user.target" ];
after = [
"systemd-tmpfiles-clean.service"
"munge.service"
"network-online.target"
"remote-fs.target"
wantedBy = [ "multi-user.target" ];
after = [
"systemd-tmpfiles-clean.service"
"munge.service"
"network-online.target"
"remote-fs.target"
];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "forking";
KillMode = "process";
ExecStart = "${wrappedSlurm}/bin/slurmd";
PIDFile = "/run/slurmd.pid";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
LimitMEMLOCK = "infinity";
Delegate = "Yes";
};
};
systemd.tmpfiles.rules = lib.optionals cfg.client.enable [
"d /var/spool/slurmd 755 root root -"
"d ${cfg.mpi.PmixCliTmpDirBase} 755 root root -"
];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "forking";
KillMode = "process";
ExecStart = "${wrappedSlurm}/bin/slurmd";
PIDFile = "/run/slurmd.pid";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
LimitMEMLOCK = "infinity";
Delegate="Yes";
};
};
services.openssh.settings.X11Forwarding = lib.mkIf cfg.client.enable (lib.mkDefault true);
systemd.tmpfiles.rules = lib.optionals cfg.client.enable [
"d /var/spool/slurmd 755 root root -"
"d ${cfg.mpi.PmixCliTmpDirBase} 755 root root -"
];
systemd.services.slurmctld = lib.mkIf (cfg.server.enable) {
path =
with pkgs;
[
wrappedSlurm
munge
coreutils
]
++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
services.openssh.settings.X11Forwarding = lib.mkIf cfg.client.enable (lib.mkDefault true);
wantedBy = [ "multi-user.target" ];
after = [
"network.target"
"munged.service"
];
requires = [ "munged.service" ];
systemd.services.slurmctld = lib.mkIf (cfg.server.enable) {
path = with pkgs; [ wrappedSlurm munge coreutils ]
++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
serviceConfig = {
Type = "forking";
ExecStart = "${wrappedSlurm}/bin/slurmctld";
PIDFile = "/run/slurmctld.pid";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
};
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "munged.service" ];
requires = [ "munged.service" ];
serviceConfig = {
Type = "forking";
ExecStart = "${wrappedSlurm}/bin/slurmctld";
PIDFile = "/run/slurmctld.pid";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
preStart = ''
mkdir -p ${cfg.stateSaveLocation}
chown -R ${cfg.user}:slurm ${cfg.stateSaveLocation}
'';
};
preStart = ''
mkdir -p ${cfg.stateSaveLocation}
chown -R ${cfg.user}:slurm ${cfg.stateSaveLocation}
'';
systemd.services.slurmdbd =
let
# slurm strips the last component off the path
configPath = "$RUNTIME_DIRECTORY/slurmdbd.conf";
in
lib.mkIf (cfg.dbdserver.enable) {
path = with pkgs; [
wrappedSlurm
munge
coreutils
];
wantedBy = [ "multi-user.target" ];
after = [
"network.target"
"munged.service"
"mysql.service"
];
requires = [
"munged.service"
"mysql.service"
];
preStart = ''
install -m 600 -o ${cfg.user} -T ${slurmdbdConf} ${configPath}
${lib.optionalString (cfg.dbdserver.storagePassFile != null) ''
echo "StoragePass=$(cat ${cfg.dbdserver.storagePassFile})" \
>> ${configPath}
''}
'';
script = ''
export SLURM_CONF=${configPath}
exec ${cfg.package}/bin/slurmdbd -D
'';
serviceConfig = {
RuntimeDirectory = "slurmdbd";
Type = "simple";
PIDFile = "/run/slurmdbd.pid";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
};
};
};
systemd.services.slurmdbd = let
# slurm strips the last component off the path
configPath = "$RUNTIME_DIRECTORY/slurmdbd.conf";
in lib.mkIf (cfg.dbdserver.enable) {
path = with pkgs; [ wrappedSlurm munge coreutils ];
wantedBy = [ "multi-user.target" ];
after = [ "network.target" "munged.service" "mysql.service" ];
requires = [ "munged.service" "mysql.service" ];
preStart = ''
install -m 600 -o ${cfg.user} -T ${slurmdbdConf} ${configPath}
${lib.optionalString (cfg.dbdserver.storagePassFile != null) ''
echo "StoragePass=$(cat ${cfg.dbdserver.storagePassFile})" \
>> ${configPath}
''}
'';
script = ''
export SLURM_CONF=${configPath}
exec ${cfg.package}/bin/slurmdbd -D
'';
serviceConfig = {
RuntimeDirectory = "slurmdbd";
Type = "simple";
PIDFile = "/run/slurmdbd.pid";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
};
};
};
}

View File

@ -1,142 +1,166 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.buildkite-agents;
hooksDir = hooks:
hooksDir =
hooks:
let
mkHookEntry = name: text: ''
ln --symbolic ${pkgs.writeShellApplication { inherit name text; }}/bin/${name} $out/${name}
'';
in
pkgs.runCommand "buildkite-agent-hooks" {
preferLocalBuild = true;
} ''
mkdir $out
${lib.concatStringsSep "\n" (lib.mapAttrsToList mkHookEntry hooks)}
'';
pkgs.runCommand "buildkite-agent-hooks"
{
preferLocalBuild = true;
}
''
mkdir $out
${lib.concatStringsSep "\n" (lib.mapAttrsToList mkHookEntry hooks)}
'';
buildkiteOptions = { name ? "", config, ... }: {
options = {
enable = lib.mkOption {
default = true;
type = lib.types.bool;
description = "Whether to enable this buildkite agent";
};
buildkiteOptions =
{
name ? "",
config,
...
}:
{
options = {
enable = lib.mkOption {
default = true;
type = lib.types.bool;
description = "Whether to enable this buildkite agent";
};
package = lib.mkOption {
default = pkgs.buildkite-agent;
defaultText = lib.literalExpression "pkgs.buildkite-agent";
description = "Which buildkite-agent derivation to use";
type = lib.types.package;
};
package = lib.mkOption {
default = pkgs.buildkite-agent;
defaultText = lib.literalExpression "pkgs.buildkite-agent";
description = "Which buildkite-agent derivation to use";
type = lib.types.package;
};
dataDir = lib.mkOption {
default = "/var/lib/buildkite-agent-${name}";
description = "The workdir for the agent";
type = lib.types.str;
};
dataDir = lib.mkOption {
default = "/var/lib/buildkite-agent-${name}";
description = "The workdir for the agent";
type = lib.types.str;
};
extraGroups = lib.mkOption {
default = [ "keys" ];
description = "Groups the user for this buildkite agent should belong to";
type = lib.types.listOf lib.types.str;
};
extraGroups = lib.mkOption {
default = [ "keys" ];
description = "Groups the user for this buildkite agent should belong to";
type = lib.types.listOf lib.types.str;
};
runtimePackages = lib.mkOption {
default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ];
defaultText = lib.literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]";
description = "Add programs to the buildkite-agent environment";
type = lib.types.listOf lib.types.package;
};
runtimePackages = lib.mkOption {
default = [
pkgs.bash
pkgs.gnutar
pkgs.gzip
pkgs.git
pkgs.nix
];
defaultText = lib.literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]";
description = "Add programs to the buildkite-agent environment";
type = lib.types.listOf lib.types.package;
};
tokenPath = lib.mkOption {
type = lib.types.path;
description = ''
The token from your Buildkite "Agents" page.
tokenPath = lib.mkOption {
type = lib.types.path;
description = ''
The token from your Buildkite "Agents" page.
A run-time path to the token file, which is supposed to be provisioned
outside of Nix store.
'';
};
A run-time path to the token file, which is supposed to be provisioned
outside of Nix store.
'';
};
name = lib.mkOption {
type = lib.types.str;
default = "%hostname-${name}-%n";
description = ''
The name of the agent as seen in the buildkite dashboard.
'';
};
name = lib.mkOption {
type = lib.types.str;
default = "%hostname-${name}-%n";
description = ''
The name of the agent as seen in the buildkite dashboard.
'';
};
tags = lib.mkOption {
type = lib.types.attrsOf (lib.types.either lib.types.str (lib.types.listOf lib.types.str));
default = { };
example = { queue = "default"; docker = "true"; ruby2 = "true"; };
description = ''
Tags for the agent.
'';
};
tags = lib.mkOption {
type = lib.types.attrsOf (lib.types.either lib.types.str (lib.types.listOf lib.types.str));
default = { };
example = {
queue = "default";
docker = "true";
ruby2 = "true";
};
description = ''
Tags for the agent.
'';
};
extraConfig = lib.mkOption {
type = lib.types.lines;
default = "";
example = "debug=true";
description = ''
Extra lines to be added verbatim to the configuration file.
'';
};
extraConfig = lib.mkOption {
type = lib.types.lines;
default = "";
example = "debug=true";
description = ''
Extra lines to be added verbatim to the configuration file.
'';
};
privateSshKeyPath = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
## maximum care is taken so that secrets (ssh keys and the CI token)
## don't end up in the Nix store.
apply = final: if final == null then null else toString final;
privateSshKeyPath = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
## maximum care is taken so that secrets (ssh keys and the CI token)
## don't end up in the Nix store.
apply = final: if final == null then null else toString final;
description = ''
OpenSSH private key
description = ''
OpenSSH private key
A run-time path to the key file, which is supposed to be provisioned
outside of Nix store.
'';
};
A run-time path to the key file, which is supposed to be provisioned
outside of Nix store.
'';
};
hooks = lib.mkOption {
type = lib.types.attrsOf lib.types.lines;
default = { };
example = lib.literalExpression ''
{
environment = '''
export SECRET_VAR=`head -1 /run/keys/secret`
''';
}'';
description = ''
"Agent" hooks to install.
See <https://buildkite.com/docs/agent/v3/hooks> for possible options.
'';
};
hooks = lib.mkOption {
type = lib.types.attrsOf lib.types.lines;
default = { };
example = lib.literalExpression ''
{
environment = '''
export SECRET_VAR=`head -1 /run/keys/secret`
''';
}'';
description = ''
"Agent" hooks to install.
See <https://buildkite.com/docs/agent/v3/hooks> for possible options.
'';
};
hooksPath = lib.mkOption {
type = lib.types.path;
default = hooksDir config.hooks;
defaultText = lib.literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`";
description = ''
Path to the directory storing the hooks.
Consider using {option}`services.buildkite-agents.<name>.hooks.<name>`
instead.
'';
};
hooksPath = lib.mkOption {
type = lib.types.path;
default = hooksDir config.hooks;
defaultText = lib.literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`";
description = ''
Path to the directory storing the hooks.
Consider using {option}`services.buildkite-agents.<name>.hooks.<name>`
instead.
'';
};
shell = lib.mkOption {
type = lib.types.str;
default = "${pkgs.bash}/bin/bash -e -c";
defaultText = lib.literalExpression ''"''${pkgs.bash}/bin/bash -e -c"'';
description = ''
Command that buildkite-agent 3 will execute when it spawns a shell.
'';
shell = lib.mkOption {
type = lib.types.str;
default = "${pkgs.bash}/bin/bash -e -c";
defaultText = lib.literalExpression ''"''${pkgs.bash}/bin/bash -e -c"'';
description = ''
Command that buildkite-agent 3 will execute when it spawns a shell.
'';
};
};
};
};
enabledAgents = lib.filterAttrs (n: v: v.enable) cfg;
mapAgents = function: lib.mkMerge (lib.mapAttrsToList function enabledAgents);
in
@ -152,76 +176,92 @@ in
'';
};
config.users.users = mapAgents (name: cfg: {
"buildkite-agent-${name}" = {
name = "buildkite-agent-${name}";
home = cfg.dataDir;
createHome = true;
description = "Buildkite agent user";
extraGroups = cfg.extraGroups;
isSystemUser = true;
group = "buildkite-agent-${name}";
};
});
config.users.groups = mapAgents (name: cfg: {
"buildkite-agent-${name}" = { };
});
config.systemd.services = mapAgents (name: cfg: {
"buildkite-agent-${name}" = {
description = "Buildkite Agent";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils ];
environment = config.networking.proxy.envVars // {
HOME = cfg.dataDir;
NIX_REMOTE = "daemon";
config.users.users = mapAgents (
name: cfg: {
"buildkite-agent-${name}" = {
name = "buildkite-agent-${name}";
home = cfg.dataDir;
createHome = true;
description = "Buildkite agent user";
extraGroups = cfg.extraGroups;
isSystemUser = true;
group = "buildkite-agent-${name}";
};
}
);
config.users.groups = mapAgents (
name: cfg: {
"buildkite-agent-${name}" = { };
}
);
## NB: maximum care is taken so that secrets (ssh keys and the CI token)
## don't end up in the Nix store.
preStart =
let
sshDir = "${cfg.dataDir}/.ssh";
tagStr = name: value:
if lib.isList value
then lib.concatStringsSep "," (builtins.map (v: "${name}=${v}") value)
else "${name}=${value}";
tagsStr = lib.concatStringsSep "," (lib.mapAttrsToList tagStr cfg.tags);
in
lib.optionalString (cfg.privateSshKeyPath != null) ''
mkdir -m 0700 -p "${sshDir}"
install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
'' + ''
cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
token="$(cat ${toString cfg.tokenPath})"
name="${cfg.name}"
shell="${cfg.shell}"
tags="${tagsStr}"
build-path="${cfg.dataDir}/builds"
hooks-path="${cfg.hooksPath}"
${cfg.extraConfig}
EOF
config.systemd.services = mapAgents (
name: cfg: {
"buildkite-agent-${name}" = {
description = "Buildkite Agent";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
path = cfg.runtimePackages ++ [
cfg.package
pkgs.coreutils
];
environment = config.networking.proxy.envVars // {
HOME = cfg.dataDir;
NIX_REMOTE = "daemon";
};
## NB: maximum care is taken so that secrets (ssh keys and the CI token)
## don't end up in the Nix store.
preStart =
let
sshDir = "${cfg.dataDir}/.ssh";
tagStr =
name: value:
if lib.isList value then
lib.concatStringsSep "," (builtins.map (v: "${name}=${v}") value)
else
"${name}=${value}";
tagsStr = lib.concatStringsSep "," (lib.mapAttrsToList tagStr cfg.tags);
in
lib.optionalString (cfg.privateSshKeyPath != null) ''
mkdir -m 0700 -p "${sshDir}"
install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa"
''
+ ''
cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF
token="$(cat ${toString cfg.tokenPath})"
name="${cfg.name}"
shell="${cfg.shell}"
tags="${tagsStr}"
build-path="${cfg.dataDir}/builds"
hooks-path="${cfg.hooksPath}"
${cfg.extraConfig}
EOF
'';
serviceConfig = {
ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg";
User = "buildkite-agent-${name}";
RestartSec = 5;
Restart = "on-failure";
TimeoutSec = 10;
# set a long timeout to give buildkite-agent a chance to finish current builds
TimeoutStopSec = "2 min";
KillMode = "mixed";
};
};
}
);
config.assertions = mapAgents (
name: cfg: [
{
assertion = cfg.hooksPath != hooksDir cfg.hooks -> cfg.hooks == { };
message = ''
Options `services.buildkite-agents.${name}.hooksPath' and
`services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive.
'';
serviceConfig = {
ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg";
User = "buildkite-agent-${name}";
RestartSec = 5;
Restart = "on-failure";
TimeoutSec = 10;
# set a long timeout to give buildkite-agent a chance to finish current builds
TimeoutStopSec = "2 min";
KillMode = "mixed";
};
};
});
config.assertions = mapAgents (name: cfg: [{
assertion = cfg.hooksPath != hooksDir cfg.hooks -> cfg.hooks == { };
message = ''
Options `services.buildkite-agents.${name}.hooksPath' and
`services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive.
'';
}]);
}
]
);
}

View File

@ -1,4 +1,9 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.hydra;
@ -7,61 +12,71 @@ let
hydraConf = pkgs.writeScript "hydra.conf" cfg.extraConfig;
hydraEnv =
{ HYDRA_DBI = cfg.dbi;
HYDRA_CONFIG = "${baseDir}/hydra.conf";
HYDRA_DATA = "${baseDir}";
};
hydraEnv = {
HYDRA_DBI = cfg.dbi;
HYDRA_CONFIG = "${baseDir}/hydra.conf";
HYDRA_DATA = "${baseDir}";
};
env =
{ NIX_REMOTE = "daemon";
{
NIX_REMOTE = "daemon";
PGPASSFILE = "${baseDir}/pgpass";
NIX_REMOTE_SYSTEMS = lib.concatStringsSep ":" cfg.buildMachinesFiles;
} // lib.optionalAttrs (cfg.smtpHost != null) {
}
// lib.optionalAttrs (cfg.smtpHost != null) {
EMAIL_SENDER_TRANSPORT = "SMTP";
EMAIL_SENDER_TRANSPORT_host = cfg.smtpHost;
} // hydraEnv // cfg.extraEnv;
}
// hydraEnv
// cfg.extraEnv;
serverEnv = env //
{ HYDRA_TRACKER = cfg.tracker;
serverEnv =
env
// {
HYDRA_TRACKER = cfg.tracker;
XDG_CACHE_HOME = "${baseDir}/www/.cache";
COLUMNS = "80";
PGPASSFILE = "${baseDir}/pgpass-www"; # grrr
} // (lib.optionalAttrs cfg.debugServer { DBIC_TRACE = "1"; });
}
// (lib.optionalAttrs cfg.debugServer { DBIC_TRACE = "1"; });
localDB = "dbi:Pg:dbname=hydra;user=hydra;";
haveLocalDB = cfg.dbi == localDB;
hydra-package =
let
makeWrapperArgs = lib.concatStringsSep " " (lib.mapAttrsToList (key: value: "--set-default \"${key}\" \"${value}\"") hydraEnv);
in pkgs.buildEnv rec {
name = "hydra-env";
nativeBuildInputs = [ pkgs.makeWrapper ];
paths = [ cfg.package ];
let
makeWrapperArgs = lib.concatStringsSep " " (
lib.mapAttrsToList (key: value: "--set-default \"${key}\" \"${value}\"") hydraEnv
);
in
pkgs.buildEnv rec {
name = "hydra-env";
nativeBuildInputs = [ pkgs.makeWrapper ];
paths = [ cfg.package ];
postBuild = ''
if [ -L "$out/bin" ]; then
unlink "$out/bin"
fi
mkdir -p "$out/bin"
for path in ${lib.concatStringsSep " " paths}; do
if [ -d "$path/bin" ]; then
cd "$path/bin"
for prg in *; do
if [ -f "$prg" ]; then
rm -f "$out/bin/$prg"
if [ -x "$prg" ]; then
makeWrapper "$path/bin/$prg" "$out/bin/$prg" ${makeWrapperArgs}
fi
fi
done
postBuild = ''
if [ -L "$out/bin" ]; then
unlink "$out/bin"
fi
done
'';
};
mkdir -p "$out/bin"
for path in ${lib.concatStringsSep " " paths}; do
if [ -d "$path/bin" ]; then
cd "$path/bin"
for prg in *; do
if [ -f "$prg" ]; then
rm -f "$out/bin/$prg"
if [ -x "$prg" ]; then
makeWrapper "$path/bin/$prg" "$out/bin/$prg" ${makeWrapperArgs}
fi
fi
done
fi
done
'';
};
in
@ -199,7 +214,7 @@ in
extraEnv = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {};
default = { };
description = "Extra environment variables for Hydra.";
};
@ -211,9 +226,12 @@ in
buildMachinesFiles = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = lib.optional (config.nix.buildMachines != []) "/etc/nix/machines";
default = lib.optional (config.nix.buildMachines != [ ]) "/etc/nix/machines";
defaultText = lib.literalExpression ''lib.optional (config.nix.buildMachines != []) "/etc/nix/machines"'';
example = [ "/etc/nix/machines" "/var/lib/hydra/provisioner/machines" ];
example = [
"/etc/nix/machines"
"/var/lib/hydra/provisioner/machines"
];
description = "List of files containing build machines.";
};
@ -234,7 +252,6 @@ in
};
###### implementation
config = lib.mkIf cfg.enable {
@ -253,42 +270,41 @@ in
gid = config.ids.gids.hydra;
};
users.users.hydra =
{ description = "Hydra";
group = "hydra";
# We don't enable `createHome` here because the creation of the home directory is handled by the hydra-init service below.
home = baseDir;
useDefaultShell = true;
uid = config.ids.uids.hydra;
};
users.users.hydra = {
description = "Hydra";
group = "hydra";
# We don't enable `createHome` here because the creation of the home directory is handled by the hydra-init service below.
home = baseDir;
useDefaultShell = true;
uid = config.ids.uids.hydra;
};
users.users.hydra-queue-runner =
{ description = "Hydra queue runner";
group = "hydra";
useDefaultShell = true;
home = "${baseDir}/queue-runner"; # really only to keep SSH happy
uid = config.ids.uids.hydra-queue-runner;
};
users.users.hydra-queue-runner = {
description = "Hydra queue runner";
group = "hydra";
useDefaultShell = true;
home = "${baseDir}/queue-runner"; # really only to keep SSH happy
uid = config.ids.uids.hydra-queue-runner;
};
users.users.hydra-www =
{ description = "Hydra web server";
group = "hydra";
useDefaultShell = true;
uid = config.ids.uids.hydra-www;
};
users.users.hydra-www = {
description = "Hydra web server";
group = "hydra";
useDefaultShell = true;
uid = config.ids.uids.hydra-www;
};
services.hydra.extraConfig =
''
using_frontend_proxy = 1
base_uri = ${cfg.hydraURL}
notification_sender = ${cfg.notificationSender}
max_servers = ${toString cfg.maxServers}
${lib.optionalString (cfg.logo != null) ''
hydra_logo = ${cfg.logo}
''}
gc_roots_dir = ${cfg.gcRootsDir}
use-substitutes = ${if cfg.useSubstitutes then "1" else "0"}
'';
services.hydra.extraConfig = ''
using_frontend_proxy = 1
base_uri = ${cfg.hydraURL}
notification_sender = ${cfg.notificationSender}
max_servers = ${toString cfg.maxServers}
${lib.optionalString (cfg.logo != null) ''
hydra_logo = ${cfg.logo}
''}
gc_roots_dir = ${cfg.gcRootsDir}
use-substitutes = ${if cfg.useSubstitutes then "1" else "0"}
'';
environment.systemPackages = [ hydra-package ];
@ -301,247 +317,264 @@ in
trusted-users = [ "hydra-queue-runner" ];
}
(lib.mkIf (lib.versionOlder (lib.getVersion config.nix.package.out) "2.4pre")
{
# The default (`true') slows Nix down a lot since the build farm
# has so many GC roots.
gc-check-reachability = false;
}
)
(lib.mkIf (lib.versionOlder (lib.getVersion config.nix.package.out) "2.4pre") {
# The default (`true') slows Nix down a lot since the build farm
# has so many GC roots.
gc-check-reachability = false;
})
];
systemd.slices.system-hydra = {
description = "Hydra CI Server Slice";
documentation = [ "file://${cfg.package}/share/doc/hydra/index.html" "https://nixos.org/hydra/manual/" ];
documentation = [
"file://${cfg.package}/share/doc/hydra/index.html"
"https://nixos.org/hydra/manual/"
];
};
systemd.services.hydra-init =
{ wantedBy = [ "multi-user.target" ];
requires = lib.optional haveLocalDB "postgresql.service";
after = lib.optional haveLocalDB "postgresql.service";
environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-init";
};
path = [ pkgs.util-linux ];
preStart = ''
mkdir -p ${baseDir}
chown hydra:hydra ${baseDir}
chmod 0750 ${baseDir}
systemd.services.hydra-init = {
wantedBy = [ "multi-user.target" ];
requires = lib.optional haveLocalDB "postgresql.service";
after = lib.optional haveLocalDB "postgresql.service";
environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-init";
};
path = [ pkgs.util-linux ];
preStart = ''
mkdir -p ${baseDir}
chown hydra:hydra ${baseDir}
chmod 0750 ${baseDir}
ln -sf ${hydraConf} ${baseDir}/hydra.conf
ln -sf ${hydraConf} ${baseDir}/hydra.conf
mkdir -m 0700 ${baseDir}/www || true
chown hydra-www:hydra ${baseDir}/www
mkdir -m 0700 ${baseDir}/www || true
chown hydra-www:hydra ${baseDir}/www
mkdir -m 0700 ${baseDir}/queue-runner || true
mkdir -m 0750 ${baseDir}/build-logs || true
mkdir -m 0750 ${baseDir}/runcommand-logs || true
chown hydra-queue-runner:hydra \
${baseDir}/queue-runner \
${baseDir}/build-logs \
${baseDir}/runcommand-logs
mkdir -m 0700 ${baseDir}/queue-runner || true
mkdir -m 0750 ${baseDir}/build-logs || true
mkdir -m 0750 ${baseDir}/runcommand-logs || true
chown hydra-queue-runner:hydra \
${baseDir}/queue-runner \
${baseDir}/build-logs \
${baseDir}/runcommand-logs
${lib.optionalString haveLocalDB ''
if ! [ -e ${baseDir}/.db-created ]; then
runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser hydra
runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -- -O hydra hydra
touch ${baseDir}/.db-created
fi
echo "create extension if not exists pg_trgm" | runuser -u ${config.services.postgresql.superUser} -- ${config.services.postgresql.package}/bin/psql hydra
''}
${lib.optionalString haveLocalDB ''
if ! [ -e ${baseDir}/.db-created ]; then
runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser hydra
runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -- -O hydra hydra
touch ${baseDir}/.db-created
fi
echo "create extension if not exists pg_trgm" | runuser -u ${config.services.postgresql.superUser} -- ${config.services.postgresql.package}/bin/psql hydra
''}
if [ ! -e ${cfg.gcRootsDir} ]; then
if [ ! -e ${cfg.gcRootsDir} ]; then
# Move legacy roots directory.
if [ -e /nix/var/nix/gcroots/per-user/hydra/hydra-roots ]; then
mv /nix/var/nix/gcroots/per-user/hydra/hydra-roots ${cfg.gcRootsDir}
fi
mkdir -p ${cfg.gcRootsDir}
# Move legacy roots directory.
if [ -e /nix/var/nix/gcroots/per-user/hydra/hydra-roots ]; then
mv /nix/var/nix/gcroots/per-user/hydra/hydra-roots ${cfg.gcRootsDir}
fi
# Move legacy hydra-www roots.
if [ -e /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots ]; then
find /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots/ -type f -print0 \
| xargs -0 -r mv -f -t ${cfg.gcRootsDir}/
rmdir /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots
fi
mkdir -p ${cfg.gcRootsDir}
fi
chown hydra:hydra ${cfg.gcRootsDir}
chmod 2775 ${cfg.gcRootsDir}
'';
serviceConfig.ExecStart = "${hydra-package}/bin/hydra-init";
serviceConfig.PermissionsStartOnly = true;
serviceConfig.User = "hydra";
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
serviceConfig.Slice = "system-hydra.slice";
# Move legacy hydra-www roots.
if [ -e /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots ]; then
find /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots/ -type f -print0 \
| xargs -0 -r mv -f -t ${cfg.gcRootsDir}/
rmdir /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots
fi
chown hydra:hydra ${cfg.gcRootsDir}
chmod 2775 ${cfg.gcRootsDir}
'';
serviceConfig.ExecStart = "${hydra-package}/bin/hydra-init";
serviceConfig.PermissionsStartOnly = true;
serviceConfig.User = "hydra";
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
serviceConfig.Slice = "system-hydra.slice";
};
systemd.services.hydra-server = {
wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ];
environment = serverEnv // {
HYDRA_DBI = "${serverEnv.HYDRA_DBI};application_name=hydra-server";
};
systemd.services.hydra-server =
{ wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ];
environment = serverEnv // {
HYDRA_DBI = "${serverEnv.HYDRA_DBI};application_name=hydra-server";
};
restartTriggers = [ hydraConf ];
serviceConfig =
{ ExecStart =
"@${hydra-package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' "
+ "-p ${toString cfg.port} --min_spare_servers ${toString cfg.minSpareServers} --max_spare_servers ${toString cfg.maxSpareServers} "
+ "--max_servers ${toString cfg.maxServers} --max_requests 100 ${lib.optionalString cfg.debugServer "-d"}";
User = "hydra-www";
PermissionsStartOnly = true;
Restart = "always";
Slice = "system-hydra.slice";
};
restartTriggers = [ hydraConf ];
serviceConfig = {
ExecStart =
"@${hydra-package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' "
+ "-p ${toString cfg.port} --min_spare_servers ${toString cfg.minSpareServers} --max_spare_servers ${toString cfg.maxSpareServers} "
+ "--max_servers ${toString cfg.maxServers} --max_requests 100 ${lib.optionalString cfg.debugServer "-d"}";
User = "hydra-www";
PermissionsStartOnly = true;
Restart = "always";
Slice = "system-hydra.slice";
};
};
systemd.services.hydra-queue-runner =
{ wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" "network.target" ];
path = [ hydra-package pkgs.nettools pkgs.openssh pkgs.bzip2 config.nix.package ];
restartTriggers = [ hydraConf ];
environment = env // {
PGPASSFILE = "${baseDir}/pgpass-queue-runner"; # grrr
IN_SYSTEMD = "1"; # to get log severity levels
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-queue-runner";
};
serviceConfig =
{ ExecStart = "@${hydra-package}/bin/hydra-queue-runner hydra-queue-runner -v";
ExecStopPost = "${hydra-package}/bin/hydra-queue-runner --unlock";
User = "hydra-queue-runner";
Restart = "always";
Slice = "system-hydra.slice";
# Ensure we can get core dumps.
LimitCORE = "infinity";
WorkingDirectory = "${baseDir}/queue-runner";
};
systemd.services.hydra-queue-runner = {
wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [
"hydra-init.service"
"network.target"
];
path = [
hydra-package
pkgs.nettools
pkgs.openssh
pkgs.bzip2
config.nix.package
];
restartTriggers = [ hydraConf ];
environment = env // {
PGPASSFILE = "${baseDir}/pgpass-queue-runner"; # grrr
IN_SYSTEMD = "1"; # to get log severity levels
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-queue-runner";
};
serviceConfig = {
ExecStart = "@${hydra-package}/bin/hydra-queue-runner hydra-queue-runner -v";
ExecStopPost = "${hydra-package}/bin/hydra-queue-runner --unlock";
User = "hydra-queue-runner";
Restart = "always";
Slice = "system-hydra.slice";
systemd.services.hydra-evaluator =
{ wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
wants = [ "network-online.target" ];
after = [ "hydra-init.service" "network.target" "network-online.target" ];
path = with pkgs; [ hydra-package nettools jq ];
restartTriggers = [ hydraConf ];
environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-evaluator";
};
serviceConfig =
{ ExecStart = "@${hydra-package}/bin/hydra-evaluator hydra-evaluator";
User = "hydra";
Restart = "always";
WorkingDirectory = baseDir;
Slice = "system-hydra.slice";
};
# Ensure we can get core dumps.
LimitCORE = "infinity";
WorkingDirectory = "${baseDir}/queue-runner";
};
};
systemd.services.hydra-update-gc-roots =
{ requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ];
environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-update-gc-roots";
};
serviceConfig =
{ ExecStart = "@${hydra-package}/bin/hydra-update-gc-roots hydra-update-gc-roots";
User = "hydra";
Slice = "system-hydra.slice";
};
startAt = "2,14:15";
systemd.services.hydra-evaluator = {
wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
wants = [ "network-online.target" ];
after = [
"hydra-init.service"
"network.target"
"network-online.target"
];
path = with pkgs; [
hydra-package
nettools
jq
];
restartTriggers = [ hydraConf ];
environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-evaluator";
};
serviceConfig = {
ExecStart = "@${hydra-package}/bin/hydra-evaluator hydra-evaluator";
User = "hydra";
Restart = "always";
WorkingDirectory = baseDir;
Slice = "system-hydra.slice";
};
};
systemd.services.hydra-send-stats =
{ wantedBy = [ "multi-user.target" ];
after = [ "hydra-init.service" ];
environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-send-stats";
};
serviceConfig =
{ ExecStart = "@${hydra-package}/bin/hydra-send-stats hydra-send-stats";
User = "hydra";
Slice = "system-hydra.slice";
};
systemd.services.hydra-update-gc-roots = {
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ];
environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-update-gc-roots";
};
serviceConfig = {
ExecStart = "@${hydra-package}/bin/hydra-update-gc-roots hydra-update-gc-roots";
User = "hydra";
Slice = "system-hydra.slice";
};
startAt = "2,14:15";
};
systemd.services.hydra-notify =
{ wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ];
restartTriggers = [ hydraConf ];
path = [ pkgs.zstd ];
environment = env // {
PGPASSFILE = "${baseDir}/pgpass-queue-runner";
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-notify";
};
serviceConfig =
{ ExecStart = "@${hydra-package}/bin/hydra-notify hydra-notify";
# FIXME: run this under a less privileged user?
User = "hydra-queue-runner";
Restart = "always";
RestartSec = 5;
Slice = "system-hydra.slice";
};
systemd.services.hydra-send-stats = {
wantedBy = [ "multi-user.target" ];
after = [ "hydra-init.service" ];
environment = env // {
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-send-stats";
};
serviceConfig = {
ExecStart = "@${hydra-package}/bin/hydra-send-stats hydra-send-stats";
User = "hydra";
Slice = "system-hydra.slice";
};
};
systemd.services.hydra-notify = {
wantedBy = [ "multi-user.target" ];
requires = [ "hydra-init.service" ];
after = [ "hydra-init.service" ];
restartTriggers = [ hydraConf ];
path = [ pkgs.zstd ];
environment = env // {
PGPASSFILE = "${baseDir}/pgpass-queue-runner";
HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-notify";
};
serviceConfig = {
ExecStart = "@${hydra-package}/bin/hydra-notify hydra-notify";
# FIXME: run this under a less privileged user?
User = "hydra-queue-runner";
Restart = "always";
RestartSec = 5;
Slice = "system-hydra.slice";
};
};
# If there is less than a certain amount of free disk space, stop
# the queue/evaluator to prevent builds from failing or aborting.
systemd.services.hydra-check-space =
{ script =
''
if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFree} * 1024**3)) ]; then
echo "stopping Hydra queue runner due to lack of free space..."
systemctl stop hydra-queue-runner
fi
if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFreeEvaluator} * 1024**3)) ]; then
echo "stopping Hydra evaluator due to lack of free space..."
systemctl stop hydra-evaluator
fi
'';
startAt = "*:0/5";
serviceConfig.Slice = "system-hydra.slice";
};
systemd.services.hydra-check-space = {
script = ''
if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFree} * 1024**3)) ]; then
echo "stopping Hydra queue runner due to lack of free space..."
systemctl stop hydra-queue-runner
fi
if [ $(($(stat -f -c '%a' /nix/store) * $(stat -f -c '%S' /nix/store))) -lt $((${toString cfg.minimumDiskFreeEvaluator} * 1024**3)) ]; then
echo "stopping Hydra evaluator due to lack of free space..."
systemctl stop hydra-evaluator
fi
'';
startAt = "*:0/5";
serviceConfig.Slice = "system-hydra.slice";
};
# Periodically compress build logs. The queue runner compresses
# logs automatically after a step finishes, but this doesn't work
# if the queue runner is stopped prematurely.
systemd.services.hydra-compress-logs =
{ path = [ pkgs.bzip2 pkgs.zstd ];
script =
''
set -eou pipefail
compression=$(sed -nr 's/compress_build_logs_compression = ()/\1/p' ${baseDir}/hydra.conf)
if [[ $compression == "" || $compression == bzip2 ]]; then
compressionCmd=(bzip2)
elif [[ $compression == zstd ]]; then
compressionCmd=(zstd --rm)
fi
find ${baseDir}/build-logs -ignore_readdir_race -type f -name "*.drv" -mtime +3 -size +0c -print0 | xargs -0 -r "''${compressionCmd[@]}" --force --quiet
'';
startAt = "Sun 01:45";
serviceConfig.Slice = "system-hydra.slice";
};
systemd.services.hydra-compress-logs = {
path = [
pkgs.bzip2
pkgs.zstd
];
script = ''
set -eou pipefail
compression=$(sed -nr 's/compress_build_logs_compression = ()/\1/p' ${baseDir}/hydra.conf)
if [[ $compression == "" || $compression == bzip2 ]]; then
compressionCmd=(bzip2)
elif [[ $compression == zstd ]]; then
compressionCmd=(zstd --rm)
fi
find ${baseDir}/build-logs -ignore_readdir_race -type f -name "*.drv" -mtime +3 -size +0c -print0 | xargs -0 -r "''${compressionCmd[@]}" --force --quiet
'';
startAt = "Sun 01:45";
serviceConfig.Slice = "system-hydra.slice";
};
services.postgresql.enable = lib.mkIf haveLocalDB true;
services.postgresql.identMap = lib.optionalString haveLocalDB
''
hydra-users hydra hydra
hydra-users hydra-queue-runner hydra
hydra-users hydra-www hydra
hydra-users root hydra
# The postgres user is used to create the pg_trgm extension for the hydra database
hydra-users postgres postgres
'';
services.postgresql.identMap = lib.optionalString haveLocalDB ''
hydra-users hydra hydra
hydra-users hydra-queue-runner hydra
hydra-users hydra-www hydra
hydra-users root hydra
# The postgres user is used to create the pg_trgm extension for the hydra database
hydra-users postgres postgres
'';
services.postgresql.authentication = lib.optionalString haveLocalDB
''
local hydra all ident map=hydra-users
'';
services.postgresql.authentication = lib.optionalString haveLocalDB ''
local hydra all ident map=hydra-users
'';
};

View File

@ -37,10 +37,13 @@ let
# 2. the module configuration
# 3. the extraConfigFiles from the module options
# 4. the locally writable config file, which couchdb itself writes to
configFiles = [
"${cfg.package}/etc/default.ini"
optionsConfigFile
] ++ cfg.extraConfigFiles ++ [ cfg.configFile ];
configFiles =
[
"${cfg.package}/etc/default.ini"
optionsConfigFile
]
++ cfg.extraConfigFiles
++ [ cfg.configFile ];
executable = "${cfg.package}/bin/couchdb";
in
{

View File

@ -1,10 +1,18 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.ferretdb;
in
{
meta.maintainers = with lib.maintainers; [ julienmalka camillemndn ];
meta.maintainers = with lib.maintainers; [
julienmalka
camillemndn
];
options = {
services.ferretdb = {
@ -22,8 +30,11 @@ in
type = lib.types.submodule {
freeformType = with lib.types; attrsOf str;
options = {
FERRETDB_HANDLER = lib.mkOption {
type = lib.types.enum [ "sqlite" "pg" ];
FERRETDB_HANDLER = lib.mkOption {
type = lib.types.enum [
"sqlite"
"pg"
];
default = "sqlite";
description = "Backend handler";
};
@ -41,7 +52,10 @@ in
};
FERRETDB_TELEMETRY = lib.mkOption {
type = lib.types.enum [ "enable" "disable" ];
type = lib.types.enum [
"enable"
"disable"
];
default = "disable";
description = ''
Enable or disable basic telemetry.
@ -64,38 +78,37 @@ in
};
};
config = lib.mkIf cfg.enable
{
services.ferretdb.settings = { };
config = lib.mkIf cfg.enable {
services.ferretdb.settings = { };
systemd.services.ferretdb = {
description = "FerretDB";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment = cfg.settings;
serviceConfig = {
Type = "simple";
StateDirectory = "ferretdb";
WorkingDirectory = "/var/lib/ferretdb";
ExecStart = "${cfg.package}/bin/ferretdb";
Restart = "on-failure";
ProtectHome = true;
ProtectSystem = "strict";
PrivateTmp = true;
PrivateDevices = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
NoNewPrivileges = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
PrivateMounts = true;
DynamicUser = true;
};
systemd.services.ferretdb = {
description = "FerretDB";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment = cfg.settings;
serviceConfig = {
Type = "simple";
StateDirectory = "ferretdb";
WorkingDirectory = "/var/lib/ferretdb";
ExecStart = "${cfg.package}/bin/ferretdb";
Restart = "on-failure";
ProtectHome = true;
ProtectSystem = "strict";
PrivateTmp = true;
PrivateDevices = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
NoNewPrivileges = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
RemoveIPC = true;
PrivateMounts = true;
DynamicUser = true;
};
};
};
}

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.influxdb;
@ -55,24 +60,32 @@ let
https-enabled = false;
};
graphite = [{
enabled = false;
}];
graphite = [
{
enabled = false;
}
];
udp = [{
enabled = false;
}];
udp = [
{
enabled = false;
}
];
collectd = [{
enabled = false;
typesdb = "${pkgs.collectd-data}/share/collectd/types.db";
database = "collectd_db";
bind-address = ":25826";
}];
collectd = [
{
enabled = false;
typesdb = "${pkgs.collectd-data}/share/collectd/types.db";
database = "collectd_db";
bind-address = ":25826";
}
];
opentsdb = [{
enabled = false;
}];
opentsdb = [
{
enabled = false;
}
];
continuous_queries = {
enabled = true;
@ -93,7 +106,7 @@ let
};
} cfg.extraConfig;
configFile = (pkgs.formats.toml {}).generate "config.toml" configOptions;
configFile = (pkgs.formats.toml { }).generate "config.toml" configOptions;
in
{
@ -130,14 +143,13 @@ in
};
extraConfig = lib.mkOption {
default = {};
default = { };
description = "Extra configuration options for influxdb";
type = lib.types.attrs;
};
};
};
###### implementation
config = lib.mkIf config.services.influxdb.enable {
@ -159,7 +171,9 @@ in
postStart =
let
scheme = if configOptions.http.https-enabled then "-k https" else "http";
bindAddr = (ba: if lib.hasPrefix ":" ba then "127.0.0.1${ba}" else "${ba}")(toString configOptions.http.bind-address);
bindAddr = (ba: if lib.hasPrefix ":" ba then "127.0.0.1${ba}" else "${ba}") (
toString configOptions.http.bind-address
);
in
lib.mkBefore ''
until ${pkgs.curl.bin}/bin/curl -s -o /dev/null ${scheme}://${bindAddr}/ping; do

View File

@ -1,49 +1,137 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.redis;
mkValueString = value:
if value == true then "yes"
else if value == false then "no"
else lib.generators.mkValueStringDefault { } value;
mkValueString =
value:
if value == true then
"yes"
else if value == false then
"no"
else
lib.generators.mkValueStringDefault { } value;
redisConfig = settings: pkgs.writeText "redis.conf" (lib.generators.toKeyValue {
listsAsDuplicateKeys = true;
mkKeyValue = lib.generators.mkKeyValueDefault { inherit mkValueString; } " ";
} settings);
redisConfig =
settings:
pkgs.writeText "redis.conf" (
lib.generators.toKeyValue {
listsAsDuplicateKeys = true;
mkKeyValue = lib.generators.mkKeyValueDefault { inherit mkValueString; } " ";
} settings
);
redisName = name: "redis" + lib.optionalString (name != "") ("-"+name);
redisName = name: "redis" + lib.optionalString (name != "") ("-" + name);
enabledServers = lib.filterAttrs (name: conf: conf.enable) config.services.redis.servers;
in {
in
{
imports = [
(lib.mkRemovedOptionModule [ "services" "redis" "user" ] "The redis module now is hardcoded to the redis user.")
(lib.mkRemovedOptionModule [ "services" "redis" "dbpath" ] "The redis module now uses /var/lib/redis as data directory.")
(lib.mkRemovedOptionModule [ "services" "redis" "dbFilename" ] "The redis module now uses /var/lib/redis/dump.rdb as database dump location.")
(lib.mkRemovedOptionModule [ "services" "redis" "appendOnlyFilename" ] "This option was never used.")
(lib.mkRemovedOptionModule [
"services"
"redis"
"user"
] "The redis module now is hardcoded to the redis user.")
(lib.mkRemovedOptionModule [
"services"
"redis"
"dbpath"
] "The redis module now uses /var/lib/redis as data directory.")
(lib.mkRemovedOptionModule [
"services"
"redis"
"dbFilename"
] "The redis module now uses /var/lib/redis/dump.rdb as database dump location.")
(lib.mkRemovedOptionModule [
"services"
"redis"
"appendOnlyFilename"
] "This option was never used.")
(lib.mkRemovedOptionModule [ "services" "redis" "pidFile" ] "This option was removed.")
(lib.mkRemovedOptionModule [ "services" "redis" "extraConfig" ] "Use services.redis.servers.*.settings instead.")
(lib.mkRenamedOptionModule [ "services" "redis" "enable"] [ "services" "redis" "servers" "" "enable" ])
(lib.mkRenamedOptionModule [ "services" "redis" "port"] [ "services" "redis" "servers" "" "port" ])
(lib.mkRenamedOptionModule [ "services" "redis" "openFirewall"] [ "services" "redis" "servers" "" "openFirewall" ])
(lib.mkRenamedOptionModule [ "services" "redis" "bind"] [ "services" "redis" "servers" "" "bind" ])
(lib.mkRenamedOptionModule [ "services" "redis" "unixSocket"] [ "services" "redis" "servers" "" "unixSocket" ])
(lib.mkRenamedOptionModule [ "services" "redis" "unixSocketPerm"] [ "services" "redis" "servers" "" "unixSocketPerm" ])
(lib.mkRenamedOptionModule [ "services" "redis" "logLevel"] [ "services" "redis" "servers" "" "logLevel" ])
(lib.mkRenamedOptionModule [ "services" "redis" "logfile"] [ "services" "redis" "servers" "" "logfile" ])
(lib.mkRenamedOptionModule [ "services" "redis" "syslog"] [ "services" "redis" "servers" "" "syslog" ])
(lib.mkRenamedOptionModule [ "services" "redis" "databases"] [ "services" "redis" "servers" "" "databases" ])
(lib.mkRenamedOptionModule [ "services" "redis" "maxclients"] [ "services" "redis" "servers" "" "maxclients" ])
(lib.mkRenamedOptionModule [ "services" "redis" "save"] [ "services" "redis" "servers" "" "save" ])
(lib.mkRenamedOptionModule [ "services" "redis" "slaveOf"] [ "services" "redis" "servers" "" "slaveOf" ])
(lib.mkRenamedOptionModule [ "services" "redis" "masterAuth"] [ "services" "redis" "servers" "" "masterAuth" ])
(lib.mkRenamedOptionModule [ "services" "redis" "requirePass"] [ "services" "redis" "servers" "" "requirePass" ])
(lib.mkRenamedOptionModule [ "services" "redis" "requirePassFile"] [ "services" "redis" "servers" "" "requirePassFile" ])
(lib.mkRenamedOptionModule [ "services" "redis" "appendOnly"] [ "services" "redis" "servers" "" "appendOnly" ])
(lib.mkRenamedOptionModule [ "services" "redis" "appendFsync"] [ "services" "redis" "servers" "" "appendFsync" ])
(lib.mkRenamedOptionModule [ "services" "redis" "slowLogLogSlowerThan"] [ "services" "redis" "servers" "" "slowLogLogSlowerThan" ])
(lib.mkRenamedOptionModule [ "services" "redis" "slowLogMaxLen"] [ "services" "redis" "servers" "" "slowLogMaxLen" ])
(lib.mkRenamedOptionModule [ "services" "redis" "settings"] [ "services" "redis" "servers" "" "settings" ])
(lib.mkRemovedOptionModule [
"services"
"redis"
"extraConfig"
] "Use services.redis.servers.*.settings instead.")
(lib.mkRenamedOptionModule
[ "services" "redis" "enable" ]
[ "services" "redis" "servers" "" "enable" ]
)
(lib.mkRenamedOptionModule [ "services" "redis" "port" ] [ "services" "redis" "servers" "" "port" ])
(lib.mkRenamedOptionModule
[ "services" "redis" "openFirewall" ]
[ "services" "redis" "servers" "" "openFirewall" ]
)
(lib.mkRenamedOptionModule [ "services" "redis" "bind" ] [ "services" "redis" "servers" "" "bind" ])
(lib.mkRenamedOptionModule
[ "services" "redis" "unixSocket" ]
[ "services" "redis" "servers" "" "unixSocket" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "unixSocketPerm" ]
[ "services" "redis" "servers" "" "unixSocketPerm" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "logLevel" ]
[ "services" "redis" "servers" "" "logLevel" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "logfile" ]
[ "services" "redis" "servers" "" "logfile" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "syslog" ]
[ "services" "redis" "servers" "" "syslog" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "databases" ]
[ "services" "redis" "servers" "" "databases" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "maxclients" ]
[ "services" "redis" "servers" "" "maxclients" ]
)
(lib.mkRenamedOptionModule [ "services" "redis" "save" ] [ "services" "redis" "servers" "" "save" ])
(lib.mkRenamedOptionModule
[ "services" "redis" "slaveOf" ]
[ "services" "redis" "servers" "" "slaveOf" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "masterAuth" ]
[ "services" "redis" "servers" "" "masterAuth" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "requirePass" ]
[ "services" "redis" "servers" "" "requirePass" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "requirePassFile" ]
[ "services" "redis" "servers" "" "requirePassFile" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "appendOnly" ]
[ "services" "redis" "servers" "" "appendOnly" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "appendFsync" ]
[ "services" "redis" "servers" "" "appendFsync" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "slowLogLogSlowerThan" ]
[ "services" "redis" "servers" "" "slowLogLogSlowerThan" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "slowLogMaxLen" ]
[ "services" "redis" "servers" "" "slowLogMaxLen" ]
)
(lib.mkRenamedOptionModule
[ "services" "redis" "settings" ]
[ "services" "redis" "servers" "" "settings" ]
)
];
###### interface
@ -53,365 +141,435 @@ in {
services.redis = {
package = lib.mkPackageOption pkgs "redis" { };
vmOverCommit = lib.mkEnableOption ''
set `vm.overcommit_memory` sysctl to 1
(Suggested for Background Saving: <https://redis.io/docs/get-started/faq/>)
'' // { default = true; };
vmOverCommit =
lib.mkEnableOption ''
set `vm.overcommit_memory` sysctl to 1
(Suggested for Background Saving: <https://redis.io/docs/get-started/faq/>)
''
// {
default = true;
};
servers = lib.mkOption {
type = with lib.types; attrsOf (submodule ({ config, name, ... }: {
options = {
enable = lib.mkEnableOption "Redis server";
user = lib.mkOption {
type = types.str;
default = redisName name;
defaultText = lib.literalExpression ''
if name == "" then "redis" else "redis-''${name}"
'';
description = ''
User account under which this instance of redis-server runs.
::: {.note}
If left as the default value this user will automatically be
created on system activation, otherwise you are responsible for
ensuring the user exists before the redis service starts.
'';
};
group = lib.mkOption {
type = types.str;
default = config.user;
defaultText = lib.literalExpression "config.user";
description = ''
Group account under which this instance of redis-server runs.
::: {.note}
If left as the default value this group will automatically be
created on system activation, otherwise you are responsible for
ensuring the group exists before the redis service starts.
'';
};
port = lib.mkOption {
type = types.port;
default = if name == "" then 6379 else 0;
defaultText = lib.literalExpression ''if name == "" then 6379 else 0'';
description = ''
The TCP port to accept connections.
If port 0 is specified Redis will not listen on a TCP socket.
'';
};
openFirewall = lib.mkOption {
type = types.bool;
default = false;
description = ''
Whether to open ports in the firewall for the server.
'';
};
extraParams = lib.mkOption {
type = with types; listOf str;
default = [];
description = "Extra parameters to append to redis-server invocation";
example = [ "--sentinel" ];
};
bind = lib.mkOption {
type = with types; nullOr str;
default = "127.0.0.1";
description = ''
The IP interface to bind to.
`null` means "all interfaces".
'';
example = "192.0.2.1";
};
unixSocket = lib.mkOption {
type = with types; nullOr path;
default = "/run/${redisName name}/redis.sock";
defaultText = lib.literalExpression ''
if name == "" then "/run/redis/redis.sock" else "/run/redis-''${name}/redis.sock"
'';
description = "The path to the socket to bind to.";
};
unixSocketPerm = lib.mkOption {
type = types.int;
default = 660;
description = "Change permissions for the socket";
example = 600;
};
logLevel = lib.mkOption {
type = types.str;
default = "notice"; # debug, verbose, notice, warning
example = "debug";
description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
};
logfile = lib.mkOption {
type = types.str;
default = "/dev/null";
description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
example = "/var/log/redis.log";
};
syslog = lib.mkOption {
type = types.bool;
default = true;
description = "Enable logging to the system logger.";
};
databases = lib.mkOption {
type = types.int;
default = 16;
description = "Set the number of databases.";
};
maxclients = lib.mkOption {
type = types.int;
default = 10000;
description = "Set the max number of connected clients at the same time.";
};
save = lib.mkOption {
type = with types; listOf (listOf int);
default = [ [900 1] [300 10] [60 10000] ];
description = ''
The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.
If set to the empty list (`[]`) then RDB persistence will be disabled (useful if you are using AOF or don't want any persistence).
'';
};
slaveOf = lib.mkOption {
type = with types; nullOr (submodule ({ ... }: {
type =
with lib.types;
attrsOf (
submodule (
{ config, name, ... }:
{
options = {
ip = lib.mkOption {
type = str;
description = "IP of the Redis master";
example = "192.168.1.100";
enable = lib.mkEnableOption "Redis server";
user = lib.mkOption {
type = types.str;
default = redisName name;
defaultText = lib.literalExpression ''
if name == "" then "redis" else "redis-''${name}"
'';
description = ''
User account under which this instance of redis-server runs.
::: {.note}
If left as the default value this user will automatically be
created on system activation, otherwise you are responsible for
ensuring the user exists before the redis service starts.
'';
};
group = lib.mkOption {
type = types.str;
default = config.user;
defaultText = lib.literalExpression "config.user";
description = ''
Group account under which this instance of redis-server runs.
::: {.note}
If left as the default value this group will automatically be
created on system activation, otherwise you are responsible for
ensuring the group exists before the redis service starts.
'';
};
port = lib.mkOption {
type = port;
description = "port of the Redis master";
default = 6379;
type = types.port;
default = if name == "" then 6379 else 0;
defaultText = lib.literalExpression ''if name == "" then 6379 else 0'';
description = ''
The TCP port to accept connections.
If port 0 is specified Redis will not listen on a TCP socket.
'';
};
openFirewall = lib.mkOption {
type = types.bool;
default = false;
description = ''
Whether to open ports in the firewall for the server.
'';
};
extraParams = lib.mkOption {
type = with types; listOf str;
default = [ ];
description = "Extra parameters to append to redis-server invocation";
example = [ "--sentinel" ];
};
bind = lib.mkOption {
type = with types; nullOr str;
default = "127.0.0.1";
description = ''
The IP interface to bind to.
`null` means "all interfaces".
'';
example = "192.0.2.1";
};
unixSocket = lib.mkOption {
type = with types; nullOr path;
default = "/run/${redisName name}/redis.sock";
defaultText = lib.literalExpression ''
if name == "" then "/run/redis/redis.sock" else "/run/redis-''${name}/redis.sock"
'';
description = "The path to the socket to bind to.";
};
unixSocketPerm = lib.mkOption {
type = types.int;
default = 660;
description = "Change permissions for the socket";
example = 600;
};
logLevel = lib.mkOption {
type = types.str;
default = "notice"; # debug, verbose, notice, warning
example = "debug";
description = "Specify the server verbosity level, options: debug, verbose, notice, warning.";
};
logfile = lib.mkOption {
type = types.str;
default = "/dev/null";
description = "Specify the log file name. Also 'stdout' can be used to force Redis to log on the standard output.";
example = "/var/log/redis.log";
};
syslog = lib.mkOption {
type = types.bool;
default = true;
description = "Enable logging to the system logger.";
};
databases = lib.mkOption {
type = types.int;
default = 16;
description = "Set the number of databases.";
};
maxclients = lib.mkOption {
type = types.int;
default = 10000;
description = "Set the max number of connected clients at the same time.";
};
save = lib.mkOption {
type = with types; listOf (listOf int);
default = [
[
900
1
]
[
300
10
]
[
60
10000
]
];
description = ''
The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.
If set to the empty list (`[]`) then RDB persistence will be disabled (useful if you are using AOF or don't want any persistence).
'';
};
slaveOf = lib.mkOption {
type =
with types;
nullOr (
submodule (
{ ... }:
{
options = {
ip = lib.mkOption {
type = str;
description = "IP of the Redis master";
example = "192.168.1.100";
};
port = lib.mkOption {
type = port;
description = "port of the Redis master";
default = 6379;
};
};
}
)
);
default = null;
description = "IP and port to which this redis instance acts as a slave.";
example = {
ip = "192.168.1.100";
port = 6379;
};
};
masterAuth = lib.mkOption {
type = with types; nullOr str;
default = null;
description = ''
If the master is password protected (using the requirePass configuration)
it is possible to tell the slave to authenticate before starting the replication synchronization
process, otherwise the master will refuse the slave request.
(STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
};
requirePass = lib.mkOption {
type = with types; nullOr str;
default = null;
description = ''
Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
Use requirePassFile to store it outside of the nix store in a dedicated file.
'';
example = "letmein!";
};
requirePassFile = lib.mkOption {
type = with types; nullOr path;
default = null;
description = "File with password for the database.";
example = "/run/keys/redis-password";
};
appendOnly = lib.mkOption {
type = types.bool;
default = false;
description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
};
appendFsync = lib.mkOption {
type = types.str;
default = "everysec"; # no, always, everysec
description = "How often to fsync the append-only log, options: no, always, everysec.";
};
slowLogLogSlowerThan = lib.mkOption {
type = types.int;
default = 10000;
description = "Log queries whose execution take longer than X in milliseconds.";
example = 1000;
};
slowLogMaxLen = lib.mkOption {
type = types.int;
default = 128;
description = "Maximum number of items to keep in slow log.";
};
settings = lib.mkOption {
# TODO: this should be converted to freeformType
type =
with types;
attrsOf (oneOf [
bool
int
str
(listOf str)
]);
default = { };
description = ''
Redis configuration. Refer to
<https://redis.io/topics/config>
for details on supported values.
'';
example = lib.literalExpression ''
{
loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
}
'';
};
};
}));
default = null;
description = "IP and port to which this redis instance acts as a slave.";
example = { ip = "192.168.1.100"; port = 6379; };
};
masterAuth = lib.mkOption {
type = with types; nullOr str;
default = null;
description = ''
If the master is password protected (using the requirePass configuration)
it is possible to tell the slave to authenticate before starting the replication synchronization
process, otherwise the master will refuse the slave request.
(STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE)'';
};
requirePass = lib.mkOption {
type = with types; nullOr str;
default = null;
description = ''
Password for database (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE).
Use requirePassFile to store it outside of the nix store in a dedicated file.
'';
example = "letmein!";
};
requirePassFile = lib.mkOption {
type = with types; nullOr path;
default = null;
description = "File with password for the database.";
example = "/run/keys/redis-password";
};
appendOnly = lib.mkOption {
type = types.bool;
default = false;
description = "By default data is only periodically persisted to disk, enable this option to use an append-only file for improved persistence.";
};
appendFsync = lib.mkOption {
type = types.str;
default = "everysec"; # no, always, everysec
description = "How often to fsync the append-only log, options: no, always, everysec.";
};
slowLogLogSlowerThan = lib.mkOption {
type = types.int;
default = 10000;
description = "Log queries whose execution take longer than X in milliseconds.";
example = 1000;
};
slowLogMaxLen = lib.mkOption {
type = types.int;
default = 128;
description = "Maximum number of items to keep in slow log.";
};
settings = lib.mkOption {
# TODO: this should be converted to freeformType
type = with types; attrsOf (oneOf [ bool int str (listOf str) ]);
default = {};
description = ''
Redis configuration. Refer to
<https://redis.io/topics/config>
for details on supported values.
'';
example = lib.literalExpression ''
{
loadmodule = [ "/path/to/my_module.so" "/path/to/other_module.so" ];
}
'';
};
};
config.settings = lib.mkMerge [
{
inherit (config) port logfile databases maxclients appendOnly;
daemonize = false;
supervised = "systemd";
loglevel = config.logLevel;
syslog-enabled = config.syslog;
save = if config.save == []
then ''""'' # Disable saving with `save = ""`
else map
(d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}")
config.save;
dbfilename = "dump.rdb";
dir = "/var/lib/${redisName name}";
appendfsync = config.appendFsync;
slowlog-log-slower-than = config.slowLogLogSlowerThan;
slowlog-max-len = config.slowLogMaxLen;
}
(lib.mkIf (config.bind != null) { inherit (config) bind; })
(lib.mkIf (config.unixSocket != null) {
unixsocket = config.unixSocket;
unixsocketperm = toString config.unixSocketPerm;
})
(lib.mkIf (config.slaveOf != null) { slaveof = "${config.slaveOf.ip} ${toString config.slaveOf.port}"; })
(lib.mkIf (config.masterAuth != null) { masterauth = config.masterAuth; })
(lib.mkIf (config.requirePass != null) { requirepass = config.requirePass; })
];
}));
config.settings = lib.mkMerge [
{
inherit (config)
port
logfile
databases
maxclients
appendOnly
;
daemonize = false;
supervised = "systemd";
loglevel = config.logLevel;
syslog-enabled = config.syslog;
save =
if config.save == [ ] then
''""'' # Disable saving with `save = ""`
else
map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") config.save;
dbfilename = "dump.rdb";
dir = "/var/lib/${redisName name}";
appendfsync = config.appendFsync;
slowlog-log-slower-than = config.slowLogLogSlowerThan;
slowlog-max-len = config.slowLogMaxLen;
}
(lib.mkIf (config.bind != null) { inherit (config) bind; })
(lib.mkIf (config.unixSocket != null) {
unixsocket = config.unixSocket;
unixsocketperm = toString config.unixSocketPerm;
})
(lib.mkIf (config.slaveOf != null) {
slaveof = "${config.slaveOf.ip} ${toString config.slaveOf.port}";
})
(lib.mkIf (config.masterAuth != null) { masterauth = config.masterAuth; })
(lib.mkIf (config.requirePass != null) { requirepass = config.requirePass; })
];
}
)
);
description = "Configuration of multiple `redis-server` instances.";
default = {};
default = { };
};
};
};
###### implementation
config = lib.mkIf (enabledServers != {}) {
config = lib.mkIf (enabledServers != { }) {
assertions = lib.attrValues (lib.mapAttrs (name: conf: {
assertion = conf.requirePass != null -> conf.requirePassFile == null;
message = ''
You can only set one services.redis.servers.${name}.requirePass
or services.redis.servers.${name}.requirePassFile
'';
}) enabledServers);
assertions = lib.attrValues (
lib.mapAttrs (name: conf: {
assertion = conf.requirePass != null -> conf.requirePassFile == null;
message = ''
You can only set one services.redis.servers.${name}.requirePass
or services.redis.servers.${name}.requirePassFile
'';
}) enabledServers
);
boot.kernel.sysctl = lib.mkIf cfg.vmOverCommit {
"vm.overcommit_memory" = "1";
};
networking.firewall.allowedTCPPorts = lib.concatMap (conf:
lib.optional conf.openFirewall conf.port
networking.firewall.allowedTCPPorts = lib.concatMap (
conf: lib.optional conf.openFirewall conf.port
) (lib.attrValues enabledServers);
environment.systemPackages = [ cfg.package ];
users.users = lib.mapAttrs' (name: conf: lib.nameValuePair (redisName name) {
description = "System user for the redis-server instance ${name}";
isSystemUser = true;
group = redisName name;
}) enabledServers;
users.groups = lib.mapAttrs' (name: conf: lib.nameValuePair (redisName name) {
}) enabledServers;
users.users = lib.mapAttrs' (
name: conf:
lib.nameValuePair (redisName name) {
description = "System user for the redis-server instance ${name}";
isSystemUser = true;
group = redisName name;
}
) enabledServers;
users.groups = lib.mapAttrs' (
name: conf:
lib.nameValuePair (redisName name) {
}
) enabledServers;
systemd.services = lib.mapAttrs' (name: conf: lib.nameValuePair (redisName name) {
description = "Redis Server - ${redisName name}";
systemd.services = lib.mapAttrs' (
name: conf:
lib.nameValuePair (redisName name) {
description = "Redis Server - ${redisName name}";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/${cfg.package.serverBin or "redis-server"} /var/lib/${redisName name}/redis.conf ${lib.escapeShellArgs conf.extraParams}";
ExecStartPre = "+"+pkgs.writeShellScript "${redisName name}-prep-conf" (let
redisConfVar = "/var/lib/${redisName name}/redis.conf";
redisConfRun = "/run/${redisName name}/nixos.conf";
redisConfStore = redisConfig conf.settings;
in ''
touch "${redisConfVar}" "${redisConfRun}"
chown '${conf.user}':'${conf.group}' "${redisConfVar}" "${redisConfRun}"
chmod 0600 "${redisConfVar}" "${redisConfRun}"
if [ ! -s ${redisConfVar} ]; then
echo 'include "${redisConfRun}"' > "${redisConfVar}"
fi
echo 'include "${redisConfStore}"' > "${redisConfRun}"
${lib.optionalString (conf.requirePassFile != null) ''
{
echo -n "requirepass "
cat ${lib.escapeShellArg conf.requirePassFile}
} >> "${redisConfRun}"
''}
'');
Type = "notify";
# User and group
User = conf.user;
Group = conf.group;
# Runtime directory and mode
RuntimeDirectory = redisName name;
RuntimeDirectoryMode = "0750";
# State directory and mode
StateDirectory = redisName name;
StateDirectoryMode = "0700";
# Access write directories
UMask = "0077";
# Capabilities
CapabilityBoundingSet = "";
# Security
NoNewPrivileges = true;
# Process Properties
LimitNOFILE = lib.mkDefault "${toString (conf.maxclients + 32)}";
# Sandboxing
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateUsers = true;
ProtectClock = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
RestrictNamespaces = true;
LockPersonality = true;
# we need to disable MemoryDenyWriteExecute for keydb
MemoryDenyWriteExecute = cfg.package.pname != "keydb";
RestrictRealtime = true;
RestrictSUIDSGID = true;
PrivateMounts = true;
# System Call Filtering
SystemCallArchitectures = "native";
SystemCallFilter = "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @privileged @resources @setuid";
};
}) enabledServers;
serviceConfig = {
ExecStart = "${cfg.package}/bin/${
cfg.package.serverBin or "redis-server"
} /var/lib/${redisName name}/redis.conf ${lib.escapeShellArgs conf.extraParams}";
ExecStartPre =
"+"
+ pkgs.writeShellScript "${redisName name}-prep-conf" (
let
redisConfVar = "/var/lib/${redisName name}/redis.conf";
redisConfRun = "/run/${redisName name}/nixos.conf";
redisConfStore = redisConfig conf.settings;
in
''
touch "${redisConfVar}" "${redisConfRun}"
chown '${conf.user}':'${conf.group}' "${redisConfVar}" "${redisConfRun}"
chmod 0600 "${redisConfVar}" "${redisConfRun}"
if [ ! -s ${redisConfVar} ]; then
echo 'include "${redisConfRun}"' > "${redisConfVar}"
fi
echo 'include "${redisConfStore}"' > "${redisConfRun}"
${lib.optionalString (conf.requirePassFile != null) ''
{
echo -n "requirepass "
cat ${lib.escapeShellArg conf.requirePassFile}
} >> "${redisConfRun}"
''}
''
);
Type = "notify";
# User and group
User = conf.user;
Group = conf.group;
# Runtime directory and mode
RuntimeDirectory = redisName name;
RuntimeDirectoryMode = "0750";
# State directory and mode
StateDirectory = redisName name;
StateDirectoryMode = "0700";
# Access write directories
UMask = "0077";
# Capabilities
CapabilityBoundingSet = "";
# Security
NoNewPrivileges = true;
# Process Properties
LimitNOFILE = lib.mkDefault "${toString (conf.maxclients + 32)}";
# Sandboxing
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
PrivateDevices = true;
PrivateUsers = true;
ProtectClock = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
LockPersonality = true;
# we need to disable MemoryDenyWriteExecute for keydb
MemoryDenyWriteExecute = cfg.package.pname != "keydb";
RestrictRealtime = true;
RestrictSUIDSGID = true;
PrivateMounts = true;
# System Call Filtering
SystemCallArchitectures = "native";
SystemCallFilter = "~@cpu-emulation @debug @keyring @memlock @mount @obsolete @privileged @resources @setuid";
};
}
) enabledServers;
};
}

View File

@ -1,5 +1,10 @@
# Bamf
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
{
meta = with lib; {
maintainers = with lib.maintainers; [ ];

View File

@ -1,5 +1,10 @@
# rygel service.
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let

View File

@ -1,5 +1,10 @@
# Accounts-SSO gSignOn daemon
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
package = pkgs.gsignond.override { plugins = config.services.gsignond.plugins; };
in
@ -24,7 +29,7 @@ in
plugins = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [];
default = [ ];
description = ''
What plugins to use with the gSignOn daemon.
'';

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (builtins) concatMap;
@ -8,7 +13,13 @@ let
inherit (lib.modules) mkIf;
inherit (lib.options) literalExpression mkOption;
inherit (lib.strings) concatStringsSep makeSearchPath;
inherit (lib.types) bool listOf attrsOf package lines;
inherit (lib.types)
bool
listOf
attrsOf
package
lines
;
inherit (lib.path) subpath;
pwCfg = config.services.pipewire;
@ -17,24 +28,29 @@ let
json = pkgs.formats.json { };
configSectionsToConfFile = path: value:
pkgs.writeTextDir
path
(concatStringsSep "\n" (
mapAttrsToList
(section: content: "${section} = " + (builtins.toJSON content))
value
));
configSectionsToConfFile =
path: value:
pkgs.writeTextDir path (
concatStringsSep "\n" (
mapAttrsToList (section: content: "${section} = " + (builtins.toJSON content)) value
)
);
mapConfigToFiles = config:
mapAttrsToList
(name: value: configSectionsToConfFile "share/wireplumber/wireplumber.conf.d/${name}.conf" value)
config;
mapConfigToFiles =
config:
mapAttrsToList (
name: value: configSectionsToConfFile "share/wireplumber/wireplumber.conf.d/${name}.conf" value
) config;
mapScriptsToFiles = scripts:
mapAttrsToList
(relativePath: value: pkgs.writeTextDir (subpath.join ["share/wireplumber/scripts" relativePath]) value)
scripts;
mapScriptsToFiles =
scripts:
mapAttrsToList (
relativePath: value:
pkgs.writeTextDir (subpath.join [
"share/wireplumber/scripts"
relativePath
]) value
) scripts;
in
{
meta.maintainers = [ maintainers.k900 ];
@ -62,34 +78,34 @@ in
type = attrsOf (attrsOf json.type);
default = { };
example = literalExpression ''
{
"log-level-debug" = {
"context.properties" = {
# Output Debug log messages as opposed to only the default level (Notice)
"log.level" = "D";
{
"log-level-debug" = {
"context.properties" = {
# Output Debug log messages as opposed to only the default level (Notice)
"log.level" = "D";
};
};
};
"wh-1000xm3-ldac-hq" = {
"monitor.bluez.rules" = [
{
matches = [
{
# Match any bluetooth device with ids equal to that of a WH-1000XM3
"device.name" = "~bluez_card.*";
"device.product.id" = "0x0cd3";
"device.vendor.id" = "usb:054c";
}
];
actions = {
update-props = {
# Set quality to high quality instead of the default of auto
"bluez5.a2dp.ldac.quality" = "hq";
"wh-1000xm3-ldac-hq" = {
"monitor.bluez.rules" = [
{
matches = [
{
# Match any bluetooth device with ids equal to that of a WH-1000XM3
"device.name" = "~bluez_card.*";
"device.product.id" = "0x0cd3";
"device.vendor.id" = "usb:054c";
}
];
actions = {
update-props = {
# Set quality to high quality instead of the default of auto
"bluez5.a2dp.ldac.quality" = "hq";
};
};
};
}
];
};
}
}
];
};
}
'';
description = ''
Additional configuration for the WirePlumber daemon when run in
@ -169,16 +185,16 @@ in
type = listOf package;
default = [ ];
example = literalExpression ''
[
(pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/10-bluez.conf" '''
monitor.bluez.properties = {
bluez5.roles = [ a2dp_sink a2dp_source bap_sink bap_source hsp_hs hsp_ag hfp_hf hfp_ag ]
bluez5.codecs = [ sbc sbc_xq aac ]
bluez5.enable-sbc-xq = true
bluez5.hfphsp-backend = "native"
}
''')
]
[
(pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/10-bluez.conf" '''
monitor.bluez.properties = {
bluez5.roles = [ a2dp_sink a2dp_source bap_sink bap_source hsp_hs hsp_ag hfp_hf hfp_ag ]
bluez5.codecs = [ sbc sbc_xq aac ]
bluez5.enable-sbc-xq = true
bluez5.hfphsp-backend = "native"
}
''')
]
'';
description = ''
List of packages that provide WirePlumber configuration, in the form of
@ -231,8 +247,12 @@ in
pathsToLink = [ "/share/wireplumber/scripts" ];
};
configPackages = cfg.configPackages
++ [ extraConfigPkg extraScriptsPkg ]
configPackages =
cfg.configPackages
++ [
extraConfigPkg
extraScriptsPkg
]
++ optional (!pwUsedForAudio) pwNotForAudioConfigPkg;
configs = pkgs.buildEnv {
@ -241,14 +261,9 @@ in
pathsToLink = [ "/share/wireplumber" ];
};
requiredLv2Packages = flatten
(
concatMap
(p:
attrByPath [ "passthru" "requiredLv2Packages" ] [ ] p
)
configPackages
);
requiredLv2Packages = flatten (
concatMap (p: attrByPath [ "passthru" "requiredLv2Packages" ] [ ] p) configPackages
);
lv2Plugins = pkgs.buildEnv {
name = "wireplumber-lv2-plugins";
@ -280,12 +295,18 @@ in
# Make WirePlumber find our config/script files and lv2 plugins required by those
# (but also the configs/scripts shipped with WirePlumber)
XDG_DATA_DIRS = makeSearchPath "share" [ configs cfg.package ];
XDG_DATA_DIRS = makeSearchPath "share" [
configs
cfg.package
];
LV2_PATH = "${lv2Plugins}/lib/lv2";
};
systemd.user.services.wireplumber.environment = mkIf (!pwCfg.systemWide) {
XDG_DATA_DIRS = makeSearchPath "share" [ configs cfg.package ];
XDG_DATA_DIRS = makeSearchPath "share" [
configs
cfg.package
];
LV2_PATH = "${lv2Plugins}/lib/lv2";
};
};

View File

@ -1,148 +1,151 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.athens;
athensConfig = lib.flip lib.recursiveUpdate cfg.extraConfig (
{
GoBinary = "${cfg.goBinary}/bin/go";
GoEnv = cfg.goEnv;
GoBinaryEnvVars = lib.mapAttrsToList (k: v: "${k}=${v}") cfg.goBinaryEnvVars;
GoGetWorkers = cfg.goGetWorkers;
GoGetDir = cfg.goGetDir;
ProtocolWorkers = cfg.protocolWorkers;
LogLevel = cfg.logLevel;
CloudRuntime = cfg.cloudRuntime;
EnablePprof = cfg.enablePprof;
PprofPort = ":${toString cfg.pprofPort}";
FilterFile = cfg.filterFile;
RobotsFile = cfg.robotsFile;
Timeout = cfg.timeout;
StorageType = cfg.storageType;
TLSCertFile = cfg.tlsCertFile;
TLSKeyFile = cfg.tlsKeyFile;
Port = ":${toString cfg.port}";
UnixSocket = cfg.unixSocket;
GlobalEndpoint = cfg.globalEndpoint;
BasicAuthUser = cfg.basicAuthUser;
BasicAuthPass = cfg.basicAuthPass;
ForceSSL = cfg.forceSSL;
ValidatorHook = cfg.validatorHook;
PathPrefix = cfg.pathPrefix;
NETRCPath = cfg.netrcPath;
GithubToken = cfg.githubToken;
HGRCPath = cfg.hgrcPath;
TraceExporter = cfg.traceExporter;
StatsExporter = cfg.statsExporter;
SumDBs = cfg.sumDBs;
NoSumPatterns = cfg.noSumPatterns;
DownloadMode = cfg.downloadMode;
NetworkMode = cfg.networkMode;
DownloadURL = cfg.downloadURL;
SingleFlightType = cfg.singleFlightType;
IndexType = cfg.indexType;
ShutdownTimeout = cfg.shutdownTimeout;
SingleFlight = {
Etcd = {
Endpoints = builtins.concatStringsSep "," cfg.singleFlight.etcd.endpoints;
};
Redis = {
Endpoint = cfg.singleFlight.redis.endpoint;
Password = cfg.singleFlight.redis.password;
LockConfig = {
TTL = cfg.singleFlight.redis.lockConfig.ttl;
Timeout = cfg.singleFlight.redis.lockConfig.timeout;
MaxRetries = cfg.singleFlight.redis.lockConfig.maxRetries;
};
};
RedisSentinel = {
Endpoints = cfg.singleFlight.redisSentinel.endpoints;
MasterName = cfg.singleFlight.redisSentinel.masterName;
SentinelPassword = cfg.singleFlight.redisSentinel.sentinelPassword;
LockConfig = {
TTL = cfg.singleFlight.redisSentinel.lockConfig.ttl;
Timeout = cfg.singleFlight.redisSentinel.lockConfig.timeout;
MaxRetries = cfg.singleFlight.redisSentinel.lockConfig.maxRetries;
};
athensConfig = lib.flip lib.recursiveUpdate cfg.extraConfig ({
GoBinary = "${cfg.goBinary}/bin/go";
GoEnv = cfg.goEnv;
GoBinaryEnvVars = lib.mapAttrsToList (k: v: "${k}=${v}") cfg.goBinaryEnvVars;
GoGetWorkers = cfg.goGetWorkers;
GoGetDir = cfg.goGetDir;
ProtocolWorkers = cfg.protocolWorkers;
LogLevel = cfg.logLevel;
CloudRuntime = cfg.cloudRuntime;
EnablePprof = cfg.enablePprof;
PprofPort = ":${toString cfg.pprofPort}";
FilterFile = cfg.filterFile;
RobotsFile = cfg.robotsFile;
Timeout = cfg.timeout;
StorageType = cfg.storageType;
TLSCertFile = cfg.tlsCertFile;
TLSKeyFile = cfg.tlsKeyFile;
Port = ":${toString cfg.port}";
UnixSocket = cfg.unixSocket;
GlobalEndpoint = cfg.globalEndpoint;
BasicAuthUser = cfg.basicAuthUser;
BasicAuthPass = cfg.basicAuthPass;
ForceSSL = cfg.forceSSL;
ValidatorHook = cfg.validatorHook;
PathPrefix = cfg.pathPrefix;
NETRCPath = cfg.netrcPath;
GithubToken = cfg.githubToken;
HGRCPath = cfg.hgrcPath;
TraceExporter = cfg.traceExporter;
StatsExporter = cfg.statsExporter;
SumDBs = cfg.sumDBs;
NoSumPatterns = cfg.noSumPatterns;
DownloadMode = cfg.downloadMode;
NetworkMode = cfg.networkMode;
DownloadURL = cfg.downloadURL;
SingleFlightType = cfg.singleFlightType;
IndexType = cfg.indexType;
ShutdownTimeout = cfg.shutdownTimeout;
SingleFlight = {
Etcd = {
Endpoints = builtins.concatStringsSep "," cfg.singleFlight.etcd.endpoints;
};
Redis = {
Endpoint = cfg.singleFlight.redis.endpoint;
Password = cfg.singleFlight.redis.password;
LockConfig = {
TTL = cfg.singleFlight.redis.lockConfig.ttl;
Timeout = cfg.singleFlight.redis.lockConfig.timeout;
MaxRetries = cfg.singleFlight.redis.lockConfig.maxRetries;
};
};
Storage = {
CDN = {
Endpoint = cfg.storage.cdn.endpoint;
};
Disk = {
RootPath = cfg.storage.disk.rootPath;
};
GCP = {
ProjectID = cfg.storage.gcp.projectID;
Bucket = cfg.storage.gcp.bucket;
JSONKey = cfg.storage.gcp.jsonKey;
};
Minio = {
Endpoint = cfg.storage.minio.endpoint;
Key = cfg.storage.minio.key;
Secret = cfg.storage.minio.secret;
EnableSSL = cfg.storage.minio.enableSSL;
Bucket = cfg.storage.minio.bucket;
region = cfg.storage.minio.region;
};
Mongo = {
URL = cfg.storage.mongo.url;
DefaultDBName = cfg.storage.mongo.defaultDBName;
CertPath = cfg.storage.mongo.certPath;
Insecure = cfg.storage.mongo.insecure;
};
S3 = {
Region = cfg.storage.s3.region;
Key = cfg.storage.s3.key;
Secret = cfg.storage.s3.secret;
Token = cfg.storage.s3.token;
Bucket = cfg.storage.s3.bucket;
ForcePathStyle = cfg.storage.s3.forcePathStyle;
UseDefaultConfiguration = cfg.storage.s3.useDefaultConfiguration;
CredentialsEndpoint = cfg.storage.s3.credentialsEndpoint;
AwsContainerCredentialsRelativeURI = cfg.storage.s3.awsContainerCredentialsRelativeURI;
Endpoint = cfg.storage.s3.endpoint;
};
AzureBlob = {
AccountName = cfg.storage.azureblob.accountName;
AccountKey = cfg.storage.azureblob.accountKey;
ContainerName = cfg.storage.azureblob.containerName;
};
External = {
URL = cfg.storage.external.url;
RedisSentinel = {
Endpoints = cfg.singleFlight.redisSentinel.endpoints;
MasterName = cfg.singleFlight.redisSentinel.masterName;
SentinelPassword = cfg.singleFlight.redisSentinel.sentinelPassword;
LockConfig = {
TTL = cfg.singleFlight.redisSentinel.lockConfig.ttl;
Timeout = cfg.singleFlight.redisSentinel.lockConfig.timeout;
MaxRetries = cfg.singleFlight.redisSentinel.lockConfig.maxRetries;
};
};
Index = {
MySQL = {
Protocol = cfg.index.mysql.protocol;
Host = cfg.index.mysql.host;
Port = cfg.index.mysql.port;
User = cfg.index.mysql.user;
Password = cfg.index.mysql.password;
Database = cfg.index.mysql.database;
Params = {
parseTime = cfg.index.mysql.params.parseTime;
timeout = cfg.index.mysql.params.timeout;
};
};
Postgres = {
Host = cfg.index.postgres.host;
Port = cfg.index.postgres.port;
User = cfg.index.postgres.user;
Password = cfg.index.postgres.password;
Database = cfg.index.postgres.database;
Params = {
connect_timeout = cfg.index.postgres.params.connect_timeout;
sslmode = cfg.index.postgres.params.sslmode;
};
};
Storage = {
CDN = {
Endpoint = cfg.storage.cdn.endpoint;
};
Disk = {
RootPath = cfg.storage.disk.rootPath;
};
GCP = {
ProjectID = cfg.storage.gcp.projectID;
Bucket = cfg.storage.gcp.bucket;
JSONKey = cfg.storage.gcp.jsonKey;
};
Minio = {
Endpoint = cfg.storage.minio.endpoint;
Key = cfg.storage.minio.key;
Secret = cfg.storage.minio.secret;
EnableSSL = cfg.storage.minio.enableSSL;
Bucket = cfg.storage.minio.bucket;
region = cfg.storage.minio.region;
};
Mongo = {
URL = cfg.storage.mongo.url;
DefaultDBName = cfg.storage.mongo.defaultDBName;
CertPath = cfg.storage.mongo.certPath;
Insecure = cfg.storage.mongo.insecure;
};
S3 = {
Region = cfg.storage.s3.region;
Key = cfg.storage.s3.key;
Secret = cfg.storage.s3.secret;
Token = cfg.storage.s3.token;
Bucket = cfg.storage.s3.bucket;
ForcePathStyle = cfg.storage.s3.forcePathStyle;
UseDefaultConfiguration = cfg.storage.s3.useDefaultConfiguration;
CredentialsEndpoint = cfg.storage.s3.credentialsEndpoint;
AwsContainerCredentialsRelativeURI = cfg.storage.s3.awsContainerCredentialsRelativeURI;
Endpoint = cfg.storage.s3.endpoint;
};
AzureBlob = {
AccountName = cfg.storage.azureblob.accountName;
AccountKey = cfg.storage.azureblob.accountKey;
ContainerName = cfg.storage.azureblob.containerName;
};
External = {
URL = cfg.storage.external.url;
};
};
Index = {
MySQL = {
Protocol = cfg.index.mysql.protocol;
Host = cfg.index.mysql.host;
Port = cfg.index.mysql.port;
User = cfg.index.mysql.user;
Password = cfg.index.mysql.password;
Database = cfg.index.mysql.database;
Params = {
parseTime = cfg.index.mysql.params.parseTime;
timeout = cfg.index.mysql.params.timeout;
};
};
}
);
Postgres = {
Host = cfg.index.postgres.host;
Port = cfg.index.postgres.port;
User = cfg.index.postgres.user;
Password = cfg.index.postgres.password;
Database = cfg.index.postgres.database;
Params = {
connect_timeout = cfg.index.postgres.params.connect_timeout;
sslmode = cfg.index.postgres.params.sslmode;
};
};
};
});
configFile = lib.pipe athensConfig [
(lib.filterAttrsRecursive (_k: v: v != null))
((pkgs.formats.toml {}).generate "config.toml")
((pkgs.formats.toml { }).generate "config.toml")
];
in
{
@ -177,7 +180,10 @@ in
};
goEnv = lib.mkOption {
type = lib.types.enum [ "development" "production" ];
type = lib.types.enum [
"development"
"production"
];
description = "Specifies the type of environment to run. One of 'development' or 'production'.";
default = "development";
example = "production";
@ -220,7 +226,17 @@ in
};
logLevel = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [ "panic" "fatal" "error" "warning" "info" "debug" "trace" ]);
type = lib.types.nullOr (
lib.types.enum [
"panic"
"fatal"
"error"
"warning"
"info"
"debug"
"trace"
]
);
description = ''
Log level for Athens.
Supports all logrus log levels (https://github.com/Sirupsen/logrus#level-logging)".
@ -230,7 +246,10 @@ in
};
cloudRuntime = lib.mkOption {
type = lib.types.enum [ "GCP" "none" ];
type = lib.types.enum [
"GCP"
"none"
];
description = ''
Specifies the Cloud Provider on which the Proxy/registry is running.
'';
@ -279,7 +298,16 @@ in
};
storageType = lib.mkOption {
type = lib.types.enum [ "memory" "disk" "mongo" "gcp" "minio" "s3" "azureblob" "external" ];
type = lib.types.enum [
"memory"
"disk"
"mongo"
"gcp"
"minio"
"s3"
"azureblob"
"external"
];
description = "Specifies the type of storage backend to use.";
default = "disk";
};
@ -401,7 +429,12 @@ in
};
traceExporter = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [ "jaeger" "datadog" ]);
type = lib.types.nullOr (
lib.types.enum [
"jaeger"
"datadog"
]
);
description = ''
Trace exporter to use.
'';
@ -442,7 +475,16 @@ in
};
downloadMode = lib.mkOption {
type = lib.types.oneOf [ (lib.types.enum [ "sync" "async" "redirect" "async_redirect" "none" ]) (lib.types.strMatching "^file:.*$|^custom:.*$") ];
type = lib.types.oneOf [
(lib.types.enum [
"sync"
"async"
"redirect"
"async_redirect"
"none"
])
(lib.types.strMatching "^file:.*$|^custom:.*$")
];
description = ''
Defines how Athens behaves when a module@version
is not found in storage. There are 7 options:
@ -466,7 +508,11 @@ in
};
networkMode = lib.mkOption {
type = lib.types.enum [ "strict" "offline" "fallback" ];
type = lib.types.enum [
"strict"
"offline"
"fallback"
];
description = ''
Configures how Athens will return the results
of the /list endpoint as it can be assembled from both its own
@ -492,7 +538,14 @@ in
};
singleFlightType = lib.mkOption {
type = lib.types.enum [ "memory" "etcd" "redis" "redis-sentinel" "gcp" "azureblob" ];
type = lib.types.enum [
"memory"
"etcd"
"redis"
"redis-sentinel"
"gcp"
"azureblob"
];
description = ''
Determines what mechanism Athens uses to manage concurrency flowing into the Athens backend.
'';
@ -500,7 +553,12 @@ in
};
indexType = lib.mkOption {
type = lib.types.enum [ "none" "memory" "mysql" "postgres" ];
type = lib.types.enum [
"none"
"memory"
"mysql"
"postgres"
];
description = ''
Type of index backend Athens will use.
'';
@ -913,8 +971,12 @@ in
ProtectHome = "read-only";
ProtectSystem = "full";
ReadWritePaths = lib.mkIf (cfg.storage.disk.rootPath != null && (! lib.hasPrefix "/var/lib/" cfg.storage.disk.rootPath)) [ cfg.storage.disk.rootPath ];
StateDirectory = lib.mkIf (lib.hasPrefix "/var/lib/" cfg.storage.disk.rootPath) [ (lib.removePrefix "/var/lib/" cfg.storage.disk.rootPath) ];
ReadWritePaths = lib.mkIf (
cfg.storage.disk.rootPath != null && (!lib.hasPrefix "/var/lib/" cfg.storage.disk.rootPath)
) [ cfg.storage.disk.rootPath ];
StateDirectory = lib.mkIf (lib.hasPrefix "/var/lib/" cfg.storage.disk.rootPath) [
(lib.removePrefix "/var/lib/" cfg.storage.disk.rootPath)
];
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
@ -923,7 +985,8 @@ in
};
networking.firewall = {
allowedTCPPorts = lib.optionals (cfg.unixSocket == null) [ cfg.port ]
allowedTCPPorts =
lib.optionals (cfg.unixSocket == null) [ cfg.port ]
++ lib.optionals cfg.enablePprof [ cfg.pprofPort ];
};
};

View File

@ -1,13 +1,18 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.jupyterhub;
kernels = (pkgs.jupyter-kernel.create {
definitions = if cfg.kernels != null
then cfg.kernels
else pkgs.jupyter-kernel.default;
});
kernels = (
pkgs.jupyter-kernel.create {
definitions = if cfg.kernels != null then cfg.kernels else pkgs.jupyter-kernel.default;
}
);
jupyterhubConfig = pkgs.writeText "jupyterhub_config.py" ''
c.JupyterHub.bind_url = "http://${cfg.host}:${toString cfg.port}"
@ -23,7 +28,8 @@ let
${cfg.extraConfig}
'';
in {
in
{
meta.maintainers = with lib.maintainers; [ costrouc ];
options.services.jupyterhub = {
@ -71,10 +77,12 @@ in {
jupyterhubEnv = lib.mkOption {
type = lib.types.package;
default = pkgs.python3.withPackages (p: with p; [
jupyterhub
jupyterhub-systemdspawner
]);
default = pkgs.python3.withPackages (
p: with p; [
jupyterhub
jupyterhub-systemdspawner
]
);
defaultText = lib.literalExpression ''
pkgs.python3.withPackages (p: with p; [
jupyterhub
@ -93,10 +101,12 @@ in {
jupyterlabEnv = lib.mkOption {
type = lib.types.package;
default = pkgs.python3.withPackages (p: with p; [
jupyterhub
jupyterlab
]);
default = pkgs.python3.withPackages (
p: with p; [
jupyterhub
jupyterlab
]
);
defaultText = lib.literalExpression ''
pkgs.python3.withPackages (p: with p; [
jupyterhub
@ -115,9 +125,15 @@ in {
};
kernels = lib.mkOption {
type = lib.types.nullOr (lib.types.attrsOf(lib.types.submodule (import ../jupyter/kernel-options.nix {
inherit lib pkgs;
})));
type = lib.types.nullOr (
lib.types.attrsOf (
lib.types.submodule (
import ../jupyter/kernel-options.nix {
inherit lib pkgs;
}
)
)
);
default = null;
example = lib.literalExpression ''
@ -179,7 +195,7 @@ in {
};
config = lib.mkMerge [
(lib.mkIf cfg.enable {
(lib.mkIf cfg.enable {
systemd.services.jupyterhub = {
description = "Jupyterhub development server";

View File

@ -1,4 +1,9 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.greetd;
tty = "tty${toString cfg.vt}";
@ -79,12 +84,14 @@ in
Wants = [
"systemd-user-sessions.service"
];
After = [
"systemd-user-sessions.service"
"getty@${tty}.service"
] ++ lib.optionals (!cfg.greeterManagesPlymouth) [
"plymouth-quit-wait.service"
];
After =
[
"systemd-user-sessions.service"
"getty@${tty}.service"
]
++ lib.optionals (!cfg.greeterManagesPlymouth) [
"plymouth-quit-wait.service"
];
Conflicts = [
"getty@${tty}.service"
];

View File

@ -87,6 +87,7 @@
config.services.taler.enable && (config.services.taler.settings.taler ? CURRENCY)
) config.services.taler.settings.taler.CURRENCY;
services.libeufin.bank.settings.libeufin-bankdb-postgres.CONFIG = lib.mkIf config.services.libeufin.bank.createLocalDatabase "postgresql:///libeufin-bank";
services.libeufin.bank.settings.libeufin-bankdb-postgres.CONFIG =
lib.mkIf config.services.libeufin.bank.createLocalDatabase "postgresql:///libeufin-bank";
};
}

Some files were not shown because too many files have changed in this diff Show More