Files
buildroot/package/python3/python3.mk
Robin Jarry 2e3986d44a support/scripts/pycompile: fix .pyc original source file paths
When generating a .pyc file, the original .py source file path is
encoded in it. It is used for various purposes: traceback generation,
.pyc file comparison with its .py source, and code inspection.

By default, the source path used when invoking compileall is encoded in
the .pyc file. Since we use paths relative to TARGET_DIR, we end up with
paths that are only valid when relative to '/' encoded in the installed
.pyc files on the target.

This breaks code inspection at runtime since the original source path
will be invalid unless the code is executed from '/'.

Unfortunately, compileall cannot be forced to use the proper path. It
was not written with cross-compilation usage in mind.

Rework the script to call py_compile.compile() directly with pertinent
options:

- The script now has a new --strip-root argument. This argument is
  optional but will always be specified when compiling py files in
  buildroot.
- All other (non-optional) arguments are folders in which all
  "importable" .py files will be compiled to .pyc.
- Using --strip-root=$(TARGET_DIR), the future runtime path of each .py
  file is computed and encoded into the compiled .pyc.

No need to change directory before running the script anymore.

The trickery used to handle error reporting was only applicable with
compileall. Since we implement our own "compileall", error reporting
becomes trivial.

Previously, we had a --force option to tell compileall.compiledir() to
forcibly recompile files if they had changed. Now, we would have to
handle it ourselves. It turns out to not be easy and would need us to
delve into the format of bytecompiled files to extract metadata and
compare it with the expected values, that being even dependent on the
python version being used (fortunately, only two for us: python 2.7 and
the latext 3.x).

Still, this is deemed too complex, and byte-compiling is pretty fast, so
much so that it should be eclipsed by the build duration anyway.

So we just drop support for --force, and instead we always byte-compile.

Signed-off-by: Julien Floret <julien.floret@6wind.com>
Signed-off-by: Robin Jarry <robin.jarry@6wind.com>
[yann.morin.1998@free.fr:
  - always byte-compile
  - drop --force
  - expand commit log to state so and explain why
]
Signed-off-by: Yann E. MORIN <yann.morin.1998@free.fr>
(cherry picked from commit c566f5206a)
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
2020-09-15 20:04:06 +02:00

318 lines
9.9 KiB
Makefile

