1 |
On Thu, 2019-01-03 at 21:39 +0000, James Le Cuirot wrote: |
2 |
> Python has little concept of cross-compiling but it turns out that |
3 |
> making it work isn't so hard after all. |
4 |
> |
5 |
> Platform-specific details are held within _sysconfigdata.py, |
6 |
> sysconfig.py, and various distutils files. If we simply symlink these |
7 |
> from SYSROOT into an empty directory and add that directory to |
8 |
> PYTHONPATH then we can utilise the build host's Python with the target |
9 |
> host's settings. |
10 |
> |
11 |
> The pkg-config files were already being symlinked in a similar manner |
12 |
> but now we source them from within SYSROOT. |
13 |
> |
14 |
> In order for PYTHONPATH to be respected, Python must be executed via |
15 |
> the wrapper, which was not the case before. We previously relied |
16 |
> solely on the PATH but now PYTHON must point to the wrapper rather |
17 |
> than the usual location under /usr/bin. However, we only do this when |
18 |
> SYSROOT or EPREFIX are effectively set to avoid unnecessary |
19 |
> complexity. This has required some rejigging in the way that PYTHON is |
20 |
> set but it should remain compatible with existing packages. |
21 |
> |
22 |
> The python_wrapper_setup function that handles all this has had its |
23 |
> arguments reordered because no one ever uses the path/workdir |
24 |
> argument, which makes specifying other arguments tedious. |
25 |
|
26 |
This really belongs in a separate patch. |
27 |
|
28 |
> |
29 |
> Some packages rely on python-config but luckily this is just a shell |
30 |
> script so it can be executed from within SYSROOT. This is bending the |
31 |
> rules of PMS slightly but Python leaves us with little choice. I also |
32 |
> wrote those rules so I'm allowed to bend them. ;) |
33 |
> |
34 |
> PYTHON_INCLUDEDIR, PYTHON_LIBPATH, and their associated functions are |
35 |
> generally used during src_configure or src_compile and, as such, they |
36 |
> now need to have SYSROOT prepended. |
37 |
> |
38 |
> python_doheader uses PYTHON_INCLUDEDIR to install new headers and |
39 |
> therefore needs the value without SYSROOT. It was already stripping |
40 |
> EPREFIX before so now it simply strips SYSROOT as well. Similar |
41 |
> instances of this can do likewise or call the functions with SYSROOT |
42 |
> unset to achieve the same effect. |
43 |
> |
44 |
> To make all this work, we are assuming that CPython is located at |
45 |
> /usr/$(get_libdir)/${EPYTHON}, which is admittedly a little circular |
46 |
> when we are querying Python for the right paths. I feel the reason |
47 |
> that python_export was rewritten to query these dynamically was less |
48 |
> because someone might install Python to some weird location and more |
49 |
> to avoid special handling for each of the different |
50 |
> implementations. And what of those other implementations? |
51 |
|
52 |
This is a wrong assumption. CPython 3.7 is in /usr/lib/python3.7. |
53 |
|
54 |
> |
55 |
> Being Java-based, Jython is installed under the platform-neutral |
56 |
> /usr/share and presumably has few other platform-specific |
57 |
> aspects. Enabling native extensions appears non-trivial and none of |
58 |
> our module packages have enabled support for it anyway. |
59 |
> |
60 |
> I think PyPy could potentially support cross-compiling but it |
61 |
> hardcodes the native extension filename suffix within its own binaries |
62 |
> with no way to override it. Perhaps we could patch this in somehow but |
63 |
> I'm afraid I don't care enough. |
64 |
> |
65 |
> Together with the following changes to distutils-r1.eclass, I have now |
66 |
> been able to cross-compile a large number of packages with native |
67 |
> Python extensions, most with no changes at all, and the rest with only |
68 |
> minor fixes. |
69 |
> |
70 |
> Closes: https://bugs.gentoo.org/503874 |
71 |
> Bug: https://bugs.gentoo.org/648652 |
72 |
> Signed-off-by: James Le Cuirot <chewi@g.o> |
73 |
> --- |
74 |
> eclass/python-utils-r1.eclass | 120 ++++++++++++++++++++++++++-------- |
75 |
> 1 file changed, 92 insertions(+), 28 deletions(-) |
76 |
> |
77 |
> diff --git a/eclass/python-utils-r1.eclass b/eclass/python-utils-r1.eclass |
78 |
> index 1a549f49f3f2..607af1b52f75 100644 |
79 |
> --- a/eclass/python-utils-r1.eclass |
80 |
> +++ b/eclass/python-utils-r1.eclass |
81 |
> @@ -211,9 +211,15 @@ _python_impl_matches() { |
82 |
> # |
83 |
> # distutils-r1: Set within any of the python sub-phase functions. |
84 |
> # |
85 |
> -# Example value: |
86 |
> +# If SYSROOT or EPREFIX are effectively set then this will point to an |
87 |
> +# automatically generated wrapper rather than the usual path under |
88 |
> +# /usr/bin in order to accommodate cross-compiling. We could do this all |
89 |
> +# the time but it would add unnecessary complexity. |
90 |
> +# |
91 |
> +# Example values: |
92 |
> # @CODE |
93 |
> # /usr/bin/python2.7 |
94 |
> +# /var/tmp/portage/dev-python/pyblake2-1.2.3/temp/python2.7/bin/python2.7 |
95 |
> # @CODE |
96 |
> |
97 |
> # @ECLASS-VARIABLE: EPYTHON |
98 |
> @@ -256,6 +262,10 @@ _python_impl_matches() { |
99 |
> # Set and exported on request using python_export(). |
100 |
> # Requires a proper build-time dependency on the Python implementation. |
101 |
> # |
102 |
> +# This is prepended with SYSROOT in order to accommodate |
103 |
> +# cross-compiling. You may need to strip SYSROOT and EPREFIX if using it |
104 |
> +# to install new headers. |
105 |
> +# |
106 |
> # Example value: |
107 |
> # @CODE |
108 |
> # /usr/include/python2.7 |
109 |
> @@ -270,6 +280,9 @@ _python_impl_matches() { |
110 |
> # Valid only for CPython. Requires a proper build-time dependency |
111 |
> # on the Python implementation. |
112 |
> # |
113 |
> +# This is prepended with SYSROOT in order to accommodate |
114 |
> +# cross-compiling. |
115 |
> +# |
116 |
> # Example value: |
117 |
> # @CODE |
118 |
> # /usr/lib64/libpython2.7.so |
119 |
> @@ -314,6 +327,10 @@ _python_impl_matches() { |
120 |
> # Valid only for CPython. Requires a proper build-time dependency |
121 |
> # on the Python implementation and on pkg-config. |
122 |
> # |
123 |
> +# This is prepended with SYSROOT in order to accommodate |
124 |
> +# cross-compiling. You generally should not execute files within SYSROOT |
125 |
> +# but python-config is always a shell script. |
126 |
> +# |
127 |
> # Example value: |
128 |
> # @CODE |
129 |
> # /usr/bin/python2.7-config |
130 |
> @@ -380,6 +397,10 @@ python_export() { |
131 |
> esac |
132 |
> debug-print "${FUNCNAME}: implementation: ${impl}" |
133 |
> |
134 |
> + # Many variables below need a PYTHON variable but we should not |
135 |
> + # export it unless explicitly requested so use _PYTHON instead. |
136 |
> + local _PYTHON |
137 |
> + |
138 |
> for var; do |
139 |
> case "${var}" in |
140 |
> EPYTHON) |
141 |
> @@ -387,21 +408,21 @@ python_export() { |
142 |
> debug-print "${FUNCNAME}: EPYTHON = ${EPYTHON}" |
143 |
> ;; |
144 |
> PYTHON) |
145 |
> - export PYTHON=${EPREFIX}/usr/bin/${impl} |
146 |
> + python_wrapper_setup ${impl} PYTHON |
147 |
> debug-print "${FUNCNAME}: PYTHON = ${PYTHON}" |
148 |
> ;; |
149 |
> PYTHON_SITEDIR) |
150 |
> - [[ -n ${PYTHON} ]] || die "PYTHON needs to be set for ${var} to be exported, or requested before it" |
151 |
> + python_wrapper_setup ${impl} _PYTHON |
152 |
> # sysconfig can't be used because: |
153 |
> # 1) pypy doesn't give site-packages but stdlib |
154 |
> # 2) jython gives paths with wrong case |
155 |
> - PYTHON_SITEDIR=$("${PYTHON}" -c 'import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())') || die |
156 |
> + PYTHON_SITEDIR=$("${_PYTHON}" -c 'import distutils.sysconfig; print(distutils.sysconfig.get_python_lib())') || die |
157 |
> export PYTHON_SITEDIR |
158 |
> debug-print "${FUNCNAME}: PYTHON_SITEDIR = ${PYTHON_SITEDIR}" |
159 |
> ;; |
160 |
> PYTHON_INCLUDEDIR) |
161 |
> - [[ -n ${PYTHON} ]] || die "PYTHON needs to be set for ${var} to be exported, or requested before it" |
162 |
> - PYTHON_INCLUDEDIR=$("${PYTHON}" -c 'import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') || die |
163 |
> + python_wrapper_setup ${impl} _PYTHON |
164 |
> + PYTHON_INCLUDEDIR=${SYSROOT}$("${_PYTHON}" -c 'import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())') || die |
165 |
> export PYTHON_INCLUDEDIR |
166 |
> debug-print "${FUNCNAME}: PYTHON_INCLUDEDIR = ${PYTHON_INCLUDEDIR}" |
167 |
> |
168 |
> @@ -411,8 +432,8 @@ python_export() { |
169 |
> fi |
170 |
> ;; |
171 |
> PYTHON_LIBPATH) |
172 |
> - [[ -n ${PYTHON} ]] || die "PYTHON needs to be set for ${var} to be exported, or requested before it" |
173 |
> - PYTHON_LIBPATH=$("${PYTHON}" -c 'import os.path, sysconfig; print(os.path.join(sysconfig.get_config_var("LIBDIR"), sysconfig.get_config_var("LDLIBRARY")) if sysconfig.get_config_var("LDLIBRARY") else "")') || die |
174 |
> + python_wrapper_setup ${impl} _PYTHON |
175 |
> + PYTHON_LIBPATH=${SYSROOT}$("${_PYTHON}" -c 'import os.path, sysconfig; print(os.path.join(sysconfig.get_config_var("LIBDIR"), sysconfig.get_config_var("LDLIBRARY")) if sysconfig.get_config_var("LDLIBRARY") else "")') || die |
176 |
> export PYTHON_LIBPATH |
177 |
> debug-print "${FUNCNAME}: PYTHON_LIBPATH = ${PYTHON_LIBPATH}" |
178 |
> |
179 |
> @@ -457,9 +478,9 @@ python_export() { |
180 |
> |
181 |
> case "${impl}" in |
182 |
> python*) |
183 |
> - [[ -n ${PYTHON} ]] || die "PYTHON needs to be set for ${var} to be exported, or requested before it" |
184 |
> - flags=$("${PYTHON}" -c 'import sysconfig; print(sysconfig.get_config_var("ABIFLAGS") or "")') || die |
185 |
> - val=${PYTHON}${flags}-config |
186 |
> + python_wrapper_setup ${impl} _PYTHON |
187 |
> + flags=$("${_PYTHON}" -c 'import sysconfig; print(sysconfig.get_config_var("ABIFLAGS") or "")') || die |
188 |
> + val=${ESYSROOT-${SYSROOT}${EPREFIX}}/usr/bin/${PYTHON##*/}${flags}-config |
189 |
> ;; |
190 |
> *) |
191 |
> die "${impl}: obtaining ${var} not supported" |
192 |
> @@ -954,7 +975,7 @@ python_doheader() { |
193 |
> local d PYTHON_INCLUDEDIR=${PYTHON_INCLUDEDIR} |
194 |
> [[ ${PYTHON_INCLUDEDIR} ]] || python_export PYTHON_INCLUDEDIR |
195 |
> |
196 |
> - d=${PYTHON_INCLUDEDIR#${EPREFIX}} |
197 |
> + d=${PYTHON_INCLUDEDIR#${ESYSROOT-${SYSROOT}${EPREFIX}}} |
198 |
> |
199 |
> ( |
200 |
> insopts -m 0644 |
201 |
> @@ -964,7 +985,7 @@ python_doheader() { |
202 |
> } |
203 |
> |
204 |
> # @FUNCTION: python_wrapper_setup |
205 |
> -# @USAGE: [<path> [<impl>]] |
206 |
> +# @USAGE: [<impl> [<pyvar> [<path>]]] |
207 |
> # @DESCRIPTION: |
208 |
> # Create proper 'python' executable and pkg-config wrappers |
209 |
> # (if available) in the directory named by <path>. Set up PATH |
210 |
> @@ -973,6 +994,9 @@ python_doheader() { |
211 |
> # The wrappers will be created for implementation named by <impl>, |
212 |
> # or for one named by ${EPYTHON} if no <impl> passed. |
213 |
> # |
214 |
> +# The path to the 'python' executable wrapper is exported to the |
215 |
> +# variable named <pyvar> if given. |
216 |
|
217 |
Why do you need that in the first place? The path should be rather |
218 |
predictable, shouldn't it? |
219 |
|
220 |
> +# |
221 |
> # If the named directory contains a python symlink already, it will |
222 |
> # be assumed to contain proper wrappers already and only environment |
223 |
> # setup will be done. If wrapper update is requested, the directory |
224 |
> @@ -980,25 +1004,41 @@ python_doheader() { |
225 |
> python_wrapper_setup() { |
226 |
> debug-print-function ${FUNCNAME} "${@}" |
227 |
> |
228 |
> - local impl=${2:-${EPYTHON}} |
229 |
> - local workdir=${1:-${T}/${impl}} |
230 |
> + local impl=${1:-${EPYTHON}} |
231 |
> + local pyvar=${2} |
232 |
> + local lpyvar=_${pyvar:-PYTHON} |
233 |
> + |
234 |
> + # Use separate directories for SYSROOT in case we need to execute |
235 |
> + # Python in the context of the build host by unsetting SYSROOT. |
236 |
> + local workdir=${3:-${T}/${impl}${SYSROOT:+-sysroot}} |
237 |
> |
238 |
> [[ ${workdir} ]] || die "${FUNCNAME}: no workdir specified." |
239 |
> [[ ${impl} ]] || die "${FUNCNAME}: no impl nor EPYTHON specified." |
240 |
> |
241 |
> + local EPYTHON |
242 |
> + python_export "${impl}" EPYTHON |
243 |
|
244 |
I'm getting dizzy because of the amount of circular dependencies between |
245 |
the two functions you've introduced. This is going to make it trivial |
246 |
to deadlock it when changing code later. |
247 |
|
248 |
> + |
249 |
> + # We could use BROOT here but using the PATH accommodates |
250 |
> + # cross-prefix where the PATH is sometimes manipulated to prefer |
251 |
> + # build tools from the target prefix (i.e. EPREFIX). |
252 |
> + # |
253 |
> + # Also make sure we don't pick up an existing wrapper by replacing |
254 |
> + # instances of ${T} with a bogus location. The workdir can be |
255 |
> + # overridden but hopefully it will be somewhere under ${T}. |
256 |
> + local ${lpyvar}=$(PATH=${PATH//${T}//dev/null} type -P "${EPYTHON}" || die "${FUNCNAME}: can't find ${EPYTHON} in PATH") |
257 |
|
258 |
This PATH substitution hack is horrible. |
259 |
|
260 |
> + |
261 |
> + local pysysroot=${ESYSROOT-${SYSROOT%/}${EPREFIX}} |
262 |
> + |
263 |
> if [[ ! -x ${workdir}/bin/python ]]; then |
264 |
> _python_check_dead_variables |
265 |
> |
266 |
> - mkdir -p "${workdir}"/{bin,pkgconfig} || die |
267 |
> + mkdir -p "${workdir}"/{bin,lib,pkgconfig} || die |
268 |
> |
269 |
> # Clean up, in case we were supposed to do a cheap update. |
270 |
> rm -f "${workdir}"/bin/python{,2,3}{,-config} || die |
271 |
> rm -f "${workdir}"/bin/2to3 || die |
272 |
> rm -f "${workdir}"/pkgconfig/python{,2,3}.pc || die |
273 |
> |
274 |
> - local EPYTHON PYTHON |
275 |
> - python_export "${impl}" EPYTHON PYTHON |
276 |
> - |
277 |
> local pyver pyother |
278 |
> if python_is_python3; then |
279 |
> pyver=3 |
280 |
> @@ -1012,37 +1052,53 @@ python_wrapper_setup() { |
281 |
> # note: we don't use symlinks because python likes to do some |
282 |
> # symlink reading magic that breaks stuff |
283 |
> # https://bugs.gentoo.org/show_bug.cgi?id=555752 |
284 |
> - cat > "${workdir}/bin/python" <<-_EOF_ || die |
285 |
> - #!/bin/sh |
286 |
> - exec "${PYTHON}" "\${@}" |
287 |
> - _EOF_ |
288 |
> - cp "${workdir}/bin/python" "${workdir}/bin/python${pyver}" || die |
289 |
> - chmod +x "${workdir}/bin/python" "${workdir}/bin/python${pyver}" || die |
290 |
> + echo '#!/bin/sh' > "${workdir}/bin/python" || die |
291 |
> local nonsupp=( "python${pyother}" "python${pyother}-config" ) |
292 |
> |
293 |
> # CPython-specific |
294 |
> if [[ ${EPYTHON} == python* ]]; then |
295 |
> + local pysysrootlib=${pysysroot}/usr/$(get_libdir) |
296 |
> + |
297 |
> cat > "${workdir}/bin/python-config" <<-_EOF_ || die |
298 |
> #!/bin/sh |
299 |
> - exec "${PYTHON}-config" "\${@}" |
300 |
> + exec "${pysysroot}/usr/bin/${EPYTHON}-config" "\${@}" |
301 |
> _EOF_ |
302 |
> cp "${workdir}/bin/python-config" \ |
303 |
> "${workdir}/bin/python${pyver}-config" || die |
304 |
> chmod +x "${workdir}/bin/python-config" \ |
305 |
> "${workdir}/bin/python${pyver}-config" || die |
306 |
> |
307 |
> + if [[ -n ${pysysroot} ]]; then |
308 |
> + # Use host-specific data from SYSROOT. Python 2 looks |
309 |
> + # for _sysconfigdata while Python 3 uses |
310 |
> + # _PYTHON_SYSCONFIGDATA_NAME. |
311 |
> + ln -s "${pysysrootlib}/${EPYTHON}"/_sysconfigdata*.py "${workdir}"/lib/_sysconfigdata.py || die |
312 |
> + |
313 |
> + # Use distutils/sysconfig from SYSROOT as parts of it |
314 |
> + # have GENTOO_LIBDIR baked in at Python build time. |
315 |
> + ln -s "${pysysrootlib}/${EPYTHON}"/{distutils,sysconfig.py} "${workdir}"/lib/ || die |
316 |
> + |
317 |
> + # Add env vars to python wrapper accordingly. |
318 |
> + echo "PYTHONPATH=\"${workdir}/lib\${PYTHONPATH:+:}\${PYTHONPATH}\" _PYTHON_SYSCONFIGDATA_NAME=_sysconfigdata \\" \ |
319 |
> + >> "${workdir}/bin/python" || die |
320 |
|
321 |
Use explicit 'export ...' multi-line thing for readability. |
322 |
|
323 |
> + fi |
324 |
> + |
325 |
> # Python 2.6+. |
326 |
> - ln -s "${PYTHON/python/2to3-}" "${workdir}"/bin/2to3 || die |
327 |
> + ln -s "${EPYTHON/python/2to3-}" "${workdir}"/bin/2to3 || die |
328 |
> |
329 |
> # Python 2.7+. |
330 |
> - ln -s "${EPREFIX}"/usr/$(get_libdir)/pkgconfig/${EPYTHON/n/n-}.pc \ |
331 |
> + ln -s "${pysysrootlib}"/pkgconfig/${EPYTHON/n/n-}.pc \ |
332 |
> "${workdir}"/pkgconfig/python.pc || die |
333 |
> ln -s python.pc "${workdir}"/pkgconfig/python${pyver}.pc || die |
334 |
> else |
335 |
> nonsupp+=( 2to3 python-config "python${pyver}-config" ) |
336 |
> fi |
337 |
> |
338 |
> + echo "exec \"${!lpyvar}\" \"\${@}\"" >> "${workdir}"/bin/python || die |
339 |
> + tee "${workdir}"/bin/{python${pyver},"${EPYTHON}"} < "${workdir}"/bin/python >/dev/null || die |
340 |
|
341 |
Whatever this is supposed to do, do it simpler. |
342 |
|
343 |
> + chmod +x "${workdir}"/bin/{python,python${pyver},"${EPYTHON}"} || die |
344 |
> + |
345 |
> local x |
346 |
> for x in "${nonsupp[@]}"; do |
347 |
> cat >"${workdir}"/bin/${x} <<-_EOF_ || die |
348 |
> @@ -1064,6 +1120,14 @@ python_wrapper_setup() { |
349 |
> PKG_CONFIG_PATH=${workdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}} |
350 |
> fi |
351 |
> export PATH PKG_CONFIG_PATH |
352 |
> + |
353 |
> + if [[ -n ${pyvar} ]]; then |
354 |
> + if [[ -n ${pysysroot} ]]; then |
355 |
> + export -- ${pyvar}=${workdir}/bin/${EPYTHON} |
356 |
> + else |
357 |
> + export -- ${pyvar}=${!lpyvar} |
358 |
> + fi |
359 |
> + fi |
360 |
|
361 |
This is just plain ugly. No. |
362 |
|
363 |
> } |
364 |
> |
365 |
> # @FUNCTION: python_is_python3 |
366 |
|
367 |
-- |
368 |
Best regards, |
369 |
Michał Górny |