core/pkgs/by-name/py/python/python2/mk-python-derivation.nix

246 lines
8.7 KiB
Nix
Raw Normal View History

2024-05-02 00:46:19 +00:00
# Generic builder only used for EOL and deprecated Python 2.
2024-05-13 21:24:10 +00:00
{ lib, config, python, wrapPython, unzip, ensureNewerSourcesForZipFilesHook
2024-05-02 00:46:19 +00:00
# Whether the derivation provides a Python module or not.
2024-05-13 21:24:10 +00:00
, toPythonModule, namePrefix, update-python-libraries, setuptools, pipBuildHook
, pipInstallHook, pythonCatchConflictsHook, pythonImportsCheckHook
, pythonOutputDistHook, pythonRemoveBinBytecodeHook, pythonRemoveTestsDirHook
, setuptoolsBuildHook, setuptoolsCheckHook, wheelUnpackHook, eggUnpackHook
, eggBuildHook, eggInstallHook }:
2024-05-02 00:46:19 +00:00
{ name ? "${attrs.pname}-${attrs.version}"
2024-05-13 21:24:10 +00:00
# Build-time dependencies for the package
, nativeBuildInputs ? [ ]
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
# Run-time dependencies for the package
, buildInputs ? [ ]
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
# Dependencies needed for running the checkPhase.
# These are added to buildInputs when doCheck = true.
, checkInputs ? [ ], nativeCheckInputs ? [ ]
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
# propagate build dependencies so in case we have A -> B -> C,
# C can import package A propagated by B
, propagatedBuildInputs ? [ ]
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
# DEPRECATED: use propagatedBuildInputs
, pythonPath ? [ ]
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
# Enabled to detect some (native)BuildInputs mistakes
2024-05-02 00:46:19 +00:00
, strictDeps ? true
2024-05-13 21:24:10 +00:00
, outputs ? [
"out"
]
2024-05-02 00:46:19 +00:00
# used to disable derivation, useful for specific python versions
, disabled ? false
2024-05-13 21:24:10 +00:00
# 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.
2024-05-02 00:46:19 +00:00
, catchConflicts ? (python.stdenv.hostPlatform == python.stdenv.buildPlatform)
# Additional arguments to pass to the makeWrapper function, which wraps
# generated binaries.
2024-05-13 21:24:10 +00:00
, makeWrapperArgs ? [ ]
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
# Skip wrapping of python programs altogether
2024-05-02 00:46:19 +00:00
, dontWrapPythonPrograms ? false
2024-05-13 21:24:10 +00:00
# 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.
2024-05-02 00:46:19 +00:00
, dontUsePipInstall ? false
2024-05-13 21:24:10 +00:00
# Skip setting the PYTHONNOUSERSITE environment variable in wrapped programs
2024-05-02 00:46:19 +00:00
, permitUserSite ? false
2024-05-13 21:24:10 +00:00
# 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.
2024-05-02 00:46:19 +00:00
, removeBinBytecode ? true
2024-05-13 21:24:10 +00:00
# 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.
2024-05-02 00:46:19 +00:00
, format ? "setuptools"
2024-05-13 21:24:10 +00:00
, meta ? { }
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
, passthru ? { }
2024-05-02 00:46:19 +00:00
, doCheck ? config.doCheckByDefault or false
2024-05-13 21:24:10 +00:00
, disabledTestPaths ? [ ]
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
, ... }@attrs:
2024-05-02 00:46:19 +00:00
let
inherit (python) stdenv;
2024-05-13 21:24:10 +00:00
withDistOutput = lib.elem format [ "pyproject" "setuptools" "wheel" ];
2024-05-02 00:46:19 +00:00
name_ = name;
2024-05-13 21:24:10 +00:00
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;
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
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}";
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
leftPadName = name: against:
let len = lib.max (lib.stringLength name) (lib.stringLength against);
in lib.strings.fixedWidthString len " " name;
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
throwMismatch = drv:
let
myName = "'${namePrefix}${name}'";
theirName = "'${drv.name}'";
in throw ''
Python version mismatch in ${myName}:
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
The Python derivation ${myName} depends on a Python derivation
named ${theirName}, but the two derivations use different versions
of Python:
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
${leftPadName myName theirName} uses ${python}
${leftPadName theirName myName} uses ${toString drv.pythonModule}
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
Possible solutions:
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
* 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
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
* If ${theirName} is used as a tool during the build, move the reference to
${theirName} in ${myName} from ${attrName} to nativeBuildInputs
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
* If ${theirName} provides executables that are called at run time, pass its
bin path to makeWrapperArgs:
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
makeWrapperArgs = [ "--prefix PATH : ''${lib.makeBinPath [ ${
lib.getName drv
} ] }" ];
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
${optionalLocation}
'';
2024-05-02 00:46:19 +00:00
2024-05-13 21:24:10 +00:00
checkDrv = drv:
if (isPythonModule drv) && (isMismatchedPython drv) then
throwMismatch drv
else
drv;
2024-05-02 00:46:19 +00:00
in inputs: builtins.map (checkDrv) inputs;
# Keep extra attributes from `attrs`, e.g., `patchPhase', etc.
self = toPythonModule (stdenv.mkDerivation ((builtins.removeAttrs attrs [
2024-05-13 21:24:10 +00:00
"disabled"
"checkPhase"
"checkInputs"
"nativeCheckInputs"
"doCheck"
"doInstallCheck"
"dontWrapPythonPrograms"
"catchConflicts"
"format"
"disabledTestPaths"
"outputs"
2024-05-02 00:46:19 +00:00
]) // {
name = namePrefix + name_;
nativeBuildInputs = [
python
wrapPython
2024-05-13 21:24:10 +00:00
ensureNewerSourcesForZipFilesHook # move to wheel installer (pip) or builder (setuptools, ...)?
2024-05-02 00:46:19 +00:00
pythonRemoveTestsDirHook
2024-05-13 21:24:10 +00:00
] ++ 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
]);
2024-05-02 00:46:19 +00:00
inherit strictDeps;
LANG = "${if python.stdenv.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;
2024-05-13 21:24:10 +00:00
nativeInstallCheckInputs = [ ] ++ lib.optionals (format == "setuptools") [
2024-05-02 00:46:19 +00:00
# Longer-term we should get rid of this and require
# users of this function to set the `installCheckPhase` or
# pass in a hook that sets it.
setuptoolsCheckHook
] ++ nativeCheckInputs;
installCheckInputs = checkInputs;
postFixup = lib.optionalString (!dontWrapPythonPrograms) ''
wrapPythonPrograms
'' + attrs.postFixup or "";
# Python packages built through cross-compilation are always for the host platform.
2024-05-13 21:24:10 +00:00
disallowedReferences =
lib.optionals (python.stdenv.hostPlatform != python.stdenv.buildPlatform)
[ python.pythonOnBuildForHost ];
2024-05-02 00:46:19 +00:00
outputs = outputs ++ lib.optional withDistOutput "dist";
meta = {
# default to python's platforms
platforms = python.meta.platforms;
isBuildPythonPackage = python.meta.platforms;
} // meta;
2024-05-13 21:24:10 +00:00
} // lib.optionalAttrs (attrs ? checkPhase) {
2024-05-02 00:46:19 +00:00
# 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;
2024-05-13 21:24:10 +00:00
} // lib.optionalAttrs (disabledTestPaths != [ ]) {
disabledTestPaths = lib.escapeShellArgs disabledTestPaths;
2024-05-02 00:46:19 +00:00
}));
2024-05-13 21:24:10 +00:00
passthru.updateScript =
let filename = builtins.head (lib.splitString ":" self.meta.position);
2024-05-02 00:46:19 +00:00
in attrs.passthru.updateScript or [ update-python-libraries filename ];
in lib.extendDerivation
2024-05-13 21:24:10 +00:00
(disabled -> throw "${name} not supported for interpreter ${python.executable}")
passthru self