at 23.11-beta 8.6 kB view raw
1# Generic builder only used for EOL and deprecated Python 2. 2 3{ lib 4, config 5, python 6, wrapPython 7, unzip 8, ensureNewerSourcesForZipFilesHook 9# Whether the derivation provides a Python module or not. 10, toPythonModule 11, namePrefix 12, update-python-libraries 13, setuptools 14, pipBuildHook 15, pipInstallHook 16, pythonCatchConflictsHook 17, pythonImportsCheckHook 18, pythonOutputDistHook 19, pythonRemoveBinBytecodeHook 20, pythonRemoveTestsDirHook 21, setuptoolsBuildHook 22, setuptoolsCheckHook 23, wheelUnpackHook 24, eggUnpackHook 25, eggBuildHook 26, eggInstallHook 27}: 28 29{ name ? "${attrs.pname}-${attrs.version}" 30 31# Build-time dependencies for the package 32, nativeBuildInputs ? [] 33 34# Run-time dependencies for the package 35, buildInputs ? [] 36 37# Dependencies needed for running the checkPhase. 38# These are added to buildInputs when doCheck = true. 39, checkInputs ? [] 40, nativeCheckInputs ? [] 41 42# propagate build dependencies so in case we have A -> B -> C, 43# C can import package A propagated by B 44, propagatedBuildInputs ? [] 45 46# DEPRECATED: use propagatedBuildInputs 47, pythonPath ? [] 48 49# Enabled to detect some (native)BuildInputs mistakes 50, strictDeps ? true 51 52, outputs ? [ "out" ] 53 54# used to disable derivation, useful for specific python versions 55, disabled ? false 56 57# Raise an error if two packages are installed with the same name 58# TODO: For cross we probably need a different PYTHONPATH, or not 59# add the runtime deps until after buildPhase. 60, catchConflicts ? (python.stdenv.hostPlatform == python.stdenv.buildPlatform) 61 62# Additional arguments to pass to the makeWrapper function, which wraps 63# generated binaries. 64, makeWrapperArgs ? [] 65 66# Skip wrapping of python programs altogether 67, dontWrapPythonPrograms ? false 68 69# Don't use Pip to install a wheel 70# Note this is actually a variable for the pipInstallPhase in pip's setupHook. 71# It's included here to prevent an infinite recursion. 72, dontUsePipInstall ? false 73 74# Skip setting the PYTHONNOUSERSITE environment variable in wrapped programs 75, permitUserSite ? false 76 77# Remove bytecode from bin folder. 78# When a Python script has the extension `.py`, bytecode is generated 79# Typically, executables in bin have no extension, so no bytecode is generated. 80# However, some packages do provide executables with extensions, and thus bytecode is generated. 81, removeBinBytecode ? true 82 83# Several package formats are supported. 84# "setuptools" : Install a common setuptools/distutils based package. This builds a wheel. 85# "wheel" : Install from a pre-compiled wheel. 86# "pyproject": Install a package using a ``pyproject.toml`` file (PEP517). This builds a wheel. 87# "egg": Install a package from an egg. 88# "other" : Provide your own buildPhase and installPhase. 89, format ? "setuptools" 90 91, meta ? {} 92 93, passthru ? {} 94 95, doCheck ? config.doCheckByDefault or false 96 97, disabledTestPaths ? [] 98 99, ... } @ attrs: 100 101let 102 inherit (python) stdenv; 103 104 withDistOutput = lib.elem format ["pyproject" "setuptools" "wheel"]; 105 106 name_ = name; 107 108 validatePythonMatches = attrName: let 109 isPythonModule = drv: 110 # all pythonModules have the pythonModule attribute 111 (drv ? "pythonModule") 112 # Some pythonModules are turned in to a pythonApplication by setting the field to false 113 && (!builtins.isBool drv.pythonModule); 114 isMismatchedPython = drv: drv.pythonModule != python; 115 116 optionalLocation = let 117 pos = builtins.unsafeGetAttrPos (if attrs ? "pname" then "pname" else "name") attrs; 118 in lib.optionalString (pos != null) " at ${pos.file}:${toString pos.line}:${toString pos.column}"; 119 120 leftPadName = name: against: let 121 len = lib.max (lib.stringLength name) (lib.stringLength against); 122 in lib.strings.fixedWidthString len " " name; 123 124 throwMismatch = drv: let 125 myName = "'${namePrefix}${name}'"; 126 theirName = "'${drv.name}'"; 127 in throw '' 128 Python version mismatch in ${myName}: 129 130 The Python derivation ${myName} depends on a Python derivation 131 named ${theirName}, but the two derivations use different versions 132 of Python: 133 134 ${leftPadName myName theirName} uses ${python} 135 ${leftPadName theirName myName} uses ${toString drv.pythonModule} 136 137 Possible solutions: 138 139 * If ${theirName} is a Python library, change the reference to ${theirName} 140 in the ${attrName} of ${myName} to use a ${theirName} built from the same 141 version of Python 142 143 * If ${theirName} is used as a tool during the build, move the reference to 144 ${theirName} in ${myName} from ${attrName} to nativeBuildInputs 145 146 * If ${theirName} provides executables that are called at run time, pass its 147 bin path to makeWrapperArgs: 148 149 makeWrapperArgs = [ "--prefix PATH : ''${lib.makeBinPath [ ${lib.getName drv } ] }" ]; 150 151 ${optionalLocation} 152 ''; 153 154 checkDrv = drv: 155 if (isPythonModule drv) && (isMismatchedPython drv) 156 then throwMismatch drv 157 else drv; 158 159 in inputs: builtins.map (checkDrv) inputs; 160 161 # Keep extra attributes from `attrs`, e.g., `patchPhase', etc. 162 self = toPythonModule (stdenv.mkDerivation ((builtins.removeAttrs attrs [ 163 "disabled" "checkPhase" "checkInputs" "nativeCheckInputs" "doCheck" "doInstallCheck" "dontWrapPythonPrograms" "catchConflicts" "format" 164 "disabledTestPaths" "outputs" 165 ]) // { 166 167 name = namePrefix + name_; 168 169 nativeBuildInputs = [ 170 python 171 wrapPython 172 ensureNewerSourcesForZipFilesHook # move to wheel installer (pip) or builder (setuptools, ...)? 173 pythonRemoveTestsDirHook 174 ] ++ lib.optionals catchConflicts [ 175 pythonCatchConflictsHook 176 ] ++ lib.optionals removeBinBytecode [ 177 pythonRemoveBinBytecodeHook 178 ] ++ lib.optionals (lib.hasSuffix "zip" (attrs.src.name or "")) [ 179 unzip 180 ] ++ lib.optionals (format == "setuptools") [ 181 setuptoolsBuildHook 182 ] ++ lib.optionals (format == "pyproject") [( 183 pipBuildHook 184 )] ++ lib.optionals (format == "wheel") [ 185 wheelUnpackHook 186 ] ++ lib.optionals (format == "egg") [ 187 eggUnpackHook eggBuildHook eggInstallHook 188 ] ++ lib.optionals (format != "other") [( 189 pipInstallHook 190 )] ++ lib.optionals (stdenv.buildPlatform == stdenv.hostPlatform) [ 191 # This is a test, however, it should be ran independent of the checkPhase and checkInputs 192 pythonImportsCheckHook 193 ] ++ lib.optionals withDistOutput [ 194 pythonOutputDistHook 195 ] ++ nativeBuildInputs; 196 197 buildInputs = validatePythonMatches "buildInputs" (buildInputs ++ pythonPath); 198 199 propagatedBuildInputs = validatePythonMatches "propagatedBuildInputs" (propagatedBuildInputs ++ [ 200 # we propagate python even for packages transformed with 'toPythonApplication' 201 # this pollutes the PATH but avoids rebuilds 202 # see https://github.com/NixOS/nixpkgs/issues/170887 for more context 203 python 204 ]); 205 206 inherit strictDeps; 207 208 LANG = "${if python.stdenv.isDarwin then "en_US" else "C"}.UTF-8"; 209 210 # Python packages don't have a checkPhase, only an installCheckPhase 211 doCheck = false; 212 doInstallCheck = attrs.doCheck or true; 213 nativeInstallCheckInputs = [ 214 ] ++ lib.optionals (format == "setuptools") [ 215 # Longer-term we should get rid of this and require 216 # users of this function to set the `installCheckPhase` or 217 # pass in a hook that sets it. 218 setuptoolsCheckHook 219 ] ++ nativeCheckInputs; 220 installCheckInputs = checkInputs; 221 222 postFixup = lib.optionalString (!dontWrapPythonPrograms) '' 223 wrapPythonPrograms 224 '' + attrs.postFixup or ""; 225 226 # Python packages built through cross-compilation are always for the host platform. 227 disallowedReferences = lib.optionals (python.stdenv.hostPlatform != python.stdenv.buildPlatform) [ python.pythonOnBuildForHost ]; 228 229 outputs = outputs ++ lib.optional withDistOutput "dist"; 230 231 meta = { 232 # default to python's platforms 233 platforms = python.meta.platforms; 234 isBuildPythonPackage = python.meta.platforms; 235 } // meta; 236 } // lib.optionalAttrs (attrs?checkPhase) { 237 # If given use the specified checkPhase, otherwise use the setup hook. 238 # Longer-term we should get rid of `checkPhase` and use `installCheckPhase`. 239 installCheckPhase = attrs.checkPhase; 240 } // lib.optionalAttrs (disabledTestPaths != []) { 241 disabledTestPaths = lib.escapeShellArgs disabledTestPaths; 242 })); 243 244 passthru.updateScript = let 245 filename = builtins.head (lib.splitString ":" self.meta.position); 246 in attrs.passthru.updateScript or [ update-python-libraries filename ]; 247in lib.extendDerivation 248 (disabled -> throw "${name} not supported for interpreter ${python.executable}") 249 passthru 250 self