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