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