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, pythonRemoveBinBytecodeHook
21, pythonRemoveTestsDirHook
22, setuptoolsBuildHook
23, setuptoolsCheckHook
24, wheelUnpackHook
25, eggUnpackHook
26, eggBuildHook
27, eggInstallHook
28}:
29
30{ name ? "${attrs.pname}-${attrs.version}"
31
32# Build-time dependencies for the package
33, nativeBuildInputs ? []
34
35# Run-time dependencies for the package
36, buildInputs ? []
37
38# Dependencies needed for running the checkPhase.
39# These are added to buildInputs when doCheck = true.
40, checkInputs ? []
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# used to disable derivation, useful for specific python versions
53, disabled ? false
54
55# Raise an error if two packages are installed with the same name
56# TODO: For cross we probably need a different PYTHONPATH, or not
57# add the runtime deps until after buildPhase.
58, catchConflicts ? (python.stdenv.hostPlatform == python.stdenv.buildPlatform)
59
60# Additional arguments to pass to the makeWrapper function, which wraps
61# generated binaries.
62, makeWrapperArgs ? []
63
64# Skip wrapping of python programs altogether
65, dontWrapPythonPrograms ? false
66
67# Don't use Pip to install a wheel
68# Note this is actually a variable for the pipInstallPhase in pip's setupHook.
69# It's included here to prevent an infinite recursion.
70, dontUsePipInstall ? false
71
72# Skip setting the PYTHONNOUSERSITE environment variable in wrapped programs
73, permitUserSite ? false
74
75# Remove bytecode from bin folder.
76# When a Python script has the extension `.py`, bytecode is generated
77# Typically, executables in bin have no extension, so no bytecode is generated.
78# However, some packages do provide executables with extensions, and thus bytecode is generated.
79, removeBinBytecode ? true
80
81# Several package formats are supported.
82# "setuptools" : Install a common setuptools/distutils based package. This builds a wheel.
83# "wheel" : Install from a pre-compiled wheel.
84# "flit" : Install a flit package. This builds a wheel.
85# "pyproject": Install a package using a ``pyproject.toml`` file (PEP517). This builds a wheel.
86# "egg": Install a package from an egg.
87# "other" : Provide your own buildPhase and installPhase.
88, format ? "setuptools"
89
90, meta ? {}
91
92, passthru ? {}
93
94, doCheck ? config.doCheckByDefault or false
95
96, disabledTestPaths ? []
97
98, ... } @ attrs:
99
100
101# Keep extra attributes from `attrs`, e.g., `patchPhase', etc.
102if disabled
103then throw "${name} not supported for interpreter ${python.executable}"
104else
105
106let
107 inherit (python) stdenv;
108
109 name_ = name;
110
111 self = toPythonModule (stdenv.mkDerivation ((builtins.removeAttrs attrs [
112 "disabled" "checkPhase" "checkInputs" "doCheck" "doInstallCheck" "dontWrapPythonPrograms" "catchConflicts" "format"
113 "disabledTestPaths"
114 ]) // {
115
116 name = namePrefix + name_;
117
118 nativeBuildInputs = [
119 python
120 wrapPython
121 ensureNewerSourcesForZipFilesHook # move to wheel installer (pip) or builder (setuptools, flit, ...)?
122 pythonRemoveTestsDirHook
123 ] ++ lib.optionals catchConflicts [
124 setuptools pythonCatchConflictsHook
125 ] ++ lib.optionals removeBinBytecode [
126 pythonRemoveBinBytecodeHook
127 ] ++ lib.optionals (lib.hasSuffix "zip" (attrs.src.name or "")) [
128 unzip
129 ] ++ lib.optionals (format == "setuptools") [
130 setuptoolsBuildHook
131 ] ++ lib.optionals (format == "flit") [
132 flitBuildHook
133 ] ++ lib.optionals (format == "pyproject") [
134 pipBuildHook
135 ] ++ lib.optionals (format == "wheel") [
136 wheelUnpackHook
137 ] ++ lib.optionals (format == "egg") [
138 eggUnpackHook eggBuildHook eggInstallHook
139 ] ++ lib.optionals (!(format == "other") || dontUsePipInstall) [
140 pipInstallHook
141 ] ++ lib.optionals (stdenv.buildPlatform == stdenv.hostPlatform) [
142 # This is a test, however, it should be ran independent of the checkPhase and checkInputs
143 pythonImportsCheckHook
144 ] ++ lib.optionals (python.pythonAtLeast "3.3") [
145 # Optionally enforce PEP420 for python3
146 pythonNamespacesHook
147 ] ++ nativeBuildInputs;
148
149 buildInputs = buildInputs ++ pythonPath;
150
151 propagatedBuildInputs = propagatedBuildInputs ++ [ python ];
152
153 inherit strictDeps;
154
155 LANG = "${if python.stdenv.isDarwin then "en_US" else "C"}.UTF-8";
156
157 # Python packages don't have a checkPhase, only an installCheckPhase
158 doCheck = false;
159 doInstallCheck = attrs.doCheck or true;
160 installCheckInputs = [
161 ] ++ lib.optionals (format == "setuptools") [
162 # Longer-term we should get rid of this and require
163 # users of this function to set the `installCheckPhase` or
164 # pass in a hook that sets it.
165 setuptoolsCheckHook
166 ] ++ checkInputs;
167
168 postFixup = lib.optionalString (!dontWrapPythonPrograms) ''
169 wrapPythonPrograms
170 '' + attrs.postFixup or "";
171
172 # Python packages built through cross-compilation are always for the host platform.
173 disallowedReferences = lib.optionals (python.stdenv.hostPlatform != python.stdenv.buildPlatform) [ python.pythonForBuild ];
174
175 meta = {
176 # default to python's platforms
177 platforms = python.meta.platforms;
178 isBuildPythonPackage = python.meta.platforms;
179 } // lib.optionalAttrs (attrs?pname) {
180 mainProgram = attrs.pname;
181 } // meta;
182 } // lib.optionalAttrs (attrs?checkPhase) {
183 # If given use the specified checkPhase, otherwise use the setup hook.
184 # Longer-term we should get rid of `checkPhase` and use `installCheckPhase`.
185 installCheckPhase = attrs.checkPhase;
186 } // lib.optionalAttrs (disabledTestPaths != []) {
187 disabledTestPaths = lib.escapeShellArgs disabledTestPaths;
188 }));
189
190 passthru.updateScript = let
191 filename = builtins.head (lib.splitString ":" self.meta.position);
192 in attrs.passthru.updateScript or [ update-python-libraries filename ];
193in lib.extendDerivation true passthru self