diff --git a/direct/src/directtools/DirectManipulation.py b/direct/src/directtools/DirectManipulation.py index 631eb710cd..4007207516 100644 --- a/direct/src/directtools/DirectManipulation.py +++ b/direct/src/directtools/DirectManipulation.py @@ -1370,7 +1370,7 @@ class ObjectHandles(NodePath, DirectObject): self.setScalingFactor(1) def setScalingFactor(self, scaleFactor): - self.ohScalingFactor = self.ohScalingFactor * scaleFactor + self.ohScalingFactor = scaleFactor self.scalingNode.setScale(self.ohScalingFactor * self.directScalingFactor) def getScalingFactor(self): diff --git a/direct/src/dist/FreezeTool.py b/direct/src/dist/FreezeTool.py index bb06c69fe1..376940fbe0 100644 --- a/direct/src/dist/FreezeTool.py +++ b/direct/src/dist/FreezeTool.py @@ -81,6 +81,25 @@ defaultHiddenImports = { 'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'], } + +# These are modules that import other modules but shouldn't pick them up as +# dependencies (usually because they are optional). This prevents picking up +# unwanted dependencies. +ignoreImports = { + 'direct.showbase.PythonUtil': ['pstats', 'profile'], + + 'toml.encoder': ['numpy'], +} + +if sys.version_info >= (3, 8): + # importlib.metadata is a "provisional" module introduced in Python 3.8 that + # conditionally pulls in dependency-rich packages like "email" and "pep517" + # (the latter of which is a thirdparty package!) But it's only imported in + # one obscure corner, so we don't want to pull it in by default. + ignoreImports['importlib._bootstrap_external'] = ['importlib.metadata'] + ignoreImports['importlib.metadata'] = ['pep517'] + + # These are overrides for specific modules. overrideModules = { # Used by the warnings module, among others, to get line numbers. Since @@ -997,7 +1016,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]) @@ -1031,7 +1050,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) @@ -2499,6 +2518,11 @@ class PandaModuleFinder(modulefinder.ModuleFinder): if name in self.badmodules: self._add_badmodule(name, caller) return + + if level <= 0 and caller and caller.__name__ in ignoreImports: + if name in ignoreImports[caller.__name__]: + return + try: self.import_hook(name, caller, level=level) except ImportError as msg: @@ -2679,7 +2703,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 9da0a06cfa..87b8ff7fe5 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -978,6 +978,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)) diff --git a/direct/src/dist/installers.py b/direct/src/dist/installers.py index f1234d3ad1..d947d97237 100644 --- a/direct/src/dist/installers.py +++ b/direct/src/dist/installers.py @@ -4,6 +4,7 @@ import subprocess import sys import tarfile import zipfile +import struct import panda3d.core as p3d @@ -14,7 +15,8 @@ def create_zip(command, basename, build_dir): 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: @@ -28,16 +30,39 @@ def create_tarball(command, basename, build_dir, tar_compression): build_cmd = command.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("get_record_timestamp() && source_date_epoch != (time_t)-1) { + if (multifile->get_timestamp() > source_date_epoch) { + multifile->set_timestamp(source_date_epoch); + } + } + if (needs_repack) { if (!multifile->repack()) { cerr << "Failed to write " << multifile_name << ".\n"; @@ -533,6 +540,12 @@ kill_files(const vector_string ¶ms) { } } + if (multifile->get_record_timestamp() && source_date_epoch != (time_t)-1) { + if (multifile->get_timestamp() > source_date_epoch) { + multifile->set_timestamp(source_date_epoch); + } + } + bool okflag = true; if (multifile->needs_repack()) { @@ -779,6 +792,11 @@ main(int argc, char **argv) { } } + const char *source_date_epoch_str = getenv("SOURCE_DATE_EPOCH"); + if (source_date_epoch_str != nullptr && source_date_epoch_str[0] != 0) { + source_date_epoch = (time_t)strtoll(source_date_epoch_str, nullptr, 10); + } + extern char *optarg; extern int optind; static const char *optflags = "crutxkvz123456789Z:T:X:S:f:OC:ep:P:F:h"; diff --git a/panda/src/express/multifile.I b/panda/src/express/multifile.I index 128eb5297e..21462cc467 100644 --- a/panda/src/express/multifile.I +++ b/panda/src/express/multifile.I @@ -67,6 +67,17 @@ get_timestamp() const { return _timestamp; } +/** + * Changes the overall mudification timestamp of the multifile. Note that this + * will be reset to the current time every time you modify a subfile. + * Only set this if you know what you are doing! + */ +INLINE void Multifile:: +set_timestamp(time_t timestamp) { + _timestamp = timestamp; + _timestamp_dirty = true; +} + /** * Sets the flag indicating whether timestamps should be recorded within the * Multifile or not. The default is true, indicating the Multifile will diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h index 540bf434d2..99b5242127 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -59,6 +59,7 @@ PUBLISHED: INLINE bool needs_repack() const; INLINE time_t get_timestamp() const; + INLINE void set_timestamp(time_t timestamp); INLINE void set_record_timestamp(bool record_timestamp); INLINE bool get_record_timestamp() const; diff --git a/panda/src/mathutil/boundingSphere.cxx b/panda/src/mathutil/boundingSphere.cxx index 32e48adad9..beb6cb6309 100644 --- a/panda/src/mathutil/boundingSphere.cxx +++ b/panda/src/mathutil/boundingSphere.cxx @@ -112,6 +112,10 @@ xform(const LMatrix4 &mat) { // Transform the center _center = _center * mat; + + if (cinf(_radius)) { + set_infinite(); + } } } diff --git a/pandatool/src/progbase/programBase.cxx b/pandatool/src/progbase/programBase.cxx index fb6a4458ba..7486c4a942 100644 --- a/pandatool/src/progbase/programBase.cxx +++ b/pandatool/src/progbase/programBase.cxx @@ -201,14 +201,26 @@ write_man_page(std::ostream &out) { // Generate a date string for inclusion into the footer. char date_str[256]; date_str[0] = 0; - time_t current_time = time(nullptr); + time_t current_time; + tm *today = nullptr; - if (current_time != (time_t) -1) { - tm *today = localtime(¤t_time); - if (today == nullptr || 0 == strftime(date_str, 256, "%d %B %Y", today)) { - date_str[0] = 0; + // This variable overrides the time we write to the footer. + const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + if (source_date_epoch == nullptr || source_date_epoch[0] == 0 || + (current_time = (time_t)strtoll(source_date_epoch, nullptr, 10)) <= 0) { + current_time = time(nullptr); + if (current_time != (time_t)-1) { + today = localtime(¤t_time); } } + else { + // Format as UTC to avoid inconsistency being introduced due to timezones. + today = gmtime(¤t_time); + } + + if (today == nullptr || 0 == strftime(date_str, 256, "%d %B %Y", today)) { + date_str[0] = 0; + } out << " 1 \"" << date_str << "\" \"" << PandaSystem::get_version_string() << "\" Panda3D\n";