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{ lib
7, python
8, mkPythonDerivation
9, bootstrapped-pip
10}:
11
12{ buildInputs ? []
13
14# propagate build dependencies so in case we have A -> B -> C,
15# C can import package A propagated by B
16#, propagatedBuildInputs ? []
17
18# passed to "python setup.py build_ext"
19# https://github.com/pypa/pip/issues/881
20, setupPyBuildFlags ? []
21
22# Execute before shell hook
23, preShellHook ? ""
24
25# Execute after shell hook
26, postShellHook ? ""
27
28# Additional flags to pass to "pip install".
29, installFlags ? []
30
31, format ? "setup"
32
33, ... } @ attrs:
34
35
36
37
38let
39 # use setuptools shim (so that setuptools is imported before distutils)
40 # pip does the same thing: https://github.com/pypa/pip/pull/3265
41 setuppy = ./run_setup.py;
42
43 formatspecific =
44 if format == "wheel" then
45 {
46 unpackPhase = ''
47 mkdir dist
48 cp $src dist/"''${src#*-}"
49 '';
50
51 # Wheels are pre-compiled
52 buildPhase = attrs.buildPhase or ":";
53 installCheckPhase = attrs.checkPhase or ":";
54
55 # Wheels don't have any checks to run
56 doCheck = attrs.doCheck or false;
57 }
58 else if format == "setup" then
59 {
60 # we copy nix_run_setup.py over so it's executed relative to the root of the source
61 # many project make that assumption
62 buildPhase = attrs.buildPhase or ''
63 runHook preBuild
64 cp ${setuppy} nix_run_setup.py
65 ${python.interpreter} nix_run_setup.py ${lib.optionalString (setupPyBuildFlags != []) ("build_ext " + (lib.concatStringsSep " " setupPyBuildFlags))} bdist_wheel
66 runHook postBuild
67 '';
68
69 installCheckPhase = attrs.checkPhase or ''
70 runHook preCheck
71 ${python.interpreter} nix_run_setup.py test
72 runHook postCheck
73 '';
74
75 # Python packages that are installed with setuptools
76 # are typically distributed with tests.
77 # With Python it's a common idiom to run the tests
78 # after the software has been installed.
79 doCheck = attrs.doCheck or true;
80 }
81 else
82 throw "Unsupported format ${format}";
83
84in mkPythonDerivation ( attrs // {
85
86 # To build and install a wheel we need pip
87 buildInputs = buildInputs ++ [ bootstrapped-pip ];
88
89#inherit propagatedBuildInputs;
90
91 configurePhase = attrs.configurePhase or ''
92 runHook preConfigure
93
94 # patch python interpreter to write null timestamps when compiling python files
95 # this way python doesn't try to update them when we freeze timestamps in nix store
96 export DETERMINISTIC_BUILD=1
97
98 runHook postConfigure
99 '';
100
101 installPhase = attrs.installPhase or ''
102 runHook preInstall
103
104 mkdir -p "$out/${python.sitePackages}"
105 export PYTHONPATH="$out/${python.sitePackages}:$PYTHONPATH"
106
107 pushd dist
108 ${bootstrapped-pip}/bin/pip install *.whl --no-index --prefix=$out --no-cache ${toString installFlags}
109 popd
110
111 runHook postInstall
112 '';
113
114 shellHook = attrs.shellHook or ''
115 ${preShellHook}
116 if test -e setup.py; then
117 tmp_path=$(mktemp -d)
118 export PATH="$tmp_path/bin:$PATH"
119 export PYTHONPATH="$tmp_path/${python.sitePackages}:$PYTHONPATH"
120 mkdir -p $tmp_path/${python.sitePackages}
121 ${bootstrapped-pip}/bin/pip install -e . --prefix $tmp_path
122 fi
123 ${postShellHook}
124 '';
125
126} // formatspecific)