diff --git a/pkgs/development/interpreters/python/python2/mk-python-derivation.nix b/pkgs/development/interpreters/python/python2/mk-python-derivation.nix index 4bdda15062900..344360057db24 100644 --- a/pkgs/development/interpreters/python/python2/mk-python-derivation.nix +++ b/pkgs/development/interpreters/python/python2/mk-python-derivation.nix @@ -27,275 +27,295 @@ eggBuildHook, eggInstallHook, }: +lib.extendMkDerivation { + constructDrv = stdenv.mkDerivation; + + excludeDrvArgNames = [ + "disabled" + "checkPhase" + "checkInputs" + "nativeCheckInputs" + "doCheck" + "doInstallCheck" + "dontWrapPythonPrograms" + "catchConflicts" + "format" + "disabledTestPaths" + ]; -{ - name ? "${attrs.pname}-${attrs.version}", - - # Build-time dependencies for the package - nativeBuildInputs ? [ ], - - # Run-time dependencies for the package - buildInputs ? [ ], + extendDrvArgs = + finalAttrs: + { + name ? "${attrs.pname}-${attrs.version}", - # Dependencies needed for running the checkPhase. - # These are added to buildInputs when doCheck = true. - checkInputs ? [ ], - nativeCheckInputs ? [ ], + # Build-time dependencies for the package + nativeBuildInputs ? [ ], - # propagate build dependencies so in case we have A -> B -> C, - # C can import package A propagated by B - propagatedBuildInputs ? [ ], + # Run-time dependencies for the package + buildInputs ? [ ], - # DEPRECATED: use propagatedBuildInputs - pythonPath ? [ ], + # Dependencies needed for running the checkPhase. + # These are added to buildInputs when doCheck = true. + checkInputs ? [ ], + nativeCheckInputs ? [ ], - # Enabled to detect some (native)BuildInputs mistakes - strictDeps ? true, + # propagate build dependencies so in case we have A -> B -> C, + # C can import package A propagated by B + propagatedBuildInputs ? [ ], - outputs ? [ "out" ], + # DEPRECATED: use propagatedBuildInputs + pythonPath ? [ ], - # used to disable derivation, useful for specific python versions - disabled ? false, + # Enabled to detect some (native)BuildInputs mistakes + strictDeps ? true, - # Raise an error if two packages are installed with the same name - # TODO: For cross we probably need a different PYTHONPATH, or not - # add the runtime deps until after buildPhase. - catchConflicts ? (python.stdenv.hostPlatform == python.stdenv.buildPlatform), + outputs ? [ "out" ], - # Additional arguments to pass to the makeWrapper function, which wraps - # generated binaries. - makeWrapperArgs ? [ ], + # used to disable derivation, useful for specific python versions + disabled ? false, - # Skip wrapping of python programs altogether - dontWrapPythonPrograms ? false, + # Raise an error if two packages are installed with the same name + # TODO: For cross we probably need a different PYTHONPATH, or not + # add the runtime deps until after buildPhase. + catchConflicts ? (python.stdenv.hostPlatform == python.stdenv.buildPlatform), - # Don't use Pip to install a wheel - # Note this is actually a variable for the pipInstallPhase in pip's setupHook. - # It's included here to prevent an infinite recursion. - dontUsePipInstall ? false, + # Additional arguments to pass to the makeWrapper function, which wraps + # generated binaries. + makeWrapperArgs ? [ ], - # Skip setting the PYTHONNOUSERSITE environment variable in wrapped programs - permitUserSite ? false, + # Skip wrapping of python programs altogether + dontWrapPythonPrograms ? false, - # Remove bytecode from bin folder. - # When a Python script has the extension `.py`, bytecode is generated - # Typically, executables in bin have no extension, so no bytecode is generated. - # However, some packages do provide executables with extensions, and thus bytecode is generated. - removeBinBytecode ? true, + # Don't use Pip to install a wheel + # Note this is actually a variable for the pipInstallPhase in pip's setupHook. + # It's included here to prevent an infinite recursion. + dontUsePipInstall ? false, - # Several package formats are supported. - # "setuptools" : Install a common setuptools/distutils based package. This builds a wheel. - # "wheel" : Install from a pre-compiled wheel. - # "pyproject": Install a package using a ``pyproject.toml`` file (PEP517). This builds a wheel. - # "egg": Install a package from an egg. - # "other" : Provide your own buildPhase and installPhase. - format ? "setuptools", + # Skip setting the PYTHONNOUSERSITE environment variable in wrapped programs + permitUserSite ? false, - meta ? { }, + # Remove bytecode from bin folder. + # When a Python script has the extension `.py`, bytecode is generated + # Typically, executables in bin have no extension, so no bytecode is generated. + # However, some packages do provide executables with extensions, and thus bytecode is generated. + removeBinBytecode ? true, - passthru ? { }, + # Several package formats are supported. + # "setuptools" : Install a common setuptools/distutils based package. This builds a wheel. + # "wheel" : Install from a pre-compiled wheel. + # "pyproject": Install a package using a ``pyproject.toml`` file (PEP517). This builds a wheel. + # "egg": Install a package from an egg. + # "other" : Provide your own buildPhase and installPhase. + format ? "setuptools", - doCheck ? true, + meta ? { }, - disabledTestPaths ? [ ], + passthru ? { }, - ... -}@attrs: + doCheck ? true, -let - withDistOutput = lib.elem format [ - "pyproject" - "setuptools" - "wheel" - ]; + disabledTestPaths ? [ ], - name_ = name; + ... + }@attrs: - validatePythonMatches = - attrName: let - isPythonModule = - drv: - # all pythonModules have the pythonModule attribute - (drv ? "pythonModule") - # Some pythonModules are turned in to a pythonApplication by setting the field to false - && (!builtins.isBool drv.pythonModule); - isMismatchedPython = drv: drv.pythonModule != python; + withDistOutput = lib.elem format [ + "pyproject" + "setuptools" + "wheel" + ]; - optionalLocation = - let - pos = builtins.unsafeGetAttrPos (if attrs ? "pname" then "pname" else "name") attrs; - in - lib.optionalString (pos != null) " at ${pos.file}:${toString pos.line}:${toString pos.column}"; + name_ = name; - leftPadName = - name: against: + validatePythonMatches = + attrName: let - len = lib.max (lib.stringLength name) (lib.stringLength against); - in - lib.strings.fixedWidthString len " " name; + isPythonModule = + drv: + # all pythonModules have the pythonModule attribute + (drv ? "pythonModule") + # Some pythonModules are turned in to a pythonApplication by setting the field to false + && (!builtins.isBool drv.pythonModule); + isMismatchedPython = drv: drv.pythonModule != python; + + optionalLocation = + let + pos = builtins.unsafeGetAttrPos (if attrs ? "pname" then "pname" else "name") attrs; + in + lib.optionalString (pos != null) " at ${pos.file}:${toString pos.line}:${toString pos.column}"; + + leftPadName = + name: against: + let + len = lib.max (lib.stringLength name) (lib.stringLength against); + in + lib.strings.fixedWidthString len " " name; + + throwMismatch = + drv: + let + myName = "'${namePrefix}${name}'"; + theirName = "'${drv.name}'"; + in + 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: + + ${leftPadName myName theirName} uses ${python} + ${leftPadName theirName myName} uses ${toString drv.pythonModule} + + 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 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: + + makeWrapperArgs = [ "--prefix PATH : ''${lib.makeBinPath [ ${lib.getName drv} ] }" ]; + + ${optionalLocation} + ''; + + checkDrv = drv: if (isPythonModule drv) && (isMismatchedPython drv) then throwMismatch drv else drv; - throwMismatch = - drv: - let - myName = "'${namePrefix}${name}'"; - theirName = "'${drv.name}'"; in - 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: - - ${leftPadName myName theirName} uses ${python} - ${leftPadName theirName myName} uses ${toString drv.pythonModule} - - 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 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: - - makeWrapperArgs = [ "--prefix PATH : ''${lib.makeBinPath [ ${lib.getName drv} ] }" ]; - - ${optionalLocation} - ''; - - checkDrv = drv: if (isPythonModule drv) && (isMismatchedPython drv) then throwMismatch drv else drv; + inputs: map checkDrv inputs; in - inputs: map checkDrv inputs; - - # Keep extra attributes from `attrs`, e.g., `patchPhase', etc. - self = toPythonModule ( - stdenv.mkDerivation ( - (removeAttrs attrs [ - "disabled" - "checkPhase" - "checkInputs" - "nativeCheckInputs" - "doCheck" - "doInstallCheck" - "dontWrapPythonPrograms" - "catchConflicts" - "format" - "disabledTestPaths" - "outputs" - ]) - // { - - name = namePrefix + name_; - - nativeBuildInputs = [ + { + + name = namePrefix + name_; + + nativeBuildInputs = [ + python + wrapPython + ensureNewerSourcesForZipFilesHook # move to wheel installer (pip) or builder (setuptools, ...)? + pythonRemoveTestsDirHook + ] + ++ lib.optionals catchConflicts [ + pythonCatchConflictsHook + ] + ++ lib.optionals removeBinBytecode [ + pythonRemoveBinBytecodeHook + ] + ++ lib.optionals (lib.hasSuffix "zip" (attrs.src.name or "")) [ + unzip + ] + ++ lib.optionals (format == "setuptools") [ + setuptoolsBuildHook + ] + ++ lib.optionals (format == "pyproject") [ + pipBuildHook + ] + ++ lib.optionals (format == "wheel") [ + wheelUnpackHook + ] + ++ lib.optionals (format == "egg") [ + eggUnpackHook + eggBuildHook + eggInstallHook + ] + ++ lib.optionals (format != "other") [ + pipInstallHook + ] + ++ lib.optionals (stdenv.buildPlatform == stdenv.hostPlatform) [ + # This is a test, however, it should be ran independent of the checkPhase and checkInputs + pythonImportsCheckHook + ] + ++ lib.optionals withDistOutput [ + pythonOutputDistHook + ] + ++ nativeBuildInputs; + + buildInputs = validatePythonMatches "buildInputs" (buildInputs ++ pythonPath); + + propagatedBuildInputs = validatePythonMatches "propagatedBuildInputs" ( + propagatedBuildInputs + ++ [ + # 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 - wrapPython - ensureNewerSourcesForZipFilesHook # move to wheel installer (pip) or builder (setuptools, ...)? - pythonRemoveTestsDirHook - ] - ++ lib.optionals catchConflicts [ - pythonCatchConflictsHook - ] - ++ lib.optionals removeBinBytecode [ - pythonRemoveBinBytecodeHook - ] - ++ lib.optionals (lib.hasSuffix "zip" (attrs.src.name or "")) [ - unzip - ] - ++ lib.optionals (format == "setuptools") [ - setuptoolsBuildHook - ] - ++ lib.optionals (format == "pyproject") [ - pipBuildHook - ] - ++ lib.optionals (format == "wheel") [ - wheelUnpackHook ] - ++ lib.optionals (format == "egg") [ - eggUnpackHook - eggBuildHook - eggInstallHook - ] - ++ lib.optionals (format != "other") [ - pipInstallHook - ] - ++ lib.optionals (stdenv.buildPlatform == stdenv.hostPlatform) [ - # This is a test, however, it should be ran independent of the checkPhase and checkInputs - pythonImportsCheckHook - ] - ++ lib.optionals withDistOutput [ - pythonOutputDistHook - ] - ++ nativeBuildInputs; - - buildInputs = validatePythonMatches "buildInputs" (buildInputs ++ pythonPath); - - propagatedBuildInputs = validatePythonMatches "propagatedBuildInputs" ( - propagatedBuildInputs - ++ [ - # 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 = - lib.optionalString (!dontWrapPythonPrograms) '' - wrapPythonPrograms - '' - + attrs.postFixup or ""; - - # Python packages built through cross-compilation are always for the host platform. - disallowedReferences = lib.optionals (python.stdenv.hostPlatform != python.stdenv.buildPlatform) [ - python.pythonOnBuildForHost - ]; - - outputs = outputs ++ lib.optional withDistOutput "dist"; - - meta = { - # default to python's platforms - platforms = python.meta.platforms; - isBuildPythonPackage = python.meta.platforms; - } - // meta; + ); + + 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 = + lib.optionalString (!dontWrapPythonPrograms) '' + wrapPythonPrograms + '' + + attrs.postFixup or ""; + + # Python packages built through cross-compilation are always for the host platform. + disallowedReferences = lib.optionals (python.stdenv.hostPlatform != python.stdenv.buildPlatform) [ + python.pythonOnBuildForHost + ]; + + outputs = outputs ++ lib.optional withDistOutput "dist"; + + passthru = { + inherit + disabled + ; + updateScript = + let + filename = builtins.head (lib.splitString ":" finalAttrs.finalPackage.meta.position); + in + [ + update-python-libraries + filename + ]; } - // lib.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; - } - // lib.optionalAttrs (disabledTestPaths != [ ]) { - disabledTestPaths = lib.escapeShellArgs disabledTestPaths; - } - ) - ); + // passthru; - passthru.updateScript = + meta = { + # default to python's platforms + platforms = python.meta.platforms; + isBuildPythonPackage = python.meta.platforms; + } + // meta; + } + // lib.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; + } + // lib.optionalAttrs (disabledTestPaths != [ ]) { + disabledTestPaths = lib.escapeShellArgs disabledTestPaths; + }; + + transformDrv = let - filename = builtins.head (lib.splitString ":" self.meta.position); + # Workaround to make the `lib.extendDerivation`-based disabled functionality + # respect `.overrideAttrs` + # It doesn't cover `..overrideAttrs`. + disablePythonPackage = + drv: + lib.extendDerivation ( + drv.disabled + -> throw "${lib.removePrefix namePrefix drv.name} not supported for interpreter ${python.executable}" + ) { } drv + // { + overrideAttrs = fdrv: disablePythonPackage (drv.overrideAttrs fdrv); + }; in - attrs.passthru.updateScript or [ - update-python-libraries - filename - ]; -in -lib.extendDerivation ( - disabled -> throw "${name} not supported for interpreter ${python.executable}" -) passthru self + drv: disablePythonPackage (toPythonModule drv); +}