{ lib, config, stdenv }: let checkMeta = import ./check-meta.nix { inherit lib config; # Nix itself uses the `system` field of a derivation to decide where # to build it. This is a bit confusing for cross compilation. inherit (stdenv) hostPlatform; }; in rec { # `mkDerivation` wraps the builtin `derivation` function to # produce derivations that use this stdenv and its shell. # # See also: # # * https://nixos.org/nixpkgs/manual/#sec-using-stdenv # Details on how to use this mkDerivation function # # * https://nixos.org/nix/manual/#ssec-derivation # Explanation about derivations in general mkDerivation = { # These types of dependencies are all exhaustively documented in # the "Specifying Dependencies" section of the "Standard # Environment" chapter of the Nixpkgs manual. # TODO(@Ericson2314): Stop using legacy dep attribute names # host offset -> target offset depsBuildBuild ? [] # -1 -> -1 , depsBuildBuildPropagated ? [] # -1 -> -1 , nativeBuildInputs ? [] # -1 -> 0 N.B. Legacy name , propagatedNativeBuildInputs ? [] # -1 -> 0 N.B. Legacy name , depsBuildTarget ? [] # -1 -> 1 , depsBuildTargetPropagated ? [] # -1 -> 1 , depsHostHost ? [] # 0 -> 0 , depsHostHostPropagated ? [] # 0 -> 0 , buildInputs ? [] # 0 -> 1 N.B. Legacy name , propagatedBuildInputs ? [] # 0 -> 1 N.B. Legacy name , depsTargetTarget ? [] # 1 -> 1 , depsTargetTargetPropagated ? [] # 1 -> 1 , checkInputs ? [] , installCheckInputs ? [] # Configure Phase , configureFlags ? [] , cmakeFlags ? [] , mesonFlags ? [] , # Target is not included by default because most programs don't care. # Including it then would cause needless mass rebuilds. # # TODO(@Ericson2314): Make [ "build" "host" ] always the default. configurePlatforms ? lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [ "build" "host" ] # TODO(@Ericson2314): Make unconditional / resolve #33599 # Check phase , doCheck ? config.doCheckByDefault or false # TODO(@Ericson2314): Make unconditional / resolve #33599 # InstallCheck phase , doInstallCheck ? config.doCheckByDefault or false , # TODO(@Ericson2314): Make always true and remove strictDeps ? stdenv.hostPlatform != stdenv.buildPlatform , meta ? {} , passthru ? {} , pos ? # position used in error messages and for meta.position (if attrs.meta.description or null != null then builtins.unsafeGetAttrPos "description" attrs.meta else if attrs.version or null != null then builtins.unsafeGetAttrPos "version" attrs else builtins.unsafeGetAttrPos "name" attrs) , separateDebugInfo ? false , outputs ? [ "out" ] , __darwinAllowLocalNetworking ? false , __impureHostDeps ? [] , __propagatedImpureHostDeps ? [] , sandboxProfile ? "" , propagatedSandboxProfile ? "" , hardeningEnable ? [] , hardeningDisable ? [] , patches ? [] , ... } @ attrs: let # TODO(@oxij, @Ericson2314): This is here to keep the old semantics, remove when # no package has `doCheck = true`. doCheck' = doCheck && stdenv.hostPlatform == stdenv.buildPlatform; doInstallCheck' = doInstallCheck && stdenv.hostPlatform == stdenv.buildPlatform; separateDebugInfo' = separateDebugInfo && stdenv.hostPlatform.isLinux && !(stdenv.hostPlatform.useLLVM or false); outputs' = outputs ++ lib.optional separateDebugInfo' "debug"; noNonNativeDeps = builtins.length (depsBuildTarget ++ depsBuildTargetPropagated ++ depsHostHost ++ depsHostHostPropagated ++ buildInputs ++ propagatedBuildInputs ++ depsTargetTarget ++ depsTargetTargetPropagated) == 0; dontAddHostSuffix = attrs ? outputHash && !noNonNativeDeps || (stdenv.noCC or false); supportedHardeningFlags = [ "fortify" "stackprotector" "pie" "pic" "strictoverflow" "format" "relro" "bindnow" ]; defaultHardeningFlags = if stdenv.hostPlatform.isMusl then supportedHardeningFlags else lib.remove "pie" supportedHardeningFlags; enabledHardeningOptions = if builtins.elem "all" hardeningDisable then [] else lib.subtractLists hardeningDisable (defaultHardeningFlags ++ hardeningEnable); # hardeningDisable additionally supports "all". erroneousHardeningFlags = lib.subtractLists supportedHardeningFlags (hardeningEnable ++ lib.remove "all" hardeningDisable); in if builtins.length erroneousHardeningFlags != 0 then abort ("mkDerivation was called with unsupported hardening flags: " + lib.generators.toPretty {} { inherit erroneousHardeningFlags hardeningDisable hardeningEnable supportedHardeningFlags; }) else let doCheck = doCheck'; doInstallCheck = doInstallCheck'; outputs = outputs'; references = nativeBuildInputs ++ buildInputs ++ propagatedNativeBuildInputs ++ propagatedBuildInputs; dependencies = map (map lib.chooseDevOutputs) [ [ (map (drv: drv.__spliced.buildBuild or drv) depsBuildBuild) (map (drv: drv.nativeDrv or drv) nativeBuildInputs ++ lib.optional separateDebugInfo' ../../build-support/setup-hooks/separate-debug-info.sh ++ lib.optional stdenv.hostPlatform.isWindows ../../build-support/setup-hooks/win-dll-link.sh ++ lib.optionals doCheck checkInputs ++ lib.optionals doInstallCheck' installCheckInputs) (map (drv: drv.__spliced.buildTarget or drv) depsBuildTarget) ] [ (map (drv: drv.__spliced.hostHost or drv) depsHostHost) (map (drv: drv.crossDrv or drv) buildInputs) ] [ (map (drv: drv.__spliced.targetTarget or drv) depsTargetTarget) ] ]; propagatedDependencies = map (map lib.chooseDevOutputs) [ [ (map (drv: drv.__spliced.buildBuild or drv) depsBuildBuildPropagated) (map (drv: drv.nativeDrv or drv) propagatedNativeBuildInputs) (map (drv: drv.__spliced.buildTarget or drv) depsBuildTargetPropagated) ] [ (map (drv: drv.__spliced.hostHost or drv) depsHostHostPropagated) (map (drv: drv.crossDrv or drv) propagatedBuildInputs) ] [ (map (drv: drv.__spliced.targetTarget or drv) depsTargetTargetPropagated) ] ]; computedSandboxProfile = lib.concatMap (input: input.__propagatedSandboxProfile or []) (stdenv.extraNativeBuildInputs ++ stdenv.extraBuildInputs ++ lib.concatLists dependencies); computedPropagatedSandboxProfile = lib.concatMap (input: input.__propagatedSandboxProfile or []) (lib.concatLists propagatedDependencies); computedImpureHostDeps = lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or []) (stdenv.extraNativeBuildInputs ++ stdenv.extraBuildInputs ++ lib.concatLists dependencies)); computedPropagatedImpureHostDeps = lib.unique (lib.concatMap (input: input.__propagatedImpureHostDeps or []) (lib.concatLists propagatedDependencies)); derivationArg = (removeAttrs attrs ["meta" "passthru" "pos" "checkInputs" "installCheckInputs" "__darwinAllowLocalNetworking" "__impureHostDeps" "__propagatedImpureHostDeps" "sandboxProfile" "propagatedSandboxProfile"]) // (lib.optionalAttrs (!(attrs ? name) && attrs ? pname && attrs ? version)) { name = "${attrs.pname}-${attrs.version}"; } // (lib.optionalAttrs (stdenv.hostPlatform != stdenv.buildPlatform && !dontAddHostSuffix && (attrs ? name || (attrs ? pname && attrs ? version)))) { # Fixed-output derivations like source tarballs shouldn't get a host # suffix. But we have some weird ones with run-time deps that are # just used for their side-affects. Those might as well since the # hash can't be the same. See #32986. name = "${attrs.name or "${attrs.pname}-${attrs.version}"}-${stdenv.hostPlatform.config}"; } // { builder = attrs.realBuilder or stdenv.shell; args = attrs.args or ["-e" (attrs.builder or ./default-builder.sh)]; inherit stdenv; # The `system` attribute of a derivation has special meaning to Nix. # Derivations set it to choose what sort of machine could be used to # execute the build, The build platform entirely determines this, # indeed more finely than Nix knows or cares about. The `system` # attribute of `buildPlatfom` matches Nix's degree of specificity. # exactly. inherit (stdenv.buildPlatform) system; userHook = config.stdenv.userHook or null; __ignoreNulls = true; inherit strictDeps; depsBuildBuild = lib.elemAt (lib.elemAt dependencies 0) 0; nativeBuildInputs = lib.elemAt (lib.elemAt dependencies 0) 1; depsBuildTarget = lib.elemAt (lib.elemAt dependencies 0) 2; depsHostHost = lib.elemAt (lib.elemAt dependencies 1) 0; buildInputs = lib.elemAt (lib.elemAt dependencies 1) 1; depsTargetTarget = lib.elemAt (lib.elemAt dependencies 2) 0; depsBuildBuildPropagated = lib.elemAt (lib.elemAt propagatedDependencies 0) 0; propagatedNativeBuildInputs = lib.elemAt (lib.elemAt propagatedDependencies 0) 1; depsBuildTargetPropagated = lib.elemAt (lib.elemAt propagatedDependencies 0) 2; depsHostHostPropagated = lib.elemAt (lib.elemAt propagatedDependencies 1) 0; propagatedBuildInputs = lib.elemAt (lib.elemAt propagatedDependencies 1) 1; depsTargetTargetPropagated = lib.elemAt (lib.elemAt propagatedDependencies 2) 0; # This parameter is sometimes a string, sometimes null, and sometimes a list, yuck configureFlags = let inherit (lib) optional elem; in (/**/ if lib.isString configureFlags then [configureFlags] else if configureFlags == null then [] else configureFlags) ++ optional (elem "build" configurePlatforms) "--build=${stdenv.buildPlatform.config}" ++ optional (elem "host" configurePlatforms) "--host=${stdenv.hostPlatform.config}" ++ optional (elem "target" configurePlatforms) "--target=${stdenv.targetPlatform.config}"; inherit patches; inherit doCheck doInstallCheck; inherit outputs; } // lib.optionalAttrs (stdenv.hostPlatform != stdenv.buildPlatform) { cmakeFlags = (/**/ if lib.isString cmakeFlags then [cmakeFlags] else if cmakeFlags == null then [] else cmakeFlags) ++ [ "-DCMAKE_SYSTEM_NAME=${lib.findFirst lib.isString "Generic" [ stdenv.hostPlatform.uname.system ]}" ] ++ lib.optional (stdenv.hostPlatform.uname.processor != null) "-DCMAKE_SYSTEM_PROCESSOR=${stdenv.hostPlatform.uname.processor}" ++ lib.optional (stdenv.hostPlatform.uname.release != null) "-DCMAKE_SYSTEM_VERSION=${stdenv.hostPlatform.release}" ++ lib.optional (stdenv.buildPlatform.uname.system != null) "-DCMAKE_HOST_SYSTEM_NAME=${stdenv.buildPlatform.uname.system}" ++ lib.optional (stdenv.buildPlatform.uname.processor != null) "-DCMAKE_HOST_SYSTEM_PROCESSOR=${stdenv.buildPlatform.uname.processor}" ++ lib.optional (stdenv.buildPlatform.uname.release != null) "-DCMAKE_HOST_SYSTEM_VERSION=${stdenv.buildPlatform.uname.release}"; mesonFlags = if mesonFlags == null then null else let # See https://mesonbuild.com/Reference-tables.html#cpu-families cpuFamilies = { aarch64 = "aarch64"; armv5tel = "arm"; armv6l = "arm"; armv7l = "arm"; i686 = "x86"; x86_64 = "x86_64"; }; crossFile = builtins.toFile "cross-file.conf" ('' [properties] needs_exe_wrapper = true [host_machine] system = '${stdenv.targetPlatform.parsed.kernel.name}' cpu_family = '${cpuFamilies.${stdenv.targetPlatform.parsed.cpu.name}}' cpu = '${stdenv.targetPlatform.parsed.cpu.name}' endian = ${if stdenv.targetPlatform.isLittleEndian then "'little'" else "'big'"} '' # TODO should have target prefix too, issue #86077 + '' [binaries] pkgconfig = 'pkg-config' ''); in [ "--cross-file=${crossFile}" ] ++ mesonFlags; } // lib.optionalAttrs (attrs.enableParallelBuilding or false) { enableParallelChecking = attrs.enableParallelChecking or true; } // lib.optionalAttrs (hardeningDisable != [] || hardeningEnable != []) { NIX_HARDENING_ENABLE = enabledHardeningOptions; } // lib.optionalAttrs (stdenv.hostPlatform.isx86_64 && stdenv.hostPlatform ? platform.gcc.arch) { requiredSystemFeatures = attrs.requiredSystemFeatures or [] ++ [ "gccarch-${stdenv.hostPlatform.platform.gcc.arch}" ]; } // lib.optionalAttrs (stdenv.buildPlatform.isDarwin) { inherit __darwinAllowLocalNetworking; # TODO: remove lib.unique once nix has a list canonicalization primitive __sandboxProfile = let profiles = [ stdenv.extraSandboxProfile ] ++ computedSandboxProfile ++ computedPropagatedSandboxProfile ++ [ propagatedSandboxProfile sandboxProfile ]; final = lib.concatStringsSep "\n" (lib.filter (x: x != "") (lib.unique profiles)); in final; __propagatedSandboxProfile = lib.unique (computedPropagatedSandboxProfile ++ [ propagatedSandboxProfile ]); __impureHostDeps = computedImpureHostDeps ++ computedPropagatedImpureHostDeps ++ __propagatedImpureHostDeps ++ __impureHostDeps ++ stdenv.__extraImpureHostDeps ++ [ "/dev/zero" "/dev/random" "/dev/urandom" "/bin/sh" ]; __propagatedImpureHostDeps = computedPropagatedImpureHostDeps ++ __propagatedImpureHostDeps; }; validity = checkMeta { inherit meta attrs; }; # The meta attribute is passed in the resulting attribute set, # but it's not part of the actual derivation, i.e., it's not # passed to the builder and is not a dependency. But since we # include it in the result, it *is* available to nix-env for queries. meta = { # `name` above includes cross-compilation cruft (and is under assert), # lets have a clean always accessible version here. name = attrs.name or "${attrs.pname}-${attrs.version}"; # If the packager hasn't specified `outputsToInstall`, choose a default, # which is the name of `p.bin or p.out or p`; # if he has specified it, it will be overridden below in `// meta`. # Note: This default probably shouldn't be globally configurable. # Services and users should specify outputs explicitly, # unless they are comfortable with this default. outputsToInstall = let hasOutput = out: builtins.elem out outputs; in [( lib.findFirst hasOutput null (["bin" "out"] ++ outputs) )] ++ lib.optional (hasOutput "man") "man"; } // attrs.meta or {} # Fill `meta.position` to identify the source location of the package. // lib.optionalAttrs (pos != null) { position = pos.file + ":" + toString pos.line; # Expose the result of the checks for everyone to see. } // { available = validity.valid && (if config.checkMetaRecursively or false then lib.all (d: d.meta.available or true) references else true); }; in lib.extendDerivation validity.handled ({ overrideAttrs = f: mkDerivation (attrs // (f attrs)); inherit meta passthru; } // # Pass through extra attributes that are not inputs, but # should be made available to Nix expressions using the # derivation (e.g., in assertions). passthru) (derivation derivationArg); }