1################################################################################
2# Python package infrastructure
3#
4# This file implements an infrastructure that eases development of
5# package .mk files for Python packages. It should be used for all
6# packages that use Python setup.py/setuptools as their build system.
7#
8# See the Buildroot documentation for details on the usage of this
9# infrastructure
10#
11# In terms of implementation, this Python infrastructure requires the
12# .mk file to only specify metadata information about the package:
13# name, version, download URL, etc.
14#
15# We still allow the package .mk file to override what the different
16# steps are doing, if needed. For example, if <PKG>_BUILD_CMDS is
17# already defined, it is used as the list of commands to perform to
18# build the package, instead of the default Python behaviour. The
19# package can also define some post operation hooks.
20#
21################################################################################
22
23ifeq ($(BR2_arm)$(BR2_armeb),y)
24PKG_PYTHON_ARCH = arm
25else
26PKG_PYTHON_ARCH = $(ARCH)
27endif
28PKG_PYTHON_HOST_PLATFORM = linux-$(PKG_PYTHON_ARCH)
29
30# basename does not evaluate if a file exists, so we must check to ensure
31# the _sysconfigdata__linux_*.py file exists. The "|| true" is added to return
32# an empty string if the file does not exist.
33PKG_PYTHON_SYSCONFIGDATA_PATH = $(PYTHON3_PATH)/_sysconfigdata__linux_*.py
34PKG_PYTHON_SYSCONFIGDATA_NAME = `{ [ -e $(PKG_PYTHON_SYSCONFIGDATA_PATH) ] && basename $(PKG_PYTHON_SYSCONFIGDATA_PATH) .py; } || true`
35
36# Target python packages
37PKG_PYTHON_ENV = \
38	_PYTHON_HOST_PLATFORM="$(PKG_PYTHON_HOST_PLATFORM)" \
39	_PYTHON_PROJECT_BASE="$(PYTHON3_DIR)" \
40	_PYTHON_SYSCONFIGDATA_NAME="$(PKG_PYTHON_SYSCONFIGDATA_NAME)" \
41	PATH=$(BR_PATH) \
42	$(TARGET_CONFIGURE_OPTS) \
43	PYTHONPATH="$(PYTHON3_PATH)" \
44	PYTHONNOUSERSITE=1
45
46# Host python packages
47HOST_PKG_PYTHON_ENV = \
48	PATH=$(BR_PATH) \
49	PYTHONNOUSERSITE=1 \
50	$(HOST_CONFIGURE_OPTS)
51
52# Target pep517-based packages
53PKG_PYTHON_PEP517_ENV = \
54	$(PKG_PYTHON_ENV)
55
56PKG_PYTHON_PEP517_BUILD_CMD = \
57	-m build -n -w
58
59PKG_PYTHON_PEP517_INSTALL_OPTS = \
60	--interpreter=/usr/bin/python \
61	--script-kind=posix
62
63PKG_PYTHON_PEP517_INSTALL_TARGET_CMD = \
64	$(TOPDIR)/support/scripts/pyinstaller.py \
65	dist/* \
66	$(PKG_PYTHON_PEP517_INSTALL_OPTS) \
67	--purelib=$(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)/site-packages \
68	--headers=$(TARGET_DIR)/usr/include/python$(PYTHON3_VERSION_MAJOR) \
69	--scripts=$(TARGET_DIR)/usr/bin \
70	--data=$(TARGET_DIR)/usr
71
72PKG_PYTHON_PEP517_INSTALL_STAGING_CMD = \
73	$(TOPDIR)/support/scripts/pyinstaller.py \
74	dist/* \
75	$(PKG_PYTHON_PEP517_INSTALL_OPTS) \
76	--purelib=$(STAGING_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)/site-packages \
77	--headers=$(STAGING_DIR)/usr/include/python$(PYTHON3_VERSION_MAJOR) \
78	--scripts=$(STAGING_DIR)/usr/bin \
79	--data=$(STAGING_DIR)/usr
80
81PKG_PYTHON_PEP517_DEPENDENCIES = \
82	host-python-pypa-build \
83	host-python-installer
84
85# Host pep517-based packages
86HOST_PKG_PYTHON_PEP517_ENV = \
87	$(HOST_PKG_PYTHON_ENV)
88
89HOST_PKG_PYTHON_PEP517_BUILD_CMD = \
90	-m build -n -w
91
92HOST_PKG_PYTHON_PEP517_INSTALL_CMD = \
93	$(TOPDIR)/support/scripts/pyinstaller.py \
94	dist/* \
95	--interpreter=$(HOST_DIR)/bin/python \
96	--script-kind=posix \
97	--purelib=$(HOST_DIR)/lib/python$(PYTHON3_VERSION_MAJOR)/site-packages \
98	--headers=$(HOST_DIR)/include/python$(PYTHON3_VERSION_MAJOR) \
99	--scripts=$(HOST_DIR)/bin \
100	--data=$(HOST_DIR)
101
102# Target setuptools-based packages
103PKG_PYTHON_SETUPTOOLS_ENV = \
104	$(PKG_PYTHON_PEP517_ENV)
105
106PKG_PYTHON_SETUPTOOLS_BUILD_CMD = \
107	$(PKG_PYTHON_PEP517_BUILD_CMD)
108
109PKG_PYTHON_SETUPTOOLS_INSTALL_OPTS = \
110	--install-headers=/usr/include/python$(PYTHON3_VERSION_MAJOR) \
111	--prefix=/usr \
112	--executable=/usr/bin/python \
113	--single-version-externally-managed
114
115PKG_PYTHON_SETUPTOOLS_INSTALL_TARGET_CMD = \
116	$(PKG_PYTHON_PEP517_INSTALL_TARGET_CMD)
117
118PKG_PYTHON_SETUPTOOLS_INSTALL_STAGING_CMD = \
119	$(PKG_PYTHON_PEP517_INSTALL_STAGING_CMD)
120
121PKG_PYTHON_SETUPTOOLS_DEPENDENCIES = \
122	$(PKG_PYTHON_PEP517_DEPENDENCIES) \
123	host-python-setuptools
124
125# Host setuptools-based packages
126HOST_PKG_PYTHON_SETUPTOOLS_ENV = \
127	$(HOST_PKG_PYTHON_PEP517_ENV)
128
129HOST_PKG_PYTHON_SETUPTOOLS_BUILD_CMD = \
130	$(HOST_PKG_PYTHON_PEP517_BUILD_CMD)
131
132HOST_PKG_PYTHON_SETUPTOOLS_INSTALL_CMD = \
133	$(HOST_PKG_PYTHON_PEP517_INSTALL_CMD)
134
135# Target setuptools-rust-based packages
136PKG_PYTHON_SETUPTOOLS_RUST_ENV = \
137	$(PKG_PYTHON_SETUPTOOLS_ENV) \
138	$(PKG_CARGO_ENV) \
139	PYO3_CROSS_LIB_DIR="$(STAGING_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)"
140
141PKG_PYTHON_SETUPTOOLS_RUST_BUILD_CMD = \
142	$(PKG_PYTHON_SETUPTOOLS_BUILD_CMD)
143
144PKG_PYTHON_SETUPTOOLS_RUST_INSTALL_TARGET_CMD = \
145	$(PKG_PYTHON_SETUPTOOLS_INSTALL_TARGET_CMD)
146
147PKG_PYTHON_SETUPTOOLS_RUST_INSTALL_STAGING_CMD = \
148	$(PKG_PYTHON_SETUPTOOLS_INSTALL_STAGING_CMD)
149
150PKG_PYTHON_SETUPTOOLS_RUST_DEPENDENCIES = \
151	$(PKG_PYTHON_SETUPTOOLS_DEPENDENCIES) \
152	host-python-setuptools-rust
153
154# Host setuptools-rust-based packages
155HOST_PKG_PYTHON_SETUPTOOLS_RUST_ENV = \
156	$(HOST_PKG_PYTHON_SETUPTOOLS_ENV) \
157	$(HOST_PKG_CARGO_ENV) \
158	PYO3_CROSS_LIB_DIR="$(HOST_DIR)/lib/python$(PYTHON3_VERSION_MAJOR)"
159
160HOST_PKG_PYTHON_SETUPTOOLS_RUST_BUILD_CMD = \
161	$(HOST_PKG_PYTHON_SETUPTOOLS_BUILD_CMD)
162
163HOST_PKG_PYTHON_SETUPTOOLS_RUST_INSTALL_CMD = \
164	$(HOST_PKG_PYTHON_SETUPTOOLS_INSTALL_CMD)
165
166# Target flit packages
167PKG_PYTHON_FLIT_ENV = \
168	$(PKG_PYTHON_PEP517_ENV)
169
170PKG_PYTHON_FLIT_BUILD_CMD = \
171	$(PKG_PYTHON_PEP517_BUILD_CMD)
172
173PKG_PYTHON_FLIT_INSTALL_TARGET_CMD = \
174	$(PKG_PYTHON_PEP517_INSTALL_TARGET_CMD)
175
176PKG_PYTHON_FLIT_INSTALL_STAGING_CMD = \
177	$(PKG_PYTHON_PEP517_INSTALL_STAGING_CMD)
178
179PKG_PYTHON_FLIT_DEPENDENCIES = \
180	$(PKG_PYTHON_PEP517_DEPENDENCIES) \
181	host-python-flit-core
182
183# Host flit packages
184HOST_PKG_PYTHON_FLIT_ENV = \
185	$(HOST_PKG_PYTHON_PEP517_ENV)
186
187HOST_PKG_PYTHON_FLIT_BUILD_CMD = \
188	$(HOST_PKG_PYTHON_PEP517_BUILD_CMD)
189
190HOST_PKG_PYTHON_FLIT_INSTALL_CMD = \
191	$(HOST_PKG_PYTHON_PEP517_INSTALL_CMD)
192
193# Host flit-bootstrap packages
194HOST_PKG_PYTHON_FLIT_BOOTSTRAP_ENV = \
195	$(HOST_PKG_PYTHON_PEP517_ENV)
196
197HOST_PKG_PYTHON_FLIT_BOOTSTRAP_BUILD_CMD = \
198	-m flit_core.wheel
199
200HOST_PKG_PYTHON_FLIT_BOOTSTRAP_INSTALL_CMD = \
201	$(HOST_PKG_PYTHON_PEP517_INSTALL_CMD)
202
203# Target maturin packages
204PKG_PYTHON_MATURIN_ENV = \
205	$(PKG_PYTHON_PEP517_ENV) \
206	$(PKG_CARGO_ENV) \
207	PYO3_CROSS_LIB_DIR="$(STAGING_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)"
208
209PKG_PYTHON_MATURIN_BUILD_CMD = \
210	$(PKG_PYTHON_PEP517_BUILD_CMD)
211
212PKG_PYTHON_MATURIN_INSTALL_TARGET_CMD = \
213	$(PKG_PYTHON_PEP517_INSTALL_TARGET_CMD)
214
215PKG_PYTHON_MATURIN_INSTALL_STAGING_CMD = \
216	$(PKG_PYTHON_PEP517_INSTALL_STAGING_CMD)
217
218PKG_PYTHON_MATURIN_DEPENDENCIES = \
219	$(PKG_PYTHON_PEP517_DEPENDENCIES) \
220	host-python-maturin
221
222# Host maturin packages
223HOST_PKG_PYTHON_MATURIN_ENV = \
224	$(HOST_PKG_PYTHON_PEP517_ENV) \
225	$(HOST_PKG_CARGO_ENV) \
226	PYO3_CROSS_LIB_DIR="$(HOST_DIR)/lib/python$(PYTHON3_VERSION_MAJOR)"
227
228HOST_PKG_PYTHON_MATURIN_BUILD_CMD = \
229	$(HOST_PKG_PYTHON_PEP517_BUILD_CMD)
230
231HOST_PKG_PYTHON_MATURIN_INSTALL_CMD = \
232	$(HOST_PKG_PYTHON_PEP517_INSTALL_CMD)
233
234################################################################################
235# inner-python-package -- defines how the configuration, compilation
236# and installation of a Python package should be done, implements a
237# few hooks to tune the build process and calls the generic package
238# infrastructure to generate the necessary make targets
239#
240#  argument 1 is the lowercase package name
241#  argument 2 is the uppercase package name, including a HOST_ prefix
242#             for host packages
243#  argument 3 is the uppercase package name, without the HOST_ prefix
244#             for host packages
245#  argument 4 is the type (target or host)
246################################################################################
247
248define inner-python-package
249
250ifndef $(2)_SETUP_TYPE
251 ifdef $(3)_SETUP_TYPE
252  $(2)_SETUP_TYPE = $$($(3)_SETUP_TYPE)
253 else
254  $$(error "$(2)_SETUP_TYPE must be set")
255 endif
256endif
257
258$(2)_SETUP_TYPE_UPPER = $$(call UPPERCASE,$$($(2)_SETUP_TYPE))
259
260ifneq ($$(filter-out setuptools setuptools-rust pep517 flit flit-bootstrap maturin,$$($(2)_SETUP_TYPE)),)
261$$(error "Invalid $(2)_SETUP_TYPE. Valid options are 'maturin', 'setuptools', 'setuptools-rust', 'pep517' or 'flit'.")
262endif
263ifeq ($(4)-$$($(2)_SETUP_TYPE),target-flit-bootstrap)
264$$(error flit-bootstrap setup type only supported for host packages)
265endif
266
267# We need to vendor the Cargo crates at download time for pyo3 based
268# packages.
269#
270ifneq ($$(filter maturin setuptools-rust,$$($(2)_SETUP_TYPE)),)
271ifeq ($(4),target)
272$(2)_DL_ENV = $$(PKG_CARGO_ENV)
273else
274$(2)_DL_ENV = $$(HOST_PKG_CARGO_ENV)
275endif
276ifndef $(2)_CARGO_MANIFEST_PATH
277ifdef $(3)_CARGO_MANIFEST_PATH
278$(2)_DL_ENV += BR_CARGO_MANIFEST_PATH=$$($(3)_CARGO_MANIFEST_PATH)
279else
280ifneq ($$($(2)_SUBDIR),)
281$(2)_DL_ENV += BR_CARGO_MANIFEST_PATH=$$($(2)_SUBDIR)/Cargo.toml
282endif
283endif
284else
285$(2)_DL_ENV += BR_CARGO_MANIFEST_PATH=$$($(2)_CARGO_MANIFEST_PATH)
286endif
287endif
288
289# Target packages need both the python interpreter on the target (for
290# runtime) and the python interpreter on the host (for
291# compilation). However, host packages only need the python
292# interpreter on the host.
293#
294ifeq ($(4),target)
295$(2)_DEPENDENCIES += host-python3 python3
296else
297$(2)_DEPENDENCIES += host-python3
298endif # ($(4),target)
299
300# Setup type specific dependencies are the same whether we are
301# building for the host or the target.
302#
303ifeq ($$($(2)_SETUP_TYPE),flit-bootstrap)
304# Don't add dependency on host-python-installer for
305# host-python-installer itself, and its dependencies.
306ifeq ($$(filter host-python-flit-core host-python-installer,$(1)),)
307$(2)_DEPENDENCIES += host-python-installer
308endif
309else
310$(2)_DEPENDENCIES += $$(PKG_PYTHON_$$($(2)_SETUP_TYPE_UPPER)_DEPENDENCIES)
311endif
312
313# Pyo3 based packages(setuptools-rust and maturin) will need rust
314# toolchain dependencies for the host Python interpreter (both host
315# and target).
316#
317ifneq ($$(filter maturin setuptools-rust,$$($(2)_SETUP_TYPE)),)
318$(2)_DEPENDENCIES += host-rustc
319$(2)_DOWNLOAD_POST_PROCESS = cargo
320$(2)_DOWNLOAD_DEPENDENCIES = host-rustc
321endif # SETUP_TYPE
322
323ifeq ($(4),target)
324#
325# Build step. Only define it if not already defined by the package .mk
326# file.
327#
328ifndef $(2)_BUILD_CMDS
329define $(2)_BUILD_CMDS
330	(cd $$($$(PKG)_BUILDDIR)/; \
331		$$(PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_ENV) \
332		$$($$(PKG)_ENV) \
333		$$(HOST_DIR)/bin/python3 \
334		$$(PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_BUILD_CMD) \
335		$$($$(PKG)_BUILD_OPTS))
336endef
337endif
338
339#
340# Target installation step. Only define it if not already defined by
341# the package .mk file.
342#
343ifndef $(2)_INSTALL_TARGET_CMDS
344define $(2)_INSTALL_TARGET_CMDS
345	(cd $$($$(PKG)_BUILDDIR)/; \
346		$$(PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_ENV) \
347		$$($$(PKG)_ENV) \
348		$$(HOST_DIR)/bin/python3 \
349		$$(PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_INSTALL_TARGET_CMD) \
350		$$($$(PKG)_INSTALL_TARGET_OPTS))
351endef
352endif
353
354#
355# Staging installation step. Only define it if not already defined by
356# the package .mk file.
357#
358ifndef $(2)_INSTALL_STAGING_CMDS
359define $(2)_INSTALL_STAGING_CMDS
360	(cd $$($$(PKG)_BUILDDIR)/; \
361		$$(PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_ENV) \
362		$$($$(PKG)_ENV) \
363		$$(HOST_DIR)/bin/python3 \
364		$$(PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_INSTALL_STAGING_CMD) \
365		$$($$(PKG)_INSTALL_STAGING_OPTS))
366endef
367endif
368
369else # host
370
371#
372# Host build step. Only define it if not already defined by the package .mk
373# file.
374#
375ifndef $(2)_BUILD_CMDS
376define $(2)_BUILD_CMDS
377	(cd $$($$(PKG)_BUILDDIR)/; \
378		$$(HOST_PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_ENV) \
379		$$($$(PKG)_ENV) \
380		$$(HOST_DIR)/bin/python3 \
381		$$(HOST_PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_BUILD_CMD) \
382		$$($$(PKG)_BUILD_OPTS))
383endef
384endif
385
386#
387# Host installation step. Only define it if not already defined by the
388# package .mk file.
389#
390ifndef $(2)_INSTALL_CMDS
391define $(2)_INSTALL_CMDS
392	(cd $$($$(PKG)_BUILDDIR)/; \
393		$$(HOST_PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_ENV) \
394		$$($$(PKG)_ENV) \
395		$$(HOST_DIR)/bin/python3 \
396		$$(HOST_PKG_PYTHON_$$($$(PKG)_SETUP_TYPE_UPPER)_INSTALL_CMD) \
397		$$($$(PKG)_INSTALL_OPTS))
398endef
399endif
400
401endif # host / target
402
403# Call the generic package infrastructure to generate the necessary
404# make targets
405$(call inner-generic-package,$(1),$(2),$(3),$(4))
406
407endef
408
409################################################################################
410# python-package -- the target generator macro for Python packages
411################################################################################
412
413python-package = $(call inner-python-package,$(pkgname),$(call UPPERCASE,$(pkgname)),$(call UPPERCASE,$(pkgname)),target)
414host-python-package = $(call inner-python-package,host-$(pkgname),$(call UPPERCASE,host-$(pkgname)),$(call UPPERCASE,$(pkgname)),host)
415