diff --git a/pkgs/development/interpreters/python/mk-python-derivation.nix b/pkgs/development/interpreters/python/mk-python-derivation.nix index 11bb1af773ee9..8f3dec2dc8bff 100644 --- a/pkgs/development/interpreters/python/mk-python-derivation.nix +++ b/pkgs/development/interpreters/python/mk-python-derivation.nix @@ -206,252 +206,259 @@ in }@attrs: let - format' = - assert (pyproject != null) -> (format == null); - if pyproject != null then - if pyproject then "pyproject" else "other" - else if format != null then - format - else - "setuptools"; - - withDistOutput = withDistOutput' format'; - - validatePythonMatches = + # Keep extra attributes from `attrs`, e.g., `patchPhase', etc. + self = stdenv.mkDerivation ( + finalAttrs: let - throwMismatch = - attrName: drv: + format' = + assert (pyproject != null) -> (format == null); + if pyproject != null then + if pyproject then "pyproject" else "other" + else if format != null then + format + else + "setuptools"; + + withDistOutput = withDistOutput' format'; + + validatePythonMatches = let - myName = "'${namePrefix}${name}'"; - theirName = "'${drv.name}'"; - optionalLocation = + throwMismatch = + attrName: drv: let - pos = unsafeGetAttrPos (if attrs ? "pname" then "pname" else "name") attrs; + myName = "'${namePrefix}${name}'"; + theirName = "'${drv.name}'"; + optionalLocation = + let + pos = unsafeGetAttrPos (if attrs ? "pname" then "pname" else "name") attrs; + in + optionalString (pos != null) " at ${pos.file}:${toString pos.line}:${toString pos.column}"; in - optionalString (pos != null) " at ${pos.file}:${toString pos.line}:${toString pos.column}"; - in - throw '' - Python version mismatch in ${myName}: + throw '' + Python version mismatch in ${myName}: - The Python derivation ${myName} depends on a Python derivation - named ${theirName}, but the two derivations use different versions - of Python: + The Python derivation ${myName} depends on a Python derivation + named ${theirName}, but the two derivations use different versions + of Python: - ${leftPadName myName theirName} uses ${python} - ${leftPadName theirName myName} uses ${toString drv.pythonModule} + ${leftPadName myName theirName} uses ${python} + ${leftPadName theirName myName} uses ${toString drv.pythonModule} - Possible solutions: + Possible solutions: - * If ${theirName} is a Python library, change the reference to ${theirName} - in the ${attrName} of ${myName} to use a ${theirName} built from the same - version of Python + * If ${theirName} is a Python library, change the reference to ${theirName} + in the ${attrName} of ${myName} to use a ${theirName} built from the same + version of Python - * If ${theirName} is used as a tool during the build, move the reference to - ${theirName} in ${myName} from ${attrName} to nativeBuildInputs + * If ${theirName} is used as a tool during the build, move the reference to + ${theirName} in ${myName} from ${attrName} to nativeBuildInputs - * If ${theirName} provides executables that are called at run time, pass its - bin path to makeWrapperArgs: + * If ${theirName} provides executables that are called at run time, pass its + bin path to makeWrapperArgs: - makeWrapperArgs = [ "--prefix PATH : ''${lib.makeBinPath [ ${getName drv} ] }" ]; + makeWrapperArgs = [ "--prefix PATH : ''${lib.makeBinPath [ ${getName drv} ] }" ]; - ${optionalLocation} - ''; + ${optionalLocation} + ''; - checkDrv = - attrName: drv: - if (isPythonModule drv) && (isMismatchedPython drv) then throwMismatch attrName drv else drv; + checkDrv = + attrName: drv: + if (isPythonModule drv) && (isMismatchedPython drv) then throwMismatch attrName drv else drv; - in - attrName: inputs: map (checkDrv attrName) inputs; + in + attrName: inputs: map (checkDrv attrName) inputs; - isBootstrapInstallPackage = isBootstrapInstallPackage' (attrs.pname or null); + isBootstrapInstallPackage = isBootstrapInstallPackage' (attrs.pname or null); - isBootstrapPackage = isBootstrapInstallPackage || isBootstrapPackage' (attrs.pname or null); + isBootstrapPackage = isBootstrapInstallPackage || isBootstrapPackage' (attrs.pname or null); - isSetuptoolsDependency = isSetuptoolsDependency' (attrs.pname or null); + isSetuptoolsDependency = isSetuptoolsDependency' (attrs.pname or null); - # Keep extra attributes from `attrs`, e.g., `patchPhase', etc. - self = toPythonModule ( - stdenv.mkDerivation ( - finalAttrs: - (cleanAttrs attrs) - // { - - name = namePrefix + name; - - nativeBuildInputs = - [ - python - wrapPython - ensureNewerSourcesForZipFilesHook # move to wheel installer (pip) or builder (setuptools, flit, ...)? - pythonRemoveTestsDirHook - ] - ++ optionals (catchConflicts && !isBootstrapPackage && !isSetuptoolsDependency) [ - # - # 1. When building a package that is also part of the bootstrap chain, we - # must ignore conflicts after installation, because there will be one with - # the package in the bootstrap. - # - # 2. When a package is a dependency of setuptools, we must ignore conflicts - # because the hook that checks for conflicts uses setuptools. - # - pythonCatchConflictsHook - ] - ++ optionals (attrs ? pythonRelaxDeps || attrs ? pythonRemoveDeps) [ - pythonRelaxDepsHook - ] - ++ optionals removeBinBytecode [ - pythonRemoveBinBytecodeHook - ] - ++ optionals (hasSuffix "zip" (attrs.src.name or "")) [ - unzip - ] - ++ optionals (format' == "setuptools") [ - setuptoolsBuildHook - ] - ++ optionals (format' == "pyproject") [ - ( - if isBootstrapPackage then - pypaBuildHook.override { - inherit (python.pythonOnBuildForHost.pkgs.bootstrap) build; - wheel = null; - } - else - pypaBuildHook - ) - ( - if isBootstrapPackage then - pythonRuntimeDepsCheckHook.override { - inherit (python.pythonOnBuildForHost.pkgs.bootstrap) packaging; - } - else - pythonRuntimeDepsCheckHook - ) - ] - ++ optionals (format' == "wheel") [ - wheelUnpackHook - ] - ++ optionals (format' == "egg") [ - eggUnpackHook - eggBuildHook - eggInstallHook - ] - ++ optionals (format' != "other") [ - ( - if isBootstrapInstallPackage then - pypaInstallHook.override { - inherit (python.pythonOnBuildForHost.pkgs.bootstrap) installer; - } - else - pypaInstallHook - ) - ] - ++ optionals (stdenv.buildPlatform == stdenv.hostPlatform) [ - # This is a test, however, it should be ran independent of the checkPhase and checkInputs - pythonImportsCheckHook - ] - ++ optionals (python.pythonAtLeast "3.3") [ - # Optionally enforce PEP420 for python3 - pythonNamespacesHook - ] - ++ optionals withDistOutput [ - pythonOutputDistHook - ] - ++ nativeBuildInputs - ++ build-system; - - buildInputs = validatePythonMatches "buildInputs" (buildInputs ++ pythonPath); - - propagatedBuildInputs = validatePythonMatches "propagatedBuildInputs" ( - propagatedBuildInputs - ++ dependencies - ++ [ - # we propagate python even for packages transformed with 'toPythonApplication' - # this pollutes the PATH but avoids rebuilds - # see https://github.com/NixOS/nixpkgs/issues/170887 for more context - python - ] - ); - - inherit strictDeps; - - LANG = "${if python.stdenv.hostPlatform.isDarwin then "en_US" else "C"}.UTF-8"; - - # Python packages don't have a checkPhase, only an installCheckPhase - doCheck = false; - doInstallCheck = attrs.doCheck or true; - nativeInstallCheckInputs = nativeCheckInputs; - installCheckInputs = checkInputs; - - postFixup = - optionalString (!dontWrapPythonPrograms) '' - wrapPythonPrograms - '' - + attrs.postFixup or ""; - - # Python packages built through cross-compilation are always for the host platform. - disallowedReferences = optionals (python.stdenv.hostPlatform != python.stdenv.buildPlatform) [ - python.pythonOnBuildForHost - ]; - - outputs = outputs ++ optional withDistOutput "dist"; - - passthru = - { - inherit disabled; - } - // { - updateScript = - let - filename = head (splitString ":" finalAttrs.finalPackage.meta.position); - in - [ - update-python-libraries - filename - ]; - } - // optionalAttrs (dependencies != [ ]) { - inherit dependencies; - } - // optionalAttrs (optional-dependencies != { }) { - inherit optional-dependencies; - } - // optionalAttrs (build-system != [ ]) { - inherit build-system; - } - // attrs.passthru or { }; - - meta = { - # default to python's platforms - platforms = python.meta.platforms; - isBuildPythonPackage = python.meta.platforms; - } // meta; - } - // optionalAttrs (attrs ? checkPhase) { - # If given use the specified checkPhase, otherwise use the setup hook. - # Longer-term we should get rid of `checkPhase` and use `installCheckPhase`. - installCheckPhase = attrs.checkPhase; - } - // optionalAttrs (attrs.doCheck or true) ( - optionalAttrs (disabledTestPaths != [ ]) { - disabledTestPaths = escapeShellArgs disabledTestPaths; + in + (cleanAttrs attrs) + // { + + name = namePrefix + name; + + nativeBuildInputs = + [ + python + wrapPython + ensureNewerSourcesForZipFilesHook # move to wheel installer (pip) or builder (setuptools, flit, ...)? + pythonRemoveTestsDirHook + ] + ++ optionals (catchConflicts && !isBootstrapPackage && !isSetuptoolsDependency) [ + # + # 1. When building a package that is also part of the bootstrap chain, we + # must ignore conflicts after installation, because there will be one with + # the package in the bootstrap. + # + # 2. When a package is a dependency of setuptools, we must ignore conflicts + # because the hook that checks for conflicts uses setuptools. + # + pythonCatchConflictsHook + ] + ++ optionals (attrs ? pythonRelaxDeps || attrs ? pythonRemoveDeps) [ + pythonRelaxDepsHook + ] + ++ optionals removeBinBytecode [ + pythonRemoveBinBytecodeHook + ] + ++ optionals (hasSuffix "zip" (attrs.src.name or "")) [ + unzip + ] + ++ optionals (format' == "setuptools") [ + setuptoolsBuildHook + ] + ++ optionals (format' == "pyproject") [ + ( + if isBootstrapPackage then + pypaBuildHook.override { + inherit (python.pythonOnBuildForHost.pkgs.bootstrap) build; + wheel = null; + } + else + pypaBuildHook + ) + ( + if isBootstrapPackage then + pythonRuntimeDepsCheckHook.override { + inherit (python.pythonOnBuildForHost.pkgs.bootstrap) packaging; + } + else + pythonRuntimeDepsCheckHook + ) + ] + ++ optionals (format' == "wheel") [ + wheelUnpackHook + ] + ++ optionals (format' == "egg") [ + eggUnpackHook + eggBuildHook + eggInstallHook + ] + ++ optionals (format' != "other") [ + ( + if isBootstrapInstallPackage then + pypaInstallHook.override { + inherit (python.pythonOnBuildForHost.pkgs.bootstrap) installer; + } + else + pypaInstallHook + ) + ] + ++ optionals (stdenv.buildPlatform == stdenv.hostPlatform) [ + # This is a test, however, it should be ran independent of the checkPhase and checkInputs + pythonImportsCheckHook + ] + ++ optionals (python.pythonAtLeast "3.3") [ + # Optionally enforce PEP420 for python3 + pythonNamespacesHook + ] + ++ optionals withDistOutput [ + pythonOutputDistHook + ] + ++ nativeBuildInputs + ++ build-system; + + buildInputs = validatePythonMatches "buildInputs" (buildInputs ++ pythonPath); + + propagatedBuildInputs = validatePythonMatches "propagatedBuildInputs" ( + propagatedBuildInputs + ++ dependencies + ++ [ + # we propagate python even for packages transformed with 'toPythonApplication' + # this pollutes the PATH but avoids rebuilds + # see https://github.com/NixOS/nixpkgs/issues/170887 for more context + python + ] + ); + + inherit strictDeps; + + LANG = "${if python.stdenv.hostPlatform.isDarwin then "en_US" else "C"}.UTF-8"; + + # Python packages don't have a checkPhase, only an installCheckPhase + doCheck = false; + doInstallCheck = attrs.doCheck or true; + nativeInstallCheckInputs = nativeCheckInputs; + installCheckInputs = checkInputs; + + postFixup = + optionalString (!dontWrapPythonPrograms) '' + wrapPythonPrograms + '' + + attrs.postFixup or ""; + + # Python packages built through cross-compilation are always for the host platform. + disallowedReferences = optionals (python.stdenv.hostPlatform != python.stdenv.buildPlatform) [ + python.pythonOnBuildForHost + ]; + + outputs = outputs ++ optional withDistOutput "dist"; + + passthru = + { + inherit disabled; } - // optionalAttrs (attrs ? disabledTests) { - # `escapeShellArgs` should be used as well as `disabledTestPaths`, - # but some packages rely on existing raw strings. - disabledTests = attrs.disabledTests; + // { + updateScript = + let + filename = head (splitString ":" finalAttrs.finalPackage.meta.position); + in + [ + update-python-libraries + filename + ]; + } + // optionalAttrs (dependencies != [ ]) { + inherit dependencies; } - // optionalAttrs (attrs ? pytestFlagsArray) { - pytestFlagsArray = attrs.pytestFlagsArray; + // optionalAttrs (optional-dependencies != { }) { + inherit optional-dependencies; } - // optionalAttrs (attrs ? unittestFlagsArray) { - unittestFlagsArray = attrs.unittestFlagsArray; + // optionalAttrs (build-system != [ ]) { + inherit build-system; } - ) + // attrs.passthru or { }; + + meta = { + # default to python's platforms + platforms = python.meta.platforms; + isBuildPythonPackage = python.meta.platforms; + } // meta; + } + // optionalAttrs (attrs ? checkPhase) { + # If given use the specified checkPhase, otherwise use the setup hook. + # Longer-term we should get rid of `checkPhase` and use `installCheckPhase`. + installCheckPhase = attrs.checkPhase; + } + // optionalAttrs (attrs.doCheck or true) ( + optionalAttrs (disabledTestPaths != [ ]) { + disabledTestPaths = escapeShellArgs disabledTestPaths; + } + // optionalAttrs (attrs ? disabledTests) { + # `escapeShellArgs` should be used as well as `disabledTestPaths`, + # but some packages rely on existing raw strings. + disabledTests = attrs.disabledTests; + } + // optionalAttrs (attrs ? pytestFlagsArray) { + pytestFlagsArray = attrs.pytestFlagsArray; + } + // optionalAttrs (attrs ? unittestFlagsArray) { + unittestFlagsArray = attrs.unittestFlagsArray; + } ) ); + # This derivation transformation function must be independent to `attrs` + # for fixed-point arguments support in the future. + transformDrv = + drv: + extendDerivation ( + drv.disabled + -> throw "${lib.removePrefix namePrefix drv.name} not supported for interpreter ${python.executable}" + ) { } (toPythonModule drv); + in -extendDerivation ( - disabled -> throw "${name} not supported for interpreter ${python.executable}" -) { } self +transformDrv self