···2233`pkgs.checkpointBuildTools` provides a way to build derivations incrementally. It consists of two functions to make checkpoint builds using Nix possible.4455-For hermeticity, Nix derivations do not allow any state to carry over between builds, making a transparent incremental build within a derivation impossible.55+For hermeticity, Nix derivations do not allow any state to be carried over between builds, making a transparent incremental build within a derivation impossible.6677However, we can tell Nix explicitly what the previous build state was, by representing that previous state as a derivation output. This allows the passed build state to be used for an incremental build.8899To change a normal derivation to a checkpoint based build, these steps must be taken:1010- - apply `prepareCheckpointBuild` on the desired derivation1111- e.g.:1010+ - apply `prepareCheckpointBuild` on the desired derivation, e.g.1211```nix1312checkpointArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);1413```1515- - change something you want in the sources of the package. (e.g. using a source override)1414+ - change something you want in the sources of the package, e.g. use a source override:1615```nix1716changedVBox = pkgs.virtualbox.overrideAttrs (old: {1817 src = path/to/vbox/sources;1919-}1818+});2019```2121- - use `mkCheckpointedBuild changedVBox buildOutput`2020+ - use `mkCheckpointBuild changedVBox checkpointArtifacts`2221 - enjoy shorter build times23222423## Example {#sec-checkpoint-build-example}2524```nix2626-{ pkgs ? import <nixpkgs> {} }: with (pkgs) checkpointBuildTools;2525+{ pkgs ? import <nixpkgs> {} }:2726let2828- helloCheckpoint = checkpointBuildTools.prepareCheckpointBuild pkgs.hello;2727+ inherit (pkgs.checkpointBuildTools)2828+ prepareCheckpointBuild2929+ mkCheckpointBuild3030+ ;3131+ helloCheckpoint = prepareCheckpointBuild pkgs.hello;2932 changedHello = pkgs.hello.overrideAttrs (_: {3033 doCheck = false;3134 patchPhase = ''3235 sed -i 's/Hello, world!/Hello, Nix!/g' src/hello.c3336 '';3437 });3535-in checkpointBuildTools.mkCheckpointBuild changedHello helloCheckpoint3838+in mkCheckpointBuild changedHello helloCheckpoint3639```
···11+{ pkgs, config, lib, ... }:22+33+let44+ cfg = config.services.snmpd;55+ configFile = if cfg.configText != "" then66+ pkgs.writeText "snmpd.cfg" ''77+ ${cfg.configText}88+ '' else null;99+in {1010+ options.services.snmpd = {1111+ enable = lib.mkEnableOption "snmpd";1212+1313+ package = lib.mkPackageOption pkgs "net-snmp" {};1414+1515+ listenAddress = lib.mkOption {1616+ type = lib.types.str;1717+ default = "0.0.0.0";1818+ description = lib.mdDoc ''1919+ The address to listen on for SNMP and AgentX messages.2020+ '';2121+ example = "127.0.0.1";2222+ };2323+2424+ port = lib.mkOption {2525+ type = lib.types.port;2626+ default = 161;2727+ description = lib.mdDoc ''2828+ The port to listen on for SNMP and AgentX messages.2929+ '';3030+ };3131+3232+ openFirewall = lib.mkOption {3333+ type = lib.types.bool;3434+ default = false;3535+ description = lib.mdDoc ''3636+ Open port in firewall for snmpd.3737+ '';3838+ };3939+4040+ configText = lib.mkOption {4141+ type = lib.types.lines;4242+ default = "";4343+ description = lib.mdDoc ''4444+ The contents of the snmpd.conf. If the {option}`configFile` option4545+ is set, this value will be ignored.4646+4747+ Note that the contents of this option will be added to the Nix4848+ store as world-readable plain text, {option}`configFile` can be used in4949+ addition to a secret management tool to protect sensitive data.5050+ '';5151+ };5252+5353+ configFile = lib.mkOption {5454+ type = lib.types.path;5555+ default = configFile;5656+ defaultText = lib.literalMD "The value of {option}`configText`.";5757+ description = lib.mdDoc ''5858+ Path to the snmpd.conf file. By default, if {option}`configText` is set,5959+ a config file will be automatically generated.6060+ '';6161+ };6262+6363+ };6464+6565+ config = lib.mkIf cfg.enable {6666+ systemd.services."snmpd" = {6767+ description = "Simple Network Management Protocol (SNMP) daemon.";6868+ after = [ "network.target" ];6969+ wantedBy = [ "multi-user.target" ];7070+ serviceConfig = {7171+ Type = "simple";7272+ ExecStart = "${lib.getExe' cfg.package "snmpd"} -f -Lo -c ${cfg.configFile} ${cfg.listenAddress}:${toString cfg.port}";7373+ };7474+ };7575+7676+ networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [7777+ cfg.port7878+ ];7979+ };8080+8181+ meta.maintainers = [ lib.maintainers.eliandoran ];8282+8383+}
+4-1
nixos/modules/services/printing/cupsd.nix
···4455let6677- inherit (pkgs) cups cups-pk-helper cups-filters xdg-utils;77+ inherit (pkgs) cups-pk-helper cups-filters xdg-utils;8899 cfg = config.services.printing;1010+ cups = cfg.package;10111112 avahiEnabled = config.services.avahi.enable;1213 polkitEnabled = config.security.polkit.enable;···140139 Whether to enable printing support through the CUPS daemon.141140 '';142141 };142142+143143+ package = lib.mkPackageOption pkgs "cups" {};143144144145 stateless = mkOption {145146 type = types.bool;
···2727 "-Wno-dev"2828 ];29293030- preConfigure = ''3131- cp ${obs-studio.src}/cmake/external/ObsPluginHelpers.cmake cmake/FindLibObs.cmake3232- '';3333-3430 meta = with lib; {3531 description = "Audio device and application capture for OBS Studio using PipeWire";3632 homepage = "https://github.com/dimtpap/obs-pipewire-audio-capture";
···11-{ pkgs }:11+{ lib22+, buildPackages33+}:44+55+let66+ # rudimentary support for cross-compiling77+ # see: https://github.com/NixOS/nixpkgs/pull/279487#discussion_r144444972688+ inherit (buildPackages)99+ mktemp1010+ rsync1111+ ;1212+in1313+214rec {315 /* Prepare a derivation for local builds.416 *55- * This function prepares checkpoint builds by provinding,66- * containing the build output and the sources for cross checking.1717+ * This function prepares checkpoint builds by storing1818+ * the build output and the sources for cross checking.719 * The build output can be used later to allow checkpoint builds820 * by passing the derivation output to the `mkCheckpointBuild` function.921 *1010- * To build a project with checkpoints follow these steps:1111- * - run prepareIncrementalBuild on the desired derivation1212- * e.G `incrementalBuildArtifacts = (pkgs.checkpointBuildTools.prepareCheckpointBuild pkgs.virtualbox);`1313- * - change something you want in the sources of the package( e.G using source override)1414- * changedVBox = pkgs.virtuabox.overrideAttrs (old: {1515- * src = path/to/vbox/sources;1616- * }1717- * - use `mkCheckpointedBuild changedVBox buildOutput`2222+ * To build a project with checkpoints, follow these steps:2323+ * - run `prepareCheckpointBuild` on the desired derivation, e.g.2424+ * checkpointArtifacts = prepareCheckpointBuild virtualbox;2525+ * - change something you want in the sources of the package,2626+ * e.g. using source override:2727+ * changedVBox = pkgs.virtuabox.overrideAttrs (old: {2828+ * src = path/to/vbox/sources;2929+ * };3030+ * - use `mkCheckpointBuild changedVBox checkpointArtifacts`1831 * - enjoy shorter build times1932 */2033 prepareCheckpointBuild = drv: drv.overrideAttrs (old: {2134 outputs = [ "out" ];2235 name = drv.name + "-checkpointArtifacts";2336 # To determine differences between the state of the build directory2424- # from an earlier build and a later one we store the state of the build3737+ # from an earlier build and a later one we store the state of the build2538 # directory before build, but after patch phases.2639 # This way, the same derivation can be used multiple times and only changes are detected.2727- # Additionally Removed files are handled correctly in later builds.4040+ # Additionally, removed files are handled correctly in later builds.2841 preBuild = (old.preBuild or "") + ''2942 mkdir -p $out/sources3043 cp -r ./* $out/sources/3144 '';32453333- # After the build the build directory is copied again4646+ # After the build, the build directory is copied again3447 # to get the output files.3535- # We copy the complete build folder, to take care for3636- # Build tools, building in the source directory, instead of3737- # having a build root directory, e.G the Linux kernel.4848+ # We copy the complete build folder, to take care of4949+ # build tools that build in the source directory, instead of5050+ # having a separate build directory such as the Linux kernel.3851 installPhase = ''3952 runHook preCheckpointInstall4053 mkdir -p $out/outputs···5744 });58455946 /* Build a derivation based on the checkpoint output generated by6060- * the `prepareCheckpointBuild function.4747+ * the `prepareCheckpointBuild` function.6148 *6249 * Usage:6350 * let6464- * checkpointArtifacts = prepareCheckpointBuild drv6565- * in mkCheckpointedBuild drv checkpointArtifacts5151+ * checkpointArtifacts = prepareCheckpointBuild drv;5252+ * in mkCheckpointBuild drv checkpointArtifacts6653 */6767- mkCheckpointedBuild = drv: previousBuildArtifacts: drv.overrideAttrs (old: {5454+ mkCheckpointBuild = drv: checkpointArtifacts: drv.overrideAttrs (old: {6855 # The actual checkpoint build phase.6969- # We compare the changed sources from a previous build with the current and create a patch7070- # Afterwards we clean the build directory to copy the previous output files (Including the sources)7171- # The source difference patch is applied to get the latest changes again to allow short build times.5656+ # We compare the changed sources from a previous build with the current and create a patch.5757+ # Afterwards we clean the build directory and copy the previous output files (including the sources).5858+ # The source difference patch is then applied to get the latest changes again to allow short build times.7259 preBuild = (old.preBuild or "") + ''7360 set +e7474- diff -ur ${previousBuildArtifacts}/sources ./ > sourceDifference.patch6161+ sourceDifferencePatchFile=$(${mktemp}/bin/mktemp)6262+ diff -ur ${checkpointArtifacts}/sources ./ > "$sourceDifferencePatchFile"7563 set -e7676- shopt -s extglob dotglob7777- rm -r !("sourceDifference.patch")7878- ${pkgs.rsync}/bin/rsync -cutU --chown=$USER:$USER --chmod=+w -r ${previousBuildArtifacts}/outputs/* .7979- patch -p 1 -i sourceDifference.patch6464+ shopt -s dotglob6565+ rm -r *6666+ ${rsync}/bin/rsync \6767+ --checksum --times --atimes --chown=$USER:$USER --chmod=+w \6868+ -r ${checkpointArtifacts}/outputs/ .6969+ patch -p 1 -i "$sourceDifferencePatchFile"7070+ rm "$sourceDifferencePatchFile"8071 '';8172 });7373+7474+ mkCheckpointedBuild = lib.warn7575+ "`mkCheckpointedBuild` is deprecated, use `mkCheckpointBuild` instead!"7676+ mkCheckpointBuild;8277}
···1010autoAddCudaCompatRunpathPhase() (1111 local outputPaths1212 mapfile -t outputPaths < <(for o in $(getAllOutputNames); do echo "${!o}"; done)1313- find "${outputPaths[@]}" -type f -executable -print0 | while IFS= read -rd "" f; do1313+ find "${outputPaths[@]}" -type f -print0 | while IFS= read -rd "" f; do1414 if isELF "$f"; then1515 # patchelf returns an error on statically linked ELF files1616 if elfHasDynamicSection "$f" ; then
···99autoAddOpenGLRunpathPhase() (1010 local outputPaths1111 mapfile -t outputPaths < <(for o in $(getAllOutputNames); do echo "${!o}"; done)1212- find "${outputPaths[@]}" -type f -executable -print0 | while IFS= read -rd "" f; do1212+ find "${outputPaths[@]}" -type f -print0 | while IFS= read -rd "" f; do1313 if isELF "$f"; then1414 # patchelf returns an error on statically linked ELF files1515 if elfHasDynamicSection "$f" ; then
···2828- Each package directory must not refer to files outside itself using symlinks or Nix path expressions.29293030### Nix evaluation checks3131+3232+Evaluate Nixpkgs with `system` set to `x86_64-linux` and check that:3133- For each package directory, the `pkgs.${name}` attribute must be defined as `callPackage pkgs/by-name/${shard}/${name}/package.nix args` for some `args`.3234- For each package directory, `pkgs.lib.isDerivation pkgs.${name}` must be `true`.3335
+67-56
pkgs/test/nixpkgs-check-by-name/src/eval.nix
···11# Takes a path to nixpkgs and a path to the json-encoded list of attributes to check.22-# Returns an attribute set containing information on each requested attribute.33-# If the attribute is missing from Nixpkgs it's also missing from the result.44-#55-# The returned information is an attribute set with:66-# - call_package_path: The <path> from `<attr> = callPackage <path> { ... }`,77-# or null if it's not defined as with callPackage, or if the <path> is not a path88-# - is_derivation: The result of `lib.isDerivation <attr>`22+# Returns an value containing information on each requested attribute,33+# which is decoded on the Rust side.44+# See ./eval.rs for the meaning of the returned values95{106 attrsPath,117 nixpkgsPath,···913let1014 attrs = builtins.fromJSON (builtins.readFile attrsPath);11151212- # This overlay mocks callPackage to persist the path of the first argument1313- callPackageOverlay = self: super: {1616+ nixpkgsPathLength = builtins.stringLength (toString nixpkgsPath) + 1;1717+ removeNixpkgsPrefix = builtins.substring nixpkgsPathLength (-1);1818+1919+ # We need access to the `callPackage` arguments of each attribute.2020+ # The only way to do so is to override `callPackage` with our own version that adds this information to the result,2121+ # and then try to access this information.2222+ overlay = final: prev: {2323+2424+ # Information for attributes defined using `callPackage`1425 callPackage = fn: args:1515- let1616- result = super.callPackage fn args;1717- variantInfo._attributeVariant = {1818- # These names are used by the deserializer on the Rust side1919- CallPackage.path =2626+ addVariantInfo (prev.callPackage fn args) {2727+ Manual = {2828+ path =2029 if builtins.isPath fn then2121- toString fn3030+ removeNixpkgsPrefix (toString fn)2231 else2332 null;2424- CallPackage.empty_arg =3333+ empty_arg =2534 args == { };2635 };2727- in2828- if builtins.isAttrs result then2929- # If this was the last overlay to be applied, we could just only return the `_callPackagePath`,3030- # but that's not the case because stdenv has another overlays on top of user-provided ones.3131- # So to not break the stdenv build we need to return the mostly proper result here3232- result // variantInfo3333- else3434- # It's very rare that callPackage doesn't return an attribute set, but it can occur.3535- variantInfo;3636+ };36373838+ # Information for attributes that are auto-called from pkgs/by-name.3939+ # This internal attribute is only used by pkgs/by-name3740 _internalCallByNamePackageFile = file:3838- let3939- result = super._internalCallByNamePackageFile file;4040- variantInfo._attributeVariant = {4141- # This name is used by the deserializer on the Rust side4242- AutoCalled = null;4343- };4444- in4545- if builtins.isAttrs result then4646- # If this was the last overlay to be applied, we could just only return the `_callPackagePath`,4747- # but that's not the case because stdenv has another overlays on top of user-provided ones.4848- # So to not break the stdenv build we need to return the mostly proper result here4949- result // variantInfo5050- else5151- # It's very rare that callPackage doesn't return an attribute set, but it can occur.5252- variantInfo;4141+ addVariantInfo (prev._internalCallByNamePackageFile file) {4242+ Auto = null;4343+ };4444+5345 };4646+4747+ # We can't just replace attribute values with their info in the overlay,4848+ # because attributes can depend on other attributes, so this would break evaluation.4949+ addVariantInfo = value: variant:5050+ if builtins.isAttrs value then5151+ value // {5252+ _callPackageVariant = variant;5353+ }5454+ else5555+ # It's very rare that callPackage doesn't return an attribute set, but it can occur.5656+ # In such a case we can't really return anything sensible that would include the info,5757+ # so just don't return the info and let the consumer handle it.5858+ value;54595560 pkgs = import nixpkgsPath {5661 # Don't let the users home directory influence this result5762 config = { };5858- overlays = [ callPackageOverlay ];6363+ overlays = [ overlay ];6464+ # We check evaluation and callPackage only for x86_64-linux.6565+ # Not ideal, but hard to fix6666+ system = "x86_64-linux";5967 };60686161- attrInfo = attr:6262- let6363- value = pkgs.${attr};6464- in6565- {6666- # These names are used by the deserializer on the Rust side6767- variant = value._attributeVariant or { Other = null; };6868- is_derivation = pkgs.lib.isDerivation value;6969- };6969+ attrInfo = name: value:7070+ if ! builtins.isAttrs value then7171+ {7272+ NonAttributeSet = null;7373+ }7474+ else if ! value ? _callPackageVariant then7575+ {7676+ NonCallPackage = null;7777+ }7878+ else7979+ {8080+ CallPackage = {8181+ call_package_variant = value._callPackageVariant;8282+ is_derivation = pkgs.lib.isDerivation value;8383+ };8484+ };70857171- attrInfos = builtins.listToAttrs (map (name: {7272- inherit name;7373- value = attrInfo name;7474- }) attrs);8686+ attrInfos = map (name: [8787+ name8888+ (8989+ if ! pkgs ? ${name} then9090+ { Missing = null; }9191+ else9292+ { Existing = attrInfo name pkgs.${name}; }9393+ )9494+ ]) attrs;75957696in7777-# Filter out attributes not in Nixpkgs7878-builtins.intersectAttrs pkgs attrInfos9797+attrInfos
+113-80
pkgs/test/nixpkgs-check-by-name/src/eval.rs
···6677use anyhow::Context;88use serde::Deserialize;99-use std::collections::HashMap;109use std::path::PathBuf;1110use std::process;1211use tempfile::NamedTempFile;13121413/// Attribute set of this structure is returned by eval.nix1514#[derive(Deserialize)]1616-struct AttributeInfo {1717- variant: AttributeVariant,1515+enum ByNameAttribute {1616+ /// The attribute doesn't exist at all1717+ Missing,1818+ Existing(AttributeInfo),1919+}2020+2121+#[derive(Deserialize)]2222+enum AttributeInfo {2323+ /// The attribute exists, but its value isn't an attribute set2424+ NonAttributeSet,2525+ /// The attribute exists, but its value isn't defined using callPackage2626+ NonCallPackage,2727+ /// The attribute exists and its value is an attribute set2828+ CallPackage(CallPackageInfo),2929+}3030+3131+#[derive(Deserialize)]3232+struct CallPackageInfo {3333+ call_package_variant: CallPackageVariant,3434+ /// Whether the attribute is a derivation (`lib.isDerivation`)1835 is_derivation: bool,1936}20372138#[derive(Deserialize)]2222-enum AttributeVariant {3939+enum CallPackageVariant {2340 /// The attribute is auto-called as pkgs.callPackage using pkgs/by-name,2441 /// and it is not overridden by a definition in all-packages.nix2525- AutoCalled,4242+ Auto,2643 /// The attribute is defined as a pkgs.callPackage <path> <args>,2744 /// and overridden by all-packages.nix2828- CallPackage {4545+ Manual {2946 /// The <path> argument or None if it's not a path3047 path: Option<PathBuf>,3148 /// true if <args> is { }3249 empty_arg: bool,3350 },3434- /// The attribute is not defined as pkgs.callPackage3535- Other,3651}37523853/// Check that the Nixpkgs attribute values corresponding to the packages in pkgs/by-name are···6045) -> validation::Result<ratchet::Nixpkgs> {6146 // Write the list of packages we need to check into a temporary JSON file.6247 // This can then get read by the Nix evaluation.6363- let attrs_file = NamedTempFile::new().context("Failed to create a temporary file")?;4848+ let attrs_file = NamedTempFile::new().with_context(|| "Failed to create a temporary file")?;6449 // We need to canonicalise this path because if it's a symlink (which can be the case on6550 // Darwin), Nix would need to read both the symlink and the target path, therefore need 26651 // NIX_PATH entries for restrict-eval. But if we resolve the symlinks then only one predictable6752 // entry is needed.6853 let attrs_file_path = attrs_file.path().canonicalize()?;69547070- serde_json::to_writer(&attrs_file, &package_names).context(format!(7171- "Failed to serialise the package names to the temporary path {}",7272- attrs_file_path.display()7373- ))?;5555+ serde_json::to_writer(&attrs_file, &package_names).with_context(|| {5656+ format!(5757+ "Failed to serialise the package names to the temporary path {}",5858+ attrs_file_path.display()5959+ )6060+ })?;74617562 let expr_path = std::env::var("NIX_CHECK_BY_NAME_EXPR_PATH")7676- .context("Could not get environment variable NIX_CHECK_BY_NAME_EXPR_PATH")?;6363+ .with_context(|| "Could not get environment variable NIX_CHECK_BY_NAME_EXPR_PATH")?;7764 // With restrict-eval, only paths in NIX_PATH can be accessed, so we explicitly specify the7865 // ones needed needed7966 let mut command = process::Command::new("nix-instantiate");···1149711598 let result = command11699 .output()117117- .context(format!("Failed to run command {command:?}"))?;100100+ .with_context(|| format!("Failed to run command {command:?}"))?;118101119102 if !result.status.success() {120103 anyhow::bail!("Failed to run command {command:?}");121104 }122105 // Parse the resulting JSON value123123- let actual_files: HashMap<String, AttributeInfo> = serde_json::from_slice(&result.stdout)124124- .context(format!(125125- "Failed to deserialise {}",126126- String::from_utf8_lossy(&result.stdout)127127- ))?;106106+ let attributes: Vec<(String, ByNameAttribute)> = serde_json::from_slice(&result.stdout)107107+ .with_context(|| {108108+ format!(109109+ "Failed to deserialise {}",110110+ String::from_utf8_lossy(&result.stdout)111111+ )112112+ })?;128113129129- Ok(130130- validation::sequence(package_names.into_iter().map(|package_name| {131131- let relative_package_file = structure::relative_file_for_package(&package_name);132132- let absolute_package_file = nixpkgs_path.join(&relative_package_file);114114+ let check_result = validation::sequence(attributes.into_iter().map(115115+ |(attribute_name, attribute_value)| {116116+ let relative_package_file = structure::relative_file_for_package(&attribute_name);133117134134- if let Some(attribute_info) = actual_files.get(&package_name) {135135- let check_result = if !attribute_info.is_derivation {136136- NixpkgsProblem::NonDerivation {137137- relative_package_file: relative_package_file.clone(),138138- package_name: package_name.clone(),139139- }140140- .into()141141- } else {142142- Success(())143143- };118118+ use ratchet::RatchetState::*;119119+ use AttributeInfo::*;120120+ use ByNameAttribute::*;121121+ use CallPackageVariant::*;144122145145- let check_result = check_result.and(match &attribute_info.variant {146146- AttributeVariant::AutoCalled => Success(ratchet::Package {147147- empty_non_auto_called: ratchet::EmptyNonAutoCalled::Valid,148148- }),149149- AttributeVariant::CallPackage { path, empty_arg } => {150150- let correct_file = if let Some(call_package_path) = path {151151- absolute_package_file == *call_package_path152152- } else {153153- false154154- };155155-156156- if correct_file {157157- Success(ratchet::Package {158158- // Empty arguments for non-auto-called packages are not allowed anymore.159159- empty_non_auto_called: if *empty_arg {160160- ratchet::EmptyNonAutoCalled::Invalid161161- } else {162162- ratchet::EmptyNonAutoCalled::Valid163163- },164164- })165165- } else {166166- NixpkgsProblem::WrongCallPackage {167167- relative_package_file: relative_package_file.clone(),168168- package_name: package_name.clone(),169169- }170170- .into()171171- }172172- }173173- AttributeVariant::Other => NixpkgsProblem::WrongCallPackage {174174- relative_package_file: relative_package_file.clone(),175175- package_name: package_name.clone(),176176- }177177- .into(),178178- });179179-180180- check_result.map(|value| (package_name.clone(), value))181181- } else {182182- NixpkgsProblem::UndefinedAttr {123123+ let check_result = match attribute_value {124124+ Missing => NixpkgsProblem::UndefinedAttr {183125 relative_package_file: relative_package_file.clone(),184184- package_name: package_name.clone(),126126+ package_name: attribute_name.clone(),185127 }186186- .into()187187- }188188- }))189189- .map(|elems| ratchet::Nixpkgs {190190- packages: elems.into_iter().collect(),191191- }),192192- )128128+ .into(),129129+ Existing(NonAttributeSet) => NixpkgsProblem::NonDerivation {130130+ relative_package_file: relative_package_file.clone(),131131+ package_name: attribute_name.clone(),132132+ }133133+ .into(),134134+ Existing(NonCallPackage) => NixpkgsProblem::WrongCallPackage {135135+ relative_package_file: relative_package_file.clone(),136136+ package_name: attribute_name.clone(),137137+ }138138+ .into(),139139+ Existing(CallPackage(CallPackageInfo {140140+ is_derivation,141141+ call_package_variant,142142+ })) => {143143+ let check_result = if !is_derivation {144144+ NixpkgsProblem::NonDerivation {145145+ relative_package_file: relative_package_file.clone(),146146+ package_name: attribute_name.clone(),147147+ }148148+ .into()149149+ } else {150150+ Success(())151151+ };152152+153153+ check_result.and(match &call_package_variant {154154+ Auto => Success(ratchet::Package {155155+ empty_non_auto_called: Tight,156156+ }),157157+ Manual { path, empty_arg } => {158158+ let correct_file = if let Some(call_package_path) = path {159159+ relative_package_file == *call_package_path160160+ } else {161161+ false162162+ };163163+164164+ if correct_file {165165+ Success(ratchet::Package {166166+ // Empty arguments for non-auto-called packages are not allowed anymore.167167+ empty_non_auto_called: if *empty_arg {168168+ Loose(ratchet::EmptyNonAutoCalled)169169+ } else {170170+ Tight171171+ },172172+ })173173+ } else {174174+ NixpkgsProblem::WrongCallPackage {175175+ relative_package_file: relative_package_file.clone(),176176+ package_name: attribute_name.clone(),177177+ }178178+ .into()179179+ }180180+ }181181+ })182182+ }183183+ };184184+ check_result.map(|value| (attribute_name.clone(), value))185185+ },186186+ ));187187+188188+ Ok(check_result.map(|elems| ratchet::Nixpkgs {189189+ package_names,190190+ package_map: elems.into_iter().collect(),191191+ }))193192}
+20-24
pkgs/test/nixpkgs-check-by-name/src/main.rs
···38383939 /// Path to the base Nixpkgs to run ratchet checks against.4040 /// For PRs, this should be set to a checkout of the PRs base branch.4141- /// If not specified, no ratchet checks will be performed.4242- /// However, this flag will become required once CI uses it.4341 #[arg(long)]4444- base: Option<PathBuf>,4242+ base: PathBuf,4543}46444745fn main() -> ExitCode {4846 let args = Args::parse();4949- match process(args.base.as_deref(), &args.nixpkgs, &[], &mut io::stderr()) {4747+ match process(&args.base, &args.nixpkgs, &[], &mut io::stderr()) {5048 Ok(true) => {5149 eprintln!("{}", "Validated successfully".green());5250 ExitCode::SUCCESS···7577/// - `Ok(false)` if there are problems, all of which will be written to `error_writer`.7678/// - `Ok(true)` if there are no problems7779pub fn process<W: io::Write>(7878- base_nixpkgs: Option<&Path>,8080+ base_nixpkgs: &Path,7981 main_nixpkgs: &Path,8082 eval_accessible_paths: &[&Path],8183 error_writer: &mut W,···8587 let check_result = main_result.result_map(|nixpkgs_version| {8688 // If the main Nixpkgs doesn't have any problems, run the ratchet checks against the base8789 // Nixpkgs8888- if let Some(base) = base_nixpkgs {8989- check_nixpkgs(base, eval_accessible_paths, error_writer)?.result_map(9090- |base_nixpkgs_version| {9191- Ok(ratchet::Nixpkgs::compare(9292- Some(base_nixpkgs_version),9393- nixpkgs_version,9494- ))9595- },9696- )9797- } else {9898- Ok(ratchet::Nixpkgs::compare(None, nixpkgs_version))9999- }9090+ check_nixpkgs(base_nixpkgs, eval_accessible_paths, error_writer)?.result_map(9191+ |base_nixpkgs_version| {9292+ Ok(ratchet::Nixpkgs::compare(9393+ base_nixpkgs_version,9494+ nixpkgs_version,9595+ ))9696+ },9797+ )10098 })?;10199102100 match check_result {···117123 error_writer: &mut W,118124) -> validation::Result<ratchet::Nixpkgs> {119125 Ok({120120- let nixpkgs_path = nixpkgs_path.canonicalize().context(format!(121121- "Nixpkgs path {} could not be resolved",122122- nixpkgs_path.display()123123- ))?;126126+ let nixpkgs_path = nixpkgs_path.canonicalize().with_context(|| {127127+ format!(128128+ "Nixpkgs path {} could not be resolved",129129+ nixpkgs_path.display()130130+ )131131+ })?;124132125133 if !nixpkgs_path.join(utils::BASE_SUBPATH).exists() {126134 writeln!(···230234231235 let base_path = path.join("base");232236 let base_nixpkgs = if base_path.exists() {233233- Some(base_path.as_path())237237+ base_path.as_path()234238 } else {235235- None239239+ Path::new("tests/empty-base")236240 };237241238242 // We don't want coloring to mess up the tests239243 let writer = temp_env::with_var("NO_COLOR", Some("1"), || -> anyhow::Result<_> {240244 let mut writer = vec![];241245 process(base_nixpkgs, &path, &[&extra_nix_path], &mut writer)242242- .context(format!("Failed test case {name}"))?;246246+ .with_context(|| format!("Failed test case {name}"))?;243247 Ok(writer)244248 })?;245249
+57-38
pkgs/test/nixpkgs-check-by-name/src/ratchet.rs
···1010/// The ratchet value for the entirety of Nixpkgs.1111#[derive(Default)]1212pub struct Nixpkgs {1313- /// The ratchet values for each package in `pkgs/by-name`1414- pub packages: HashMap<String, Package>,1313+ /// Sorted list of attributes in package_map1414+ pub package_names: Vec<String>,1515+ /// The ratchet values for all packages1616+ pub package_map: HashMap<String, Package>,1517}16181719impl Nixpkgs {1820 /// Validates the ratchet checks for Nixpkgs1919- pub fn compare(optional_from: Option<Self>, to: Self) -> Validation<()> {2121+ pub fn compare(from: Self, to: Self) -> Validation<()> {2022 validation::sequence_(2123 // We only loop over the current attributes,2224 // we don't need to check ones that were removed2323- to.packages.into_iter().map(|(name, attr_to)| {2424- let attr_from = if let Some(from) = &optional_from {2525- from.packages.get(&name)2626- } else {2727- // This pretends that if there's no base version to compare against, all2828- // attributes existed without conforming to the new strictness check for2929- // backwards compatibility.3030- // TODO: Remove this case. This is only needed because the `--base`3131- // argument is still optional, which doesn't need to be once CI is updated3232- // to pass it.3333- Some(&Package {3434- empty_non_auto_called: EmptyNonAutoCalled::Invalid,3535- })3636- };3737- Package::compare(&name, attr_from, &attr_to)2525+ to.package_names.into_iter().map(|name| {2626+ Package::compare(&name, from.package_map.get(&name), &to.package_map[&name])3827 }),3928 )4029 }···3243/// The ratchet value for a single package in `pkgs/by-name`3344pub struct Package {3445 /// The ratchet value for the check for non-auto-called empty arguments3535- pub empty_non_auto_called: EmptyNonAutoCalled,4646+ pub empty_non_auto_called: RatchetState<EmptyNonAutoCalled>,3647}37483849impl Package {3950 /// Validates the ratchet checks for a single package defined in `pkgs/by-name`4051 pub fn compare(name: &str, optional_from: Option<&Self>, to: &Self) -> Validation<()> {4141- EmptyNonAutoCalled::compare(5252+ RatchetState::<EmptyNonAutoCalled>::compare(4253 name,4354 optional_from.map(|x| &x.empty_non_auto_called),4455 &to.empty_non_auto_called,···4657 }4758}48594949-/// The ratchet value of a single package in `pkgs/by-name`6060+/// The ratchet state of a generic ratchet check.6161+pub enum RatchetState<Context> {6262+ /// The ratchet is loose, it can be tightened more.6363+ /// In other words, this is the legacy state we're trying to move away from.6464+ /// Introducing new instances is not allowed but previous instances will continue to be allowed.6565+ /// The `Context` is context for error messages in case a new instance of this state is6666+ /// introduced6767+ Loose(Context),6868+ /// The ratchet is tight, it can't be tightened any further.6969+ /// This is either because we already use the latest state, or because the ratchet isn't7070+ /// relevant.7171+ Tight,7272+}7373+7474+/// A trait that can convert an attribute-specific error context into a NixpkgsProblem7575+pub trait ToNixpkgsProblem {7676+ /// How to convert an attribute-specific error context into a NixpkgsProblem7777+ fn to_nixpkgs_problem(name: &str, context: &Self, existed_before: bool) -> NixpkgsProblem;7878+}7979+8080+impl<Context: ToNixpkgsProblem> RatchetState<Context> {8181+ /// Compare the previous ratchet state of an attribute to the new state.8282+ /// The previous state may be `None` in case the attribute is new.8383+ fn compare(name: &str, optional_from: Option<&Self>, to: &Self) -> Validation<()> {8484+ // If we don't have a previous state, enforce a tight ratchet8585+ let from = optional_from.unwrap_or(&RatchetState::Tight);8686+ match (from, to) {8787+ // Always okay to keep it tight or tighten the ratchet8888+ (_, RatchetState::Tight) => Success(()),8989+9090+ // Grandfathering policy for a loose ratchet9191+ (RatchetState::Loose { .. }, RatchetState::Loose { .. }) => Success(()),9292+9393+ // Loosening a ratchet is now allowed9494+ (RatchetState::Tight, RatchetState::Loose(context)) => {9595+ Context::to_nixpkgs_problem(name, context, optional_from.is_some()).into()9696+ }9797+ }9898+ }9999+}100100+101101+/// The ratchet value of an attribute50102/// for the non-auto-called empty argument check of a single.51103///52104/// This checks that packages defined in `pkgs/by-name` cannot be overridden53105/// with an empty second argument like `callPackage ... { }`.5454-#[derive(PartialEq, PartialOrd)]5555-pub enum EmptyNonAutoCalled {5656- Invalid,5757- Valid,5858-}106106+pub struct EmptyNonAutoCalled;591076060-impl EmptyNonAutoCalled {6161- /// Validates the non-auto-called empty argument ratchet check for a single package defined in `pkgs/by-name`6262- fn compare(name: &str, optional_from: Option<&Self>, to: &Self) -> Validation<()> {6363- let from = optional_from.unwrap_or(&Self::Valid);6464- if to >= from {6565- Success(())6666- } else {6767- NixpkgsProblem::WrongCallPackage {6868- relative_package_file: structure::relative_file_for_package(name),6969- package_name: name.to_owned(),7070- }7171- .into()108108+impl ToNixpkgsProblem for EmptyNonAutoCalled {109109+ fn to_nixpkgs_problem(name: &str, _context: &Self, _existed_before: bool) -> NixpkgsProblem {110110+ NixpkgsProblem::WrongCallPackage {111111+ relative_package_file: structure::relative_file_for_package(name),112112+ package_name: name.to_owned(),72113 }73114 }74115}
+16-13
pkgs/test/nixpkgs-check-by-name/src/references.rs
···1717) -> validation::Result<()> {1818 // The empty argument here is the subpath under the package directory to check1919 // An empty one means the package directory itself2020- check_path(relative_package_dir, absolute_package_dir, Path::new("")).context(format!(2121- "While checking the references in package directory {}",2222- relative_package_dir.display()2323- ))2020+ check_path(relative_package_dir, absolute_package_dir, Path::new("")).with_context(|| {2121+ format!(2222+ "While checking the references in package directory {}",2323+ relative_package_dir.display()2424+ )2525+ })2426}25272628/// Checks for a specific path to not have references outside···6462 .map(|entry| {6563 let entry_subpath = subpath.join(entry.file_name());6664 check_path(relative_package_dir, absolute_package_dir, &entry_subpath)6767- .context(format!("Error while recursing into {}", subpath.display()))6565+ .with_context(|| {6666+ format!("Error while recursing into {}", subpath.display())6767+ })6868 })6969 .collect_vec()?,7070 )···7470 // Only check Nix files7571 if let Some(ext) = path.extension() {7672 if ext == OsStr::new("nix") {7777- check_nix_file(relative_package_dir, absolute_package_dir, subpath).context(7878- format!("Error while checking Nix file {}", subpath.display()),7373+ check_nix_file(relative_package_dir, absolute_package_dir, subpath).with_context(7474+ || format!("Error while checking Nix file {}", subpath.display()),7975 )?8076 } else {8177 Success(())···9793 subpath: &Path,9894) -> validation::Result<()> {9995 let path = absolute_package_dir.join(subpath);100100- let parent_dir = path.parent().context(format!(101101- "Could not get parent of path {}",102102- subpath.display()103103- ))?;9696+ let parent_dir = path9797+ .parent()9898+ .with_context(|| format!("Could not get parent of path {}", subpath.display()))?;10499105105- let contents =106106- read_to_string(&path).context(format!("Could not read file {}", subpath.display()))?;100100+ let contents = read_to_string(&path)101101+ .with_context(|| format!("Could not read file {}", subpath.display()))?;107102108103 let root = Root::parse(&contents);109104 if let Some(error) = root.errors().first() {
+2-2
pkgs/test/nixpkgs-check-by-name/src/utils.rs
···1010pub fn read_dir_sorted(base_dir: &Path) -> anyhow::Result<Vec<fs::DirEntry>> {1111 let listing = base_dir1212 .read_dir()1313- .context(format!("Could not list directory {}", base_dir.display()))?;1313+ .with_context(|| format!("Could not list directory {}", base_dir.display()))?;1414 let mut shard_entries = listing1515 .collect::<io::Result<Vec<_>>>()1616- .context(format!("Could not list directory {}", base_dir.display()))?;1616+ .with_context(|| format!("Could not list directory {}", base_dir.display()))?;1717 shard_entries.sort_by_key(|entry| entry.file_name());1818 Ok(shard_entries)1919}
···1919 overlays ? [],2020 # Passed by the checker to make sure a real Nixpkgs isn't influenced by impurities2121 config ? {},2222+ # Passed by the checker to make sure a real Nixpkgs isn't influenced by impurities2323+ system ? null,2224}:2325let2426
···342342 pymyq = python-myq; # added 2023-10-20343343 python-myq = throw "python-myq has been removed, as the service provider has decided to block its API requests"; # added 2023-12-07344344 pyqt4 = throw "pyqt4 has been removed, because it depended on the long EOL qt4"; # added 2022-06-09345345+ pyqt5_with_qtwebkit = pyqt5-webkit; # added 2024-01-07345346 pyramid_beaker = pyramid-beaker; # added 2023-08-23346347 pyramid_chameleon = pyramid-chameleon; # added 2023-08-23347348 pyramid_exclog = pyramid-exclog; # added 2023-08-24
+6-2
pkgs/top-level/python-packages.nix
···1487148714881488 bip32 = callPackage ../development/python-modules/bip32 { };1489148914901490+ birch = callPackage ../development/python-modules/birch { };14911491+14901492 bitarray = callPackage ../development/python-modules/bitarray { };1491149314921494 bitbox02 = callPackage ../development/python-modules/bitbox02 { };···1101011008 };11011110091101211010 /*1101311013- `pyqt5_with_qtwebkit` should not be used by python libraries in1101111011+ `pyqt5-webkit` should not be used by python libraries in1101411012 pkgs/development/python-modules/*. Putting this attribute in1101511013 `propagatedBuildInputs` may cause collisions.1101611014 */1101711017- pyqt5_with_qtwebkit = self.pyqt5.override {1101511015+ pyqt5-webkit = self.pyqt5.override {1101811016 withWebKit = true;1101911017 };1102011018···1381513813 stravalib = callPackage ../development/python-modules/stravalib { };13816138141381713815 strawberry-graphql = callPackage ../development/python-modules/strawberry-graphql { };1381613816+1381713817+ strct = callPackage ../development/python-modules/strct { };13818138181381913819 streamdeck = callPackage ../development/python-modules/streamdeck { };1382013820