From 68daa238b1a9a756eb4ae93d5eeab220619a8741 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 18 Jan 2021 17:55:55 +0100 Subject: [PATCH] dist: Add some determinism support to bdist_apps It's necessary to set PYTHONHASHSEED=0 as well as SOURCE_DATE_EPOCH for deterministic compilation, and moreover, the generated zip files do still have timestamps in them. --- direct/src/dist/FreezeTool.py | 6 +++--- direct/src/dist/commands.py | 30 ++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index 8cc07aaadf..66f638d7e7 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -958,7 +958,7 @@ class Freezer: # Scan the directory, looking for .py files. modules = [] - for basename in os.listdir(pathname): + for basename in sorted(os.listdir(pathname)): if basename.endswith('.py') and basename != '__init__.py': modules.append(basename[:-3]) @@ -992,7 +992,7 @@ class Freezer: modulePath = self.getModulePath(topName) if modulePath: for dirname in modulePath: - for basename in os.listdir(dirname): + for basename in sorted(os.listdir(dirname)): if os.path.exists(os.path.join(dirname, basename, '__init__.py')): parentName = '%s.%s' % (topName, basename) newParentName = '%s.%s' % (newTopName, basename) @@ -2587,7 +2587,7 @@ class PandaModuleFinder(modulefinder.ModuleFinder): except OSError: self.msg(2, "can't list directory", dir) continue - for name in names: + for name in sorted(names): mod = None for suff in self.suffixes: n = len(suff) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index 9d7b193cfd..633931d8fa 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -1045,6 +1045,7 @@ class build_apps(setuptools.Command): rootdir = os.getcwd() for dirname, subdirlist, filelist in os.walk(rootdir): + subdirlist.sort() dirpath = os.path.relpath(dirname, rootdir) if skip_directory(dirpath): self.announce('skipping directory {}'.format(dirpath)) @@ -1414,7 +1415,8 @@ class bdist_apps(setuptools.Command): zf.write(build_dir, base_dir) for dirpath, dirnames, filenames in os.walk(build_dir): - for name in sorted(dirnames): + dirnames.sort() + for name in dirnames: path = os.path.normpath(os.path.join(dirpath, name)) zf.write(path, path.replace(build_dir, base_dir, 1)) for name in filenames: @@ -1429,16 +1431,39 @@ class bdist_apps(setuptools.Command): build_cmd = self.get_finalized_command('build_apps') binary_names = list(build_cmd.console_apps.keys()) + list(build_cmd.gui_apps.keys()) + source_date = os.environ.get('SOURCE_DATE_EPOCH', '').strip() + if source_date: + max_mtime = int(source_date) + else: + max_mtime = None + def tarfilter(tarinfo): if tarinfo.isdir() or os.path.basename(tarinfo.name) in binary_names: tarinfo.mode = 0o755 else: tarinfo.mode = 0o644 + + # This isn't interesting information to retain for distribution. + tarinfo.uid = 0 + tarinfo.gid = 0 + tarinfo.uname = "" + tarinfo.gname = "" + + if max_mtime is not None and tarinfo.mtime >= max_mtime: + tarinfo.mtime = max_mtime + return tarinfo - with tarfile.open('{}.tar.{}'.format(basename, tar_compression), 'w|{}'.format(tar_compression)) as tf: + filename = '{}.tar.{}'.format(basename, tar_compression) + with tarfile.open(filename, 'w|{}'.format(tar_compression)) as tf: tf.add(build_dir, base_dir, filter=tarfilter) + if tar_compression == 'gz' and max_mtime is not None: + # Python provides no elegant way to overwrite the gzip timestamp. + with open(filename, 'r+b') as fp: + fp.seek(4) + fp.write(struct.pack("