################################################################################
#
# python3
#
################################################################################
PYTHON3_VERSION_MAJOR = 3.8
PYTHON3_VERSION = $(PYTHON3_VERSION_MAJOR).5
PYTHON3_SOURCE = Python-$(PYTHON3_VERSION).tar.xz
PYTHON3_SITE = https://python.org/ftp/python/$(PYTHON3_VERSION)
PYTHON3_LICENSE = Python-2.0, others
PYTHON3_LICENSE_FILES = LICENSE
# This host Python is installed in $(HOST_DIR), as it is needed when
# cross-compiling third-party Python modules.
HOST_PYTHON3_CONF_OPTS += \
--without-ensurepip \
--without-cxx-main \
--disable-sqlite3 \
--disable-tk \
--with-expat=system \
--disable-curses \
--disable-codecs-cjk \
--disable-nis \
--enable-unicodedata \
--disable-test-modules \
--disable-idle3 \
--disable-ossaudiodev
# Make sure that LD_LIBRARY_PATH overrides -rpath.
# This is needed because libpython may be installed at the same time that
# python is called.
# Make python believe we don't have 'hg', so that it doesn't try to
# communicate over the network during the build.
HOST_PYTHON3_CONF_ENV += \
LDFLAGS="$(HOST_LDFLAGS) -Wl,--enable-new-dtags" \
ac_cv_prog_HAS_HG=/bin/false
PYTHON3_DEPENDENCIES = host-python3 libffi
HOST_PYTHON3_DEPENDENCIES = host-expat host-zlib host-libffi
ifeq ($(BR2_PACKAGE_HOST_PYTHON3_SSL),y)
HOST_PYTHON3_DEPENDENCIES += host-openssl
else
HOST_PYTHON3_CONF_OPTS += --disable-openssl
endif
PYTHON3_INSTALL_STAGING = YES
ifeq ($(BR2_PACKAGE_PYTHON3_READLINE),y)
PYTHON3_DEPENDENCIES += readline
else
PYTHON3_CONF_OPTS += --disable-readline
endif
ifeq ($(BR2_PACKAGE_PYTHON3_CURSES),y)
PYTHON3_DEPENDENCIES += ncurses
else
PYTHON3_CONF_OPTS += --disable-curses
endif
ifeq ($(BR2_PACKAGE_PYTHON3_DECIMAL),y)
PYTHON3_DEPENDENCIES += mpdecimal
PYTHON3_CONF_OPTS += --with-libmpdec=system
else
PYTHON3_CONF_OPTS += --with-libmpdec=none
endif
ifeq ($(BR2_PACKAGE_PYTHON3_PYEXPAT),y)
PYTHON3_DEPENDENCIES += expat
PYTHON3_CONF_OPTS += --with-expat=system
else
PYTHON3_CONF_OPTS += --with-expat=none
endif
ifeq ($(BR2_PACKAGE_PYTHON3_SQLITE),y)
PYTHON3_DEPENDENCIES += sqlite
else
PYTHON3_CONF_OPTS += --disable-sqlite3
endif
ifeq ($(BR2_PACKAGE_PYTHON3_SSL),y)
PYTHON3_DEPENDENCIES += openssl
else
PYTHON3_CONF_OPTS += --disable-openssl
endif
ifneq ($(BR2_PACKAGE_PYTHON3_CODECSCJK),y)
PYTHON3_CONF_OPTS += --disable-codecs-cjk
endif
ifneq ($(BR2_PACKAGE_PYTHON3_UNICODEDATA),y)
PYTHON3_CONF_OPTS += --disable-unicodedata
endif
# Disable auto-detection of uuid.h (util-linux)
# which would add _uuid module support, instead
# default to the pure python implementation
PYTHON3_CONF_OPTS += --disable-uuid
ifeq ($(BR2_PACKAGE_PYTHON3_BZIP2),y)
PYTHON3_DEPENDENCIES += bzip2
else
PYTHON3_CONF_OPTS += --disable-bzip2
endif
ifeq ($(BR2_PACKAGE_PYTHON3_XZ),y)
PYTHON3_DEPENDENCIES += xz
else
PYTHON3_CONF_OPTS += --disable-xz
endif
ifeq ($(BR2_PACKAGE_PYTHON3_ZLIB),y)
PYTHON3_DEPENDENCIES += zlib
else
PYTHON3_CONF_OPTS += --disable-zlib
endif
ifeq ($(BR2_PACKAGE_PYTHON3_OSSAUDIODEV),y)
PYTHON3_CONF_OPTS += --enable-ossaudiodev
else
PYTHON3_CONF_OPTS += --disable-ossaudiodev
endif
# Make python believe we don't have 'hg', so that it doesn't try to
# communicate over the network during the build.
PYTHON3_CONF_ENV += \
ac_cv_have_long_long_format=yes \
ac_cv_file__dev_ptmx=yes \
ac_cv_file__dev_ptc=yes \
ac_cv_working_tzset=yes \
ac_cv_prog_HAS_HG=/bin/false
# GCC is always compliant with IEEE754
ifeq ($(BR2_ENDIAN),"LITTLE")
PYTHON3_CONF_ENV += ac_cv_little_endian_double=yes
else
PYTHON3_CONF_ENV += ac_cv_big_endian_double=yes
endif
# uClibc is known to have a broken wcsftime() implementation, so tell
# Python 3 to fall back to strftime() instead.
ifeq ($(BR2_TOOLCHAIN_USES_UCLIBC),y)
PYTHON3_CONF_ENV += ac_cv_func_wcsftime=no
endif
ifeq ($(BR2_PACKAGE_GETTEXT_PROVIDES_LIBINTL),y)
PYTHON3_DEPENDENCIES += gettext
endif
PYTHON3_CONF_OPTS += \
--without-ensurepip \
--without-cxx-main \
--with-system-ffi \
--disable-pydoc \
--disable-test-modules \
--disable-lib2to3 \
--disable-tk \
--disable-nis \
--disable-idle3 \
--disable-pyc-build
#
# Some of CPython's source code is generated using Python interpreter
# and some helper tools such as "Programs/_freeze_importlib" or
# "Parser/pgen" (look for regen-* targets in Makefile.pre.in for more
# info). Normally CPython codebase ships with those files
# pre-generated, so just regular "make" with no additional steps
# should be sufficient for a succesfull build, however due to
# Buildroot's "Add importlib fix for PEP 3147 issue" custom patch we
# end up modifying "Lib/importlib/_bootstrap_external.py" which means
# we have to do "regen-importlib" step before building CPython
# (Importlib is a builtin module that needs to be "frozen"/converted
# to a C array of bytecode using "Programs/_freeze_importlib")
#
# To achive that we add pre-build steps to host-python3 as well as
# python3 that execute "regen-importlib" target.
#
# Unfortunately, for the target Python, "Programs/_freeze_importlib"
# is built for the target, while we need to run them at build time. So
# when installing host-python3, we copy them to $(HOST_DIR)/bin...
#
define HOST_PYTHON3_MAKE_REGEN_IMPORTLIB
$(HOST_MAKE_ENV) $(PYTHON3_CONF_ENV) $(MAKE) $(HOST_CONFIGURE_OPTS) -C $(@D) regen-importlib
cp $(@D)/Programs/_freeze_importlib $(HOST_DIR)/bin/python-freeze-importlib
endef
HOST_PYTHON3_PRE_BUILD_HOOKS += HOST_PYTHON3_MAKE_REGEN_IMPORTLIB
#
# ... And then, when building the target python we first buid
# 'Programs/_freeze_importlib' to force GNU Make to update all of the
# prerequisites of 'Programs/_freeze_importlib', then copy our stashed
# "host-usable" version over the one that was just build and then
# build "regen-importlib" target
#
define PYTHON3_MAKE_REGEN_IMPORTLIB
$(TARGET_MAKE_ENV) $(PYTHON3_CONF_ENV) $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) Programs/_freeze_importlib
cp $(HOST_DIR)/bin/python-freeze-importlib $(@D)/Programs/_freeze_importlib
$(TARGET_MAKE_ENV) $(PYTHON3_CONF_ENV) $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) regen-importlib
endef
PYTHON3_PRE_BUILD_HOOKS += PYTHON3_MAKE_REGEN_IMPORTLIB
#
# Remove useless files. In the config/ directory, only the Makefile
# and the pyconfig.h files are needed at runtime.
#
define PYTHON3_REMOVE_USELESS_FILES
rm -f $(TARGET_DIR)/usr/bin/python$(PYTHON3_VERSION_MAJOR)-config
rm -f $(TARGET_DIR)/usr/bin/python$(PYTHON3_VERSION_MAJOR)m-config
rm -f $(TARGET_DIR)/usr/bin/python3-config
rm -f $(TARGET_DIR)/usr/bin/smtpd.py.3
for i in `find $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)/config-$(PYTHON3_VERSION_MAJOR)m-*/ \
-type f -not -name Makefile` ; do \
rm -f $$i ; \
done
rm -rf $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)/__pycache__/
rm -rf $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)/lib-dynload/sysconfigdata/__pycache__
rm -rf $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)/collections/__pycache__
rm -rf $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)/importlib/__pycache__
endef
PYTHON3_POST_INSTALL_TARGET_HOOKS += PYTHON3_REMOVE_USELESS_FILES
#
# Make sure libpython gets stripped out on target
#
define PYTHON3_ENSURE_LIBPYTHON_STRIPPED
chmod u+w $(TARGET_DIR)/usr/lib/libpython$(PYTHON3_VERSION_MAJOR)*.so
endef
PYTHON3_POST_INSTALL_TARGET_HOOKS += PYTHON3_ENSURE_LIBPYTHON_STRIPPED
PYTHON3_AUTORECONF = YES
define PYTHON3_INSTALL_SYMLINK
ln -fs python3 $(TARGET_DIR)/usr/bin/python
endef
ifneq ($(BR2_PACKAGE_PYTHON),y)
PYTHON3_POST_INSTALL_TARGET_HOOKS += PYTHON3_INSTALL_SYMLINK
endif
# Some packages may have build scripts requiring python3, whatever is the
# python version chosen for the target.
# Only install the python symlink in the host tree if python3 is enabled
# for the target.
ifeq ($(BR2_PACKAGE_PYTHON3),y)
define HOST_PYTHON3_INSTALL_SYMLINK
ln -fs python3 $(HOST_DIR)/bin/python
ln -fs python3-config $(HOST_DIR)/bin/python-config
endef
HOST_PYTHON3_POST_INSTALL_HOOKS += HOST_PYTHON3_INSTALL_SYMLINK
endif
# Provided to other packages
PYTHON3_PATH = $(STAGING_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)/
# Support for socket.AF_BLUETOOTH
ifeq ($(BR2_PACKAGE_BLUEZ5_UTILS_HEADERS),y)
PYTHON3_DEPENDENCIES += bluez5_utils-headers
endif
$(eval $(autotools-package))
$(eval $(host-autotools-package))
ifeq ($(BR2_REPRODUCIBLE),y)
define PYTHON3_FIX_TIME
find $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR) -name '*.py' -print0 | \
xargs -0 --no-run-if-empty touch -d @$(SOURCE_DATE_EPOCH)
endef
endif
define PYTHON3_CREATE_PYC_FILES
$(PYTHON3_FIX_TIME)
PYTHONPATH="$(PYTHON3_PATH)" \
$(HOST_DIR)/bin/python$(PYTHON3_VERSION_MAJOR) \
$(TOPDIR)/support/scripts/pycompile.py \
--strip-root $(TARGET_DIR) \
$(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR)
endef
ifeq ($(BR2_PACKAGE_PYTHON3_PYC_ONLY)$(BR2_PACKAGE_PYTHON3_PY_PYC),y)
PYTHON3_TARGET_FINALIZE_HOOKS += PYTHON3_CREATE_PYC_FILES
endif
ifeq ($(BR2_PACKAGE_PYTHON3_PYC_ONLY),y)
define PYTHON3_REMOVE_PY_FILES
find $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR) -name '*.py' \
$(if $(strip $(KEEP_PYTHON_PY_FILES)),-not \( $(call finddirclauses,$(TARGET_DIR),$(KEEP_PYTHON_PY_FILES)) \) ) \
-print0 | \
xargs -0 --no-run-if-empty rm -f
endef
PYTHON3_TARGET_FINALIZE_HOOKS += PYTHON3_REMOVE_PY_FILES
endif
# Normally, *.pyc files should not have been compiled, but just in
# case, we make sure we remove all of them.
ifeq ($(BR2_PACKAGE_PYTHON3_PY_ONLY),y)
define PYTHON3_REMOVE_PYC_FILES
find $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR) -name '*.pyc' -print0 | \
xargs -0 --no-run-if-empty rm -f
endef
PYTHON3_TARGET_FINALIZE_HOOKS += PYTHON3_REMOVE_PYC_FILES
endif
# In all cases, we don't want to keep the optimized .opt-1.pyc and
# .opt-2.pyc files, since they can't work without their non-optimized
# variant.
define PYTHON3_REMOVE_OPTIMIZED_PYC_FILES
find $(TARGET_DIR)/usr/lib/python$(PYTHON3_VERSION_MAJOR) -name '*.opt-1.pyc' -print0 -o -name '*.opt-2.pyc' -print0 | \
xargs -0 --no-run-if-empty rm -f
endef
PYTHON3_TARGET_FINALIZE_HOOKS += PYTHON3_REMOVE_OPTIMIZED_PYC_FILES