Gentoo Archives: gentoo-dev

From: "Michał Górny" <mgorny@g.o>
To: gentoo-dev@l.g.o
Cc: James Le Cuirot <chewi@g.o>
Subject: Re: [gentoo-dev] [PATCH 04/12] python-utils-r1.eclass: Use wrapper scripts to fix cross-compiling
Date: Fri, 04 Jan 2019 15:53:58
Message-Id: 1546617226.1895.9.camel@gentoo.org
In Reply to: [gentoo-dev] [PATCH 04/12] python-utils-r1.eclass: Use wrapper scripts to fix cross-compiling by James Le Cuirot
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

Attachments

File name MIME type
signature.asc application/pgp-signature

Replies