Unverified Commit 965ea7b1 authored by Maximilian Bosch's avatar Maximilian Bosch
Browse files

linux: get rid of drvAttrs



This is in principle, what Alyssa was already doing[1], but I re-did it
because rebasing this against reformats felt too annoying. The previous
attempt was reverted because of unrelated regressions in the chain.

The `drvAttrs` function made the entire code far more cluttered, harder
to understand and didn't serve a real purpose since it was just used a
single time.

I also got rid of the `kernelConf`-variable and decided to directly use
`stdenv.hostPlatform.linux-kernel`, making it more clear where the
attributes are actually coming from.

[1] f521f461

Co-authored-by: default avatarAlyssa Ross <hi@alyssa.is>
parent bd25fcc7
Loading
Loading
Loading
Loading
+445 −460
Original line number Diff line number Diff line
@@ -102,15 +102,20 @@ lib.makeOverridable (
      optional
      optionals
      optionalString
      optionalAttrs
      maintainers
      teams
      platforms
      ;

    drvAttrs =
      config_: kernelConf: kernelPatches: configfile:
      let
    commonMakeFlags = import ./common-flags.nix {
      inherit
        lib
        stdenv
        buildPackages
        extraMakeFlags
        ;
    };

    # Folding in `ubootTools` in the default nativeBuildInputs is problematic, as
    # it makes updating U-Boot cumbersome, since it will go above the current
    # threshold of rebuilds
@@ -136,33 +141,33 @@ lib.makeOverridable (
    ];
    needsUbootTools = lib.elem stdenv.hostPlatform.linuxArch linuxPlatformsUsingUImage;

        config =
    configHelpers =
      let
        attrName = attr: "CONFIG_" + attr;
      in
      {
        isSet = attr: hasAttr (attrName attr) config;

            getValue = attr: if config.isSet attr then getAttr (attrName attr) config else null;
        getValue = attr: if configHelpers.isSet attr then getAttr (attrName attr) config else null;

            isYes = attr: (config.getValue attr) == "y";
        isYes = attr: (configHelpers.getValue attr) == "y";

            isNo = attr: (config.getValue attr) == "n";
        isNo = attr: (configHelpers.getValue attr) == "n";

            isModule = attr: (config.getValue attr) == "m";
        isModule = attr: (configHelpers.getValue attr) == "m";

            isEnabled = attr: (config.isModule attr) || (config.isYes attr);
        isEnabled = attr: (configHelpers.isModule attr) || (configHelpers.isYes attr);

            isDisabled = attr: (!(config.isSet attr)) || (config.isNo attr);
        isDisabled = attr: (!(configHelpers.isSet attr)) || (configHelpers.isNo attr);
      }
          // config_;
      // config;

        isModular = config.isYes "MODULES";
        withRust = config.isYes "RUST";
    isModular = configHelpers.isYes "MODULES";
    withRust = configHelpers.isYes "RUST";

        target = kernelConf.target or "vmlinux";
    target = stdenv.hostPlatform.linux-kernel.target or "vmlinux";

        buildDTBs = kernelConf.DTB or false;
    buildDTBs = stdenv.hostPlatform.linux-kernel.DTB or false;

    # Dependencies that are required to build kernel modules
    moduleBuildDependencies = [
@@ -177,41 +182,74 @@ lib.makeOverridable (
      rustc-unwrapped
      rust-bindgen-unwrapped
    ];

  in
      (optionalAttrs isModular {
        outputs = [

  stdenv.mkDerivation {
    inherit pname version src;

    __structuredAttrs = true;

    enableParallelBuilding = true;

    hardeningDisable = [
      "bindnow"
      "format"
      "fortify"
      "stackprotector"
      "pic"
      "pie"
    ];

    ${if isModular then "outputs" else null} = [
      "out"
      "dev"
      "modules"
    ];
      })
      // {
        __structuredAttrs = true;

        passthru = rec {
          inherit
            version
            modDirVersion
            config
            kernelPatches
            configfile
            moduleBuildDependencies
            stdenv
            ;
          inherit
            isZen
            isHardened
            isLibre
            withRust
            ;
          isXen = lib.warn "The isXen attribute is deprecated. All Nixpkgs kernels that support it now have Xen enabled." true;
          baseVersion = lib.head (lib.splitString "-rc" version);
          kernelOlder = lib.versionOlder baseVersion;
          kernelAtLeast = lib.versionAtLeast baseVersion;
        };
    # We remove a bunch of stuff that is symlinked from other places to save space,
    # which trips the broken symlink check. So, just skip it. We'll know if it explodes.
    dontCheckForBrokenSymlinks = true;

    patches =
      # kernelPatches can contain config changes and no actual patch
      lib.filter (p: p != null) (map (p: p.patch) kernelPatches)
      # Required for deterministic builds along with some postPatch magic.
      ++ optional (lib.versionOlder version "5.19") ./randstruct-provide-seed.patch
      ++ optional (lib.versionAtLeast version "5.19") ./randstruct-provide-seed-5.19.patch
      # Linux 5.12 marked certain PowerPC-only symbols as GPL, which breaks
      # OpenZFS; this was fixed in Linux 5.19 so we backport the fix
      # https://github.com/openzfs/zfs/pull/13367
      ++
        optional
          (
            lib.versionAtLeast version "5.12" && lib.versionOlder version "5.19" && stdenv.hostPlatform.isPower
          )
          (fetchpatch {
            url = "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/patch/?id=d9e5c3e9e75162f845880535957b7fd0b4637d23";
            hash = "sha256-bBOyJcP6jUvozFJU0SPTOf3cmnTQ6ZZ4PlHjiniHXLU=";
          });

        inherit src;
    buildFlags = [
      "KBUILD_BUILD_VERSION=1-NixOS"
      stdenv.hostPlatform.linux-kernel.target
      "vmlinux" # for "perf" and things like that
      "scripts_gdb"
    ]
    ++ optional isModular "modules"
    ++ optionals buildDTBs [
      "dtbs"
      "DTC_FLAGS=-@"
    ]
    ++ extraMakeFlags;

    installFlags = [
      "INSTALL_PATH=${placeholder "out"}"
    ]
    ++ (optional isModular "INSTALL_MOD_PATH=${placeholder "modules"}")
    ++ optionals buildDTBs [
      "dtbs_install"
      "INSTALL_DTBS_PATH=${placeholder "out"}/dtbs"
    ];

    depsBuildBuild = [ buildPackages.stdenv.cc ];
    nativeBuildInputs = [
@@ -249,24 +287,24 @@ lib.makeOverridable (
      KRUSTFLAGS = lib.optionalString withRust "--remap-path-prefix ${rustPlatform.rustLibSrc}=/";
    };

        patches =
          # kernelPatches can contain config changes and no actual patch
          lib.filter (p: p != null) (map (p: p.patch) kernelPatches)
          # Required for deterministic builds along with some postPatch magic.
          ++ optional (lib.versionOlder version "5.19") ./randstruct-provide-seed.patch
          ++ optional (lib.versionAtLeast version "5.19") ./randstruct-provide-seed-5.19.patch
          # Linux 5.12 marked certain PowerPC-only symbols as GPL, which breaks
          # OpenZFS; this was fixed in Linux 5.19 so we backport the fix
          # https://github.com/openzfs/zfs/pull/13367
          ++
            optional
              (
                lib.versionAtLeast version "5.12" && lib.versionOlder version "5.19" && stdenv.hostPlatform.isPower
              )
              (fetchpatch {
                url = "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/patch/?id=d9e5c3e9e75162f845880535957b7fd0b4637d23";
                hash = "sha256-bBOyJcP6jUvozFJU0SPTOf3cmnTQ6ZZ4PlHjiniHXLU=";
              });
    makeFlags = [
      "O=$(buildRoot)"

      # We have a `modules` variable in the environment for our
      # split output, but the kernel Makefiles also define their
      # own `modules` variable. Their definition wins, but Make
      # remembers that the variable was originally from the
      # environment and exports it to all the build recipes. This
      # breaks the build with an “Argument list too long” error due
      # to passing the huge list of every module object file in the
      # environment of every process invoked by every build recipe.
      #
      # We use `--eval` here to undefine the inherited environment
      # variable before any Makefiles are read, ensuring that the
      # kernel’s definition creates a new, unexported variable.
      "--eval=undefine modules"
    ]
    ++ commonMakeFlags;

    postPatch = ''
      # Ensure that depmod gets resolved through PATH
@@ -332,27 +370,80 @@ lib.makeOverridable (
      cd $buildRoot
    '';

        buildFlags = [
          "KBUILD_BUILD_VERSION=1-NixOS"
          target
          "vmlinux" # for "perf" and things like that
          "scripts_gdb"
        ]
        ++ optional isModular "modules"
        ++ optionals buildDTBs [
          "dtbs"
          "DTC_FLAGS=-@"
        ]
        ++ extraMakeFlags;
    postInstall = optionalString isModular ''
      mkdir -p $dev
      cp vmlinux $dev/

        installFlags = [
          "INSTALL_PATH=${placeholder "out"}"
        ]
        ++ (optional isModular "INSTALL_MOD_PATH=${placeholder "modules"}")
        ++ optionals buildDTBs [
          "dtbs_install"
          "INSTALL_DTBS_PATH=${placeholder "out"}/dtbs"
        ];
      mkdir -p $dev/lib/modules/${modDirVersion}/build/scripts
      cp -rL ../scripts/gdb/ $dev/lib/modules/${modDirVersion}/build/scripts

      if [ -z "''${dontStrip-}" ]; then
        installFlags+=("INSTALL_MOD_STRIP=1")
      fi
      make modules_install "''${makeFlags[@]}" "''${installFlags[@]}"
      unlink $modules/lib/modules/${modDirVersion}/build

      mkdir -p $dev/lib/modules/${modDirVersion}/{build,source}

      # To save space, exclude a bunch of unneeded stuff when copying.
      (cd .. && rsync --archive --prune-empty-dirs \
          --exclude='/build/' \
          * $dev/lib/modules/${modDirVersion}/source/)

      cd $dev/lib/modules/${modDirVersion}/source

      cp $buildRoot/{.config,Module.symvers} $dev/lib/modules/${modDirVersion}/build
      make modules_prepare "''${makeFlags[@]}" O=$dev/lib/modules/${modDirVersion}/build

      # For reproducibility, removes accidental leftovers from a `cc1` call
      # from a `try-run` call from the Makefile
      rm -f $dev/lib/modules/${modDirVersion}/build/.[0-9]*.d

      # Keep some extra files on some arches (powerpc, aarch64)
      for f in arch/powerpc/lib/crtsavres.o arch/arm64/kernel/ftrace-mod.o; do
        if [ -f "$buildRoot/$f" ]; then
          mkdir -p "$(dirname $dev/lib/modules/${modDirVersion}/build/$f)"
          cp $buildRoot/$f $dev/lib/modules/${modDirVersion}/build/$f
        fi
      done

      # !!! No documentation on how much of the source tree must be kept
      # If/when kernel builds fail due to missing files, you can add
      # them here. Note that we may see packages requiring headers
      # from drivers/ in the future; it adds 50M to keep all of its
      # headers on 3.10 though.

      chmod u+w -R ..
      buildArchDir="$dev/lib/modules/${modDirVersion}/build/arch"

      # Remove unused arches
      for d in $(cd arch/; ls); do
        if [ -d "$buildArchDir/$d" ]; then continue; fi
        if [ -d "$buildArchDir/arm64" ] && [ "$d" = arm ]; then continue; fi
        rm -rf arch/$d
      done

      # Remove all driver-specific code (50M of which is headers)
      rm -fR drivers

      # Keep all headers
      find .  -type f -name '*.h' -print0 | xargs -0 -r chmod u-w

      # Keep linker scripts (they are required for out-of-tree modules on aarch64)
      find .  -type f -name '*.lds' -print0 | xargs -0 -r chmod u-w

      # Keep root and arch-specific Makefiles
      chmod u-w Makefile arch/*/Makefile*

      # Keep whole scripts dir
      chmod u-w -R scripts

      # Delete everything not kept
      find . -type f -perm -u=w -print0 | xargs -0 -r rm

      # Delete empty directories
      find -empty -type d -delete
    '';

    preInstall =
      let
@@ -412,9 +503,34 @@ lib.makeOverridable (
        export HOME=${installkernel}
      '';

    requiredSystemFeatures = [ "big-parallel" ];

    passthru = rec {
      inherit
        version
        modDirVersion
        config
        kernelPatches
        configfile
        moduleBuildDependencies
        stdenv
        commonMakeFlags
        ;
      inherit
        isZen
        isHardened
        isLibre
        withRust
        ;
      isXen = lib.warn "The isXen attribute is deprecated. All Nixpkgs kernels that support it now have Xen enabled." true;
      baseVersion = lib.head (lib.splitString "-rc" version);
      kernelOlder = lib.versionOlder baseVersion;
      kernelAtLeast = lib.versionAtLeast baseVersion;
    };

    # Some image types need special install targets (e.g. uImage is installed with make uinstall on arm)
    installTargets = [
          (kernelConf.installTarget or (
      (stdenv.hostPlatform.linux-kernel.installTarget or (
        if target == "uImage" && stdenv.hostPlatform.linuxArch == "arm" then
          "uinstall"
        else if
@@ -433,86 +549,9 @@ lib.makeOverridable (
      )
    ];

        # We remove a bunch of stuff that is symlinked from other places to save space,
        # which trips the broken symlink check. So, just skip it. We'll know if it explodes.
        dontCheckForBrokenSymlinks = true;

        postInstall = optionalString isModular ''
          mkdir -p $dev
          cp vmlinux $dev/

          mkdir -p $dev/lib/modules/${modDirVersion}/build/scripts
          cp -rL ../scripts/gdb/ $dev/lib/modules/${modDirVersion}/build/scripts

          if [ -z "''${dontStrip-}" ]; then
            installFlags+=("INSTALL_MOD_STRIP=1")
          fi
          make modules_install "''${makeFlags[@]}" "''${installFlags[@]}"
          unlink $modules/lib/modules/${modDirVersion}/build

          mkdir -p $dev/lib/modules/${modDirVersion}/{build,source}

          # To save space, exclude a bunch of unneeded stuff when copying.
          (cd .. && rsync --archive --prune-empty-dirs \
              --exclude='/build/' \
              * $dev/lib/modules/${modDirVersion}/source/)

          cd $dev/lib/modules/${modDirVersion}/source

          cp $buildRoot/{.config,Module.symvers} $dev/lib/modules/${modDirVersion}/build
          make modules_prepare "''${makeFlags[@]}" O=$dev/lib/modules/${modDirVersion}/build

          # For reproducibility, removes accidental leftovers from a `cc1` call
          # from a `try-run` call from the Makefile
          rm -f $dev/lib/modules/${modDirVersion}/build/.[0-9]*.d

          # Keep some extra files on some arches (powerpc, aarch64)
          for f in arch/powerpc/lib/crtsavres.o arch/arm64/kernel/ftrace-mod.o; do
            if [ -f "$buildRoot/$f" ]; then
              mkdir -p "$(dirname $dev/lib/modules/${modDirVersion}/build/$f)"
              cp $buildRoot/$f $dev/lib/modules/${modDirVersion}/build/$f
            fi
          done

          # !!! No documentation on how much of the source tree must be kept
          # If/when kernel builds fail due to missing files, you can add
          # them here. Note that we may see packages requiring headers
          # from drivers/ in the future; it adds 50M to keep all of its
          # headers on 3.10 though.

          chmod u+w -R ..
          buildArchDir="$dev/lib/modules/${modDirVersion}/build/arch"

          # Remove unused arches
          for d in $(cd arch/; ls); do
            if [ -d "$buildArchDir/$d" ]; then continue; fi
            if [ -d "$buildArchDir/arm64" ] && [ "$d" = arm ]; then continue; fi
            rm -rf arch/$d
          done

          # Remove all driver-specific code (50M of which is headers)
          rm -fR drivers

          # Keep all headers
          find .  -type f -name '*.h' -print0 | xargs -0 -r chmod u-w

          # Keep linker scripts (they are required for out-of-tree modules on aarch64)
          find .  -type f -name '*.lds' -print0 | xargs -0 -r chmod u-w

          # Keep root and arch-specific Makefiles
          chmod u-w Makefile arch/*/Makefile*

          # Keep whole scripts dir
          chmod u-w -R scripts

          # Delete everything not kept
          find . -type f -perm -u=w -print0 | xargs -0 -r rm

          # Delete empty directories
          find -empty -type d -delete
        '';
    karch = stdenv.hostPlatform.linuxArch;

        requiredSystemFeatures = [ "big-parallel" ];
    pos = lib.optionalDrvAttr (pos != null) pos;

    meta = {
      # https://github.com/NixOS/nixpkgs/pull/345534#issuecomment-2391238381
@@ -547,59 +586,5 @@ lib.makeOverridable (
      };
    }
    // extraMeta;
      };

    commonMakeFlags = import ./common-flags.nix {
      inherit
        lib
        stdenv
        buildPackages
        extraMakeFlags
        ;
    };
  in

  stdenv.mkDerivation (
    builtins.foldl' lib.recursiveUpdate { } [
      (drvAttrs config stdenv.hostPlatform.linux-kernel kernelPatches configfile)
      {
        inherit pname version;

        enableParallelBuilding = true;

        hardeningDisable = [
          "bindnow"
          "format"
          "fortify"
          "stackprotector"
          "pic"
          "pie"
        ];

        makeFlags = [
          "O=$(buildRoot)"

          # We have a `modules` variable in the environment for our
          # split output, but the kernel Makefiles also define their
          # own `modules` variable. Their definition wins, but Make
          # remembers that the variable was originally from the
          # environment and exports it to all the build recipes. This
          # breaks the build with an “Argument list too long” error due
          # to passing the huge list of every module object file in the
          # environment of every process invoked by every build recipe.
          #
          # We use `--eval` here to undefine the inherited environment
          # variable before any Makefiles are read, ensuring that the
          # kernel’s definition creates a new, unexported variable.
          "--eval=undefine modules"
        ]
        ++ commonMakeFlags;

        passthru = { inherit commonMakeFlags; };

        karch = stdenv.hostPlatform.linuxArch;
  }
      (optionalAttrs (pos != null) { inherit pos; })
    ]
  )
)