at v192 6.1 kB view raw
1/* This function provides a generic Python package builder. It is 2 intended to work with packages that use `distutils/setuptools' 3 (http://pypi.python.org/pypi/setuptools/), which represents a large 4 number of Python packages nowadays. */ 5 6{ python, setuptools, unzip, wrapPython, lib, recursivePthLoader, distutils-cfg }: 7 8{ name 9 10# by default prefix `name` e.g. "python3.3-${name}" 11, namePrefix ? python.libPrefix + "-" 12 13, buildInputs ? [] 14 15# pass extra information to the distutils global configuration (think as global setup.cfg) 16, distutilsExtraCfg ? "" 17 18# propagate build dependencies so in case we have A -> B -> C, 19# C can import propagated packages by A 20, propagatedBuildInputs ? [] 21 22# passed to "python setup.py install" 23, setupPyInstallFlags ? [] 24 25# passed to "python setup.py build" 26, setupPyBuildFlags ? [] 27 28# enable tests by default 29, doCheck ? true 30 31# List of packages that should be added to the PYTHONPATH 32# environment variable in programs built by this function. Packages 33# in the standard `propagatedBuildInputs' variable are also added. 34# The difference is that `pythonPath' is not propagated to the user 35# environment. This is preferrable for programs because it doesn't 36# pollute the user environment. 37, pythonPath ? [] 38 39# used to disable derivation, useful for specific python versions 40, disabled ? false 41 42, meta ? {} 43 44# Execute before shell hook 45, preShellHook ? "" 46 47# Execute after shell hook 48, postShellHook ? "" 49 50# Additional arguments to pass to the makeWrapper function, which wraps 51# generated binaries. 52, makeWrapperArgs ? [] 53 54, ... } @ attrs: 55 56 57# Keep extra attributes from `attrs`, e.g., `patchPhase', etc. 58if disabled 59then throw "${name} not supported for interpreter ${python.executable}" 60else 61 62python.stdenv.mkDerivation (attrs // { 63 inherit doCheck; 64 65 name = namePrefix + name; 66 67 buildInputs = [ 68 wrapPython setuptools 69 (distutils-cfg.override { extraCfg = distutilsExtraCfg; }) 70 ] ++ buildInputs ++ pythonPath 71 ++ (lib.optional (lib.hasSuffix "zip" attrs.src.name or "") unzip); 72 73 # propagate python/setuptools to active setup-hook in nix-shell 74 propagatedBuildInputs = propagatedBuildInputs ++ [ recursivePthLoader python setuptools ]; 75 76 pythonPath = pythonPath; 77 78 configurePhase = attrs.configurePhase or '' 79 runHook preConfigure 80 81 # patch python interpreter to write null timestamps when compiling python files 82 # with following var we tell python to activate the patch so that python doesn't 83 # try to update them when we freeze timestamps in nix store 84 export DETERMINISTIC_BUILD=1 85 86 # prepend following line to import setuptools before distutils 87 # this way we make sure setuptools monkeypatches distutils commands 88 # this way setuptools provides extra helpers such as "python setup.py test" 89 sed -i '0,/import distutils/s//import setuptools;import distutils/' setup.py 90 sed -i '0,/from distutils/s//import setuptools;from distutils/' setup.py 91 92 runHook postConfigure 93 ''; 94 95 checkPhase = attrs.checkPhase or '' 96 runHook preCheck 97 98 ${python}/bin/${python.executable} setup.py test 99 100 runHook postCheck 101 ''; 102 103 buildPhase = attrs.buildPhase or '' 104 runHook preBuild 105 106 ${python}/bin/${python.executable} setup.py build ${lib.concatStringsSep " " setupPyBuildFlags} 107 108 runHook postBuild 109 ''; 110 111 installPhase = attrs.installPhase or '' 112 runHook preInstall 113 114 mkdir -p "$out/lib/${python.libPrefix}/site-packages" 115 116 export PYTHONPATH="$out/lib/${python.libPrefix}/site-packages:$PYTHONPATH" 117 118 ${python}/bin/${python.executable} setup.py install \ 119 --install-lib=$out/lib/${python.libPrefix}/site-packages \ 120 --old-and-unmanageable \ 121 --prefix="$out" ${lib.concatStringsSep " " setupPyInstallFlags} 122 123 # --install-lib: 124 # sometimes packages specify where files should be installed outside the usual 125 # python lib prefix, we override that back so all infrastructure (setup hooks) 126 # work as expected 127 128 # --old-and-unmanagable: 129 # instruct setuptools not to use eggs but fallback to plan package install 130 # this also reduces one .pth file in the chain, but the main reason is to 131 # force install process to install only scripts for the package we are 132 # installing (otherwise it will install scripts also for dependencies) 133 134 # A pth file might have been generated to load the package from 135 # within its own site-packages, rename this package not to 136 # collide with others. 137 eapth="$out/lib/${python.libPrefix}"/site-packages/easy-install.pth 138 if [ -e "$eapth" ]; then 139 # move colliding easy_install.pth to specifically named one 140 mv "$eapth" $(dirname "$eapth")/${name}.pth 141 fi 142 143 # Remove any site.py files generated by easy_install as these 144 # cause collisions. If pth files are to be processed a 145 # corresponding site.py needs to be included in the PYTHONPATH. 146 rm -f "$out/lib/${python.libPrefix}"/site-packages/site.py* 147 148 runHook postInstall 149 ''; 150 151 postFixup = attrs.postFixup or '' 152 wrapPythonPrograms 153 154 # TODO: document 155 createBuildInputsPth build-inputs "$buildInputStrings" 156 for inputsfile in propagated-build-inputs propagated-native-build-inputs; do 157 if test -e $out/nix-support/$inputsfile; then 158 createBuildInputsPth $inputsfile "$(cat $out/nix-support/$inputsfile)" 159 fi 160 done 161 ''; 162 163 shellHook = attrs.shellHook or '' 164 if test -e setup.py; then 165 tmp_path=/tmp/`pwd | md5sum | cut -f 1 -d " "`-$name 166 mkdir -p $tmp_path/lib/${python.libPrefix}/site-packages 167 ${preShellHook} 168 export PATH="$tmp_path/bin:$PATH" 169 export PYTHONPATH="$tmp_path/lib/${python.libPrefix}/site-packages:$PYTHONPATH" 170 ${python}/bin/${python.executable} setup.py develop --prefix $tmp_path 171 ${postShellHook} 172 fi 173 ''; 174 175 meta = with lib.maintainers; { 176 # default to python's platforms 177 platforms = python.meta.platforms; 178 } // meta // { 179 # add extra maintainer(s) to every package 180 maintainers = (meta.maintainers or []) ++ [ chaoflow iElectric ]; 181 }; 182 183})