{ lib, config, stdenv }: 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 = { name ? "" # 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 ? [] , # 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" ] , __impureHostDeps ? [] , __propagatedImpureHostDeps ? [] , sandboxProfile ? "" , propagatedSandboxProfile ? "" , hardeningEnable ? [] , hardeningDisable ? [] , ... } @ attrs: let # Check that the name is consistent with pname and version: selfConsistent = (with attrs; attrs ? "name" -> (lib.assertMsg (attrs ? "version" -> lib.strings.hasInfix version name) "version ${version} does not appear in name ${name}" && lib.assertMsg (attrs ? "pname" -> lib.strings.hasInfix pname name) "pname ${pname} does not appear in name ${name}")); computedName = if name != "" then name else "${attrs.pname}-${attrs.version}"; # 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; outputs' = outputs ++ lib.optional separateDebugInfo "debug"; fixedOutputDrv = attrs ? outputHash; noNonNativeDeps = builtins.length (depsBuildTarget ++ depsBuildTargetPropagated ++ depsHostHost ++ depsHostHostPropagated ++ buildInputs ++ propagatedBuildInputs ++ depsTargetTarget ++ depsTargetTargetPropagated) == 0; runtimeSensativeIfFixedOutput = fixedOutputDrv -> !noNonNativeDeps; supportedHardeningFlags = [ "fortify" "stackprotector" "pie" "pic" "strictoverflow" "format" "relro" "bindnow" ]; defaultHardeningFlags = if stdenv.targetPlatform.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) (map (drv: drv.__spliced.buildTarget or drv) depsBuildTarget) ] [ (map (drv: drv.__spliced.hostHost or drv) depsHostHost) (map (drv: drv.crossDrv or drv) (buildInputs ++ lib.optionals doCheck checkInputs ++ lib.optionals doInstallCheck' installCheckInputs)) ] [ (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" "__impureHostDeps" "__propagatedImpureHostDeps" "sandboxProfile" "propagatedSandboxProfile"]) // { # A hack to make `nix-env -qa` and `nix search` ignore broken packages. # TODO(@oxij): remove this assert when something like NixOS/nix#1771 gets merged into nix. name = assert selfConsistent && validity.handled && (separateDebugInfo -> stdenv.hostPlatform.isLinux); computedName + lib.optionalString # 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. (stdenv.hostPlatform != stdenv.buildPlatform && runtimeSensativeIfFixedOutput) ("-" + 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 doCheck doInstallCheck; inherit outputs; } // lib.optionalAttrs strictDeps { # Make sure "build" dependencies don’t leak into outputs. We # want to disallow references to depsBuildBuild, # nativeBuildInputs, and depsBuildTarget. But depsHostHost, # buildInputs, and depsTargetTarget is okay, so we subtract # those from disallowedReferences in case a dependency is # listed in multiple dependency lists. We also include # propagated dependencies here as well. disallowedReferences = (attrs.disallowedReferences or []) ++ (lib.subtractLists (lib.concatLists ( (lib.elemAt propagatedDependencies 1) ++ (lib.elemAt dependencies 1) ++ (lib.elemAt propagatedDependencies 2) ++ (lib.elemAt dependencies 2) ) ) (lib.concatLists ( (lib.elemAt propagatedDependencies 0) ++ (lib.elemAt dependencies 0) ) ) ); } // lib.optionalAttrs (stdenv.hostPlatform != stdenv.buildPlatform) { cmakeFlags = (/**/ if lib.isString cmakeFlags then [cmakeFlags] else if cmakeFlags == null then [] else cmakeFlags) ++ lib.optional (stdenv.hostPlatform.uname.system != null) "-DCMAKE_SYSTEM_NAME=${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}"; } // lib.optionalAttrs (attrs.enableParallelBuilding or false) { enableParallelChecking = attrs.enableParallelChecking or true; } // lib.optionalAttrs (hardeningDisable != [] || hardeningEnable != []) { NIX_HARDENING_ENABLE = enabledHardeningOptions; } // lib.optionalAttrs (stdenv.buildPlatform.isDarwin) { # 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 = import ./check-meta.nix { inherit lib config meta; # 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; } 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 = computedName; # 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) )]; } // 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); }