From c327850c3ee16984623715c35496907b87b3e5a6 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 12 Dec 2017 22:37:33 +0200 Subject: [PATCH] sdist_upip.py: Replacement for optimize_upip.py. Unlike optimize_upip, which instantiates a separate distutils/setuptools command to run after "sdist", sdist_upip overrides "sdist" implementation, so it simplifies publishing workflow (no need to run adhoc commands). Besides what optimize_upip did (post-porocessing to remove superfluous files and recompressing with gzip 4K dictionary), sdist_upip also adds pre-processing step of generation resource module (R.py) from resource files. So, now there's a single (and standard) command to generate a distrubution package for MicroPython. --- sdist_upip.py | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 sdist_upip.py diff --git a/sdist_upip.py b/sdist_upip.py new file mode 100644 index 00000000..137110ed --- /dev/null +++ b/sdist_upip.py @@ -0,0 +1,137 @@ +# +# This module overrides distutils (also compatible with setuptools) "sdist" +# command to perform pre- and post-processing as required for MicroPython's +# upip package manager. +# +# Preprocessing steps: +# * Creation of Python resource module (R.py) from each top-level package's +# resources. +# Postprocessing steps: +# * Removing metadata files not used by upip (this includes setup.py) +# * Recompressing gzip archive with 4K dictionary size so it can be +# installed even on low-heap targets. +# +import sys +import os +import zlib +from subprocess import Popen, PIPE +import glob +import tarfile +import re +import io + +from distutils.filelist import FileList +from distutils.command.sdist import sdist as _sdist + + +def gzip_4k(inf, fname): + comp = zlib.compressobj(level=9, wbits=16 + 12) + with open(fname + ".out", "wb") as outf: + while 1: + data = inf.read(1024) + if not data: + break + outf.write(comp.compress(data)) + outf.write(comp.flush()) + os.rename(fname, fname + ".orig") + os.rename(fname + ".out", fname) + + +FILTERS = [ + # include, exclude, repeat + (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), + (r".+\.py$", r"[^/]+$"), + (None, r".+\.egg-info/.+"), +] + + +outbuf = io.BytesIO() + +def filter_tar(name): + fin = tarfile.open(name, "r:gz") + fout = tarfile.open(fileobj=outbuf, mode="w") + for info in fin: +# print(info) + if not "/" in info.name: + continue + fname = info.name.split("/", 1)[1] + include = None + + for inc_re, exc_re in FILTERS: + if include is None and inc_re: + if re.match(inc_re, fname): + include = True + + if include is None and exc_re: + if re.match(exc_re, fname): + include = False + + if include is None: + include = True + + if include: + print("including:", fname) + else: + print("excluding:", fname) + continue + + farch = fin.extractfile(info) + fout.addfile(info, farch) + fout.close() + fin.close() + + +def make_resource_module(manifest_files): + resources = [] + # Any non-python file included in manifest is resource + for fname in manifest_files: + ext = fname.rsplit(".", 1)[1] + if ext != "py": + resources.append(fname) + + if resources: + print("creating resource module R.py") + resources.sort() + last_pkg = None + r_file = None + for fname in resources: + pkg, res_name = fname.split("/", 1) + if last_pkg != pkg: + last_pkg = pkg + if r_file: + r_file.write("}\n") + r_file.close() + r_file = open(pkg + "/R.py", "w") + r_file.write("R = {\n") + + with open(fname, "rb") as f: + r_file.write("%r: %r,\n" % (res_name, f.read())) + + if r_file: + r_file.write("}\n") + r_file.close() + + +class sdist(_sdist): + + def run(self): + self.filelist = FileList() + self.get_file_list() + make_resource_module(self.filelist.files) + + r = super().run() + + assert len(self.archive_files) == 1 + print("filtering files and recompressing with 4K dictionary") + filter_tar(self.archive_files[0]) + outbuf.seek(0) + gzip_4k(outbuf, self.archive_files[0]) + + return r + + +# For testing only +if __name__ == "__main__": + filter_tar(sys.argv[1]) + outbuf.seek(0) + gzip_4k(outbuf, sys.argv[1])