summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta/classes-recipe/image.bbclass2
-rw-r--r--meta/classes-recipe/rootfs-postcommands.bbclass24
-rw-r--r--scripts/contrib/pycompile.py91
3 files changed, 116 insertions, 1 deletions
diff --git a/meta/classes-recipe/image.bbclass b/meta/classes-recipe/image.bbclass
index e387645503a..f029e082c50 100644
--- a/meta/classes-recipe/image.bbclass
+++ b/meta/classes-recipe/image.bbclass
@@ -39,7 +39,7 @@ INHIBIT_DEFAULT_DEPS = "1"
# IMAGE_FEATURES may contain any available package group
IMAGE_FEATURES ?= ""
IMAGE_FEATURES[type] = "list"
-IMAGE_FEATURES[validitems] += "debug-tweaks read-only-rootfs read-only-rootfs-delayed-postinsts stateless-rootfs empty-root-password allow-empty-password allow-root-login serial-autologin-root post-install-logging overlayfs-etc"
+IMAGE_FEATURES[validitems] += "debug-tweaks read-only-rootfs read-only-rootfs-delayed-postinsts stateless-rootfs empty-root-password allow-empty-password allow-root-login serial-autologin-root post-install-logging overlayfs-etc pyc-only"
# Generate companion debugfs?
IMAGE_GEN_DEBUGFS ?= "0"
diff --git a/meta/classes-recipe/rootfs-postcommands.bbclass b/meta/classes-recipe/rootfs-postcommands.bbclass
index 690fa976aae..3b4d219417f 100644
--- a/meta/classes-recipe/rootfs-postcommands.bbclass
+++ b/meta/classes-recipe/rootfs-postcommands.bbclass
@@ -25,6 +25,9 @@ ROOTFS_POSTPROCESS_COMMAND += "rootfs_update_timestamp; "
# Tweak files in /etc if read-only-rootfs is enabled
ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("IMAGE_FEATURES", "read-only-rootfs", "read_only_rootfs_hook; ", "",d)}'
+# Leave only pyc if pyc-only is enabled
+ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("IMAGE_FEATURES", "pyc-only", "pyc_only_hook; ", "",d)}'
+
# We also need to do the same for the kernel boot parameters,
# otherwise kernel or initramfs end up mounting the rootfs read/write
# (the default) if supported by the underlying storage.
@@ -151,6 +154,27 @@ read_only_rootfs_hook () {
fi
}
+
+inherit python3native
+pyc_only_hook () {
+ # Create pyc files
+ nativepython3 \
+ ${COREBASE}/scripts/contrib/pycompile.py \
+ --verbose \
+ --strip-root ${IMAGE_ROOTFS} \
+ ${IMAGE_ROOTFS}${libdir}/${PYTHON_DIR}
+
+ # Remove py files
+ find ${IMAGE_ROOTFS}${libdir}/${PYTHON_DIR} -name '*.py' \
+ -print0 | \
+ xargs -0 --no-run-if-empty rm -f
+
+ # Remove pycache files
+ find ${IMAGE_ROOTFS}${libdir}/${PYTHON_DIR} -name '__pycache__' \
+ -print0 | \
+ xargs -0 --no-run-if-empty rm -rf
+}
+
#
# This function disallows empty root passwords
#
diff --git a/scripts/contrib/pycompile.py b/scripts/contrib/pycompile.py
new file mode 100644
index 00000000000..8774144a90b
--- /dev/null
+++ b/scripts/contrib/pycompile.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+
+"""
+Byte compile all .py files from provided directories. This script is an
+alternative implementation of compileall.compile_dir written with
+cross-compilation in mind.
+"""
+
+import argparse
+import os
+import py_compile
+import re
+import sys
+
+
+def compile_one(host_path, strip_root=None, verbose=False):
+ """
+ Compile a .py file into a .pyc file located next to it.
+
+ :arg host_path:
+ Absolute path to the file to compile on the host running the build.
+ :arg strip_root:
+ Prefix to remove from the original source paths encoded in compiled
+ files.
+ :arg verbose:
+ Print compiled file paths.
+ """
+ if os.path.islink(host_path) or not os.path.isfile(host_path):
+ return # only compile real files
+
+ if not re.match(r"^[_A-Za-z][_A-Za-z0-9]*\.py$",
+ os.path.basename(host_path)):
+ return # only compile "importable" python modules
+
+ if strip_root is not None:
+ # determine the runtime path of the file (i.e.: relative path to root
+ # dir prepended with "/").
+ runtime_path = os.path.join("/", os.path.relpath(host_path, strip_root))
+ else:
+ runtime_path = host_path
+
+ if verbose:
+ print(" PYC {}".format(runtime_path))
+
+ # will raise an error if the file cannot be compiled
+ py_compile.compile(host_path, cfile=host_path + "c",
+ dfile=runtime_path, doraise=True)
+
+
+def existing_dir_abs(arg):
+ """
+ argparse type callback that checks that argument is a directory and returns
+ its absolute path.
+ """
+ if not os.path.isdir(arg):
+ raise argparse.ArgumentTypeError('no such directory: {!r}'.format(arg))
+ return os.path.abspath(arg)
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("dirs", metavar="DIR", nargs="+", type=existing_dir_abs,
+ help="Directory to recursively scan and compile")
+ parser.add_argument("--strip-root", metavar="ROOT", type=existing_dir_abs,
+ help="""
+ Prefix to remove from the original source paths encoded
+ in compiled files
+ """)
+ parser.add_argument("--verbose", action="store_true",
+ help="Print compiled files")
+
+ args = parser.parse_args()
+
+ try:
+ for d in args.dirs:
+ if args.strip_root and ".." in os.path.relpath(d, args.strip_root):
+ parser.error("DIR: not inside ROOT dir: {!r}".format(d))
+ for parent, _, files in os.walk(d):
+ for f in files:
+ compile_one(os.path.join(parent, f), args.strip_root,
+ args.verbose)
+
+ except Exception as e:
+ print("error: {}".format(e))
+ return 1
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())