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