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