[reprotest] 02/03: Fix the time variation to actually make the time constant

Ximin Luo infinity0 at debian.org
Mon Nov 27 12:03:43 UTC 2017


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository reprotest.

commit acff508a633305a3f36f6d187d3a48ce38b4e578
Author: Ximin Luo <infinity0 at debian.org>
Date:   Mon Nov 27 13:01:42 2017 +0100

    Fix the time variation to actually make the time constant
---
 README.rst            | 15 ++++++------
 debian/changelog      |  2 ++
 reprotest/__init__.py | 10 +++++---
 reprotest/build.py    | 68 +++++++++++++++++++++++++--------------------------
 4 files changed, 49 insertions(+), 46 deletions(-)

diff --git a/README.rst b/README.rst
index 2bdac3b..d428814 100644
--- a/README.rst
+++ b/README.rst
@@ -247,14 +247,13 @@ user_group.available
     a $user, with no $group variation.
 time.faketimes
     A semicolon-separated ordered set, specifying possible ``faketime(1)`` time
-    descriptors to use. Default is empty.
-time.auto_faketimes
-    A semicolon-separated ordered set, specifying a list of "magic" values
-    which will be resolved into additional values for time.faketimes. Default
-    is "SOURCE_DATE_EPOCH", possible values are:
-
-    SOURCE_DATE_EPOCH
-        Use the latest file modification time found in the source_root.
+    descriptors to use. Default is empty, in which case we randomly choose a
+    time: either now (if the latest file-modtime in ``source_root`` is older
+    than about half-a-year) or more than half-a-year in the future.
+
+    Note that the clock continues to run during the build. It is possible for
+    ``faketime(1)`` to freeze it, but we don't yet support that yet; it has a
+    higher chance of causing your build to fail or misbehave.
 
 The difference between --vary and --variations is that the former appends onto
 previous values but the latter resets them. Furthermore, the last value set for
diff --git a/debian/changelog b/debian/changelog
index 962139e..408603e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -4,6 +4,8 @@ reprotest (0.7.4) UNRELEASED; urgency=medium
   * Add aslr, domain_host, and num_cpu variations.
   * Add a --print-sudoers feature.
   * Properly drop privs when running the build. (Closes: #877813)
+  * Fix the time variation to actually make the time constant. This drops the
+    time.auto_faketimes variation-spec option.
 
  -- Ximin Luo <infinity0 at debian.org>  Fri, 20 Oct 2017 12:33:21 +0200
 
diff --git a/reprotest/__init__.py b/reprotest/__init__.py
index 0065e18..f88ce8d 100644
--- a/reprotest/__init__.py
+++ b/reprotest/__init__.py
@@ -314,7 +314,6 @@ class TestArgs(collections.namedtuple('_Test',
                         raise ValueError("already built '%s'" % name)
                     names_seen.add(name)
 
-                    var = var.replace.spec.apply_dynamic_defaults(source_root)
                     bctx = BuildContext(testbed.scratch, result_dir, source_root, name, var)
 
                     build = bctx.make_build_commands(build_command, os.environ)
@@ -772,7 +771,12 @@ def run(argv, dry_run=None):
     if parsed_args.min_cpus is None:
         logger.warn("The control build runs on 1 CPU by default, give --min-cpus to increase this.")
     min_cpus = parsed_args.min_cpus or 1
-    build_variations = Variations.of(*specs, verbosity=verbosity, min_cpus=min_cpus)
+    build_variations = Variations.of(
+        *specs,
+        verbosity=verbosity,
+        min_cpus=min_cpus,
+        # TODO: make this configurable via command line
+        base_faketime='@%d' % build.auto_source_date_epoch(source_root))
 
     # Warn about missing programs
     if virtual_server_args[0] == "null" and not dry_run:
@@ -819,6 +823,6 @@ def main():
     r = run(sys.argv[1:])
     if not isinstance(r, int):
         import pprint
-        pprint.pprint(r, width=40, compact=True)
+        pprint.pprint(r, width=80, compact=True)
     else:
         return r
diff --git a/reprotest/build.py b/reprotest/build.py
index e260af1..30af6d9 100644
--- a/reprotest/build.py
+++ b/reprotest/build.py
@@ -356,20 +356,27 @@ def timezone(ctx, build, vary):
 @tool_required("faketime")
 def faketime(ctx, build, vary):
     if not vary:
-        # FIXME: this does not actually fix the time, it just lets the system clock run normally
-        return build
-    lastmt = random.choice(ctx.spec.time.faketimes)
-    now = time.time()
-    # FIXME: better way of choosing which faketime to use
-    if lastmt.startswith("@") and int(lastmt[1:]) < now - 32253180:
-        # if lastmt is far in the past, use that, it's a bit safer
-        faket = lastmt
+        # fix the time at base_faketime
+        faket = ctx.base_faketime
+    elif ctx.spec.time.faketimes:
+        faket = random.choice(ctx.spec.time.faketimes)
     else:
-        # otherwise use a date far in the future
-        faket = '+373days+7hours+13minutes'
+        now = time.time()
+        base = int(ctx.base_faketime[1:]) if ctx.base_faketime.startswith("@") else now
+        # 15552000 = 180 days
+        if base < now - 15552000 and not random.randint(0, 1):
+            # if ctx.base_faketime is far in the past, with 1/2 probability
+            # reuse the current time and don't fake it
+            return build
+        else:
+            # otherwise use a date far in the future
+            faket = '+%sdays+%shours+%sminutes' % (
+                random.randint(180, 540), random.randint(0, 23), random.randint(0, 59))
+
     # faketime's manpages are stupidly misleading; it also modifies file timestamps.
     # this is only mentioned in the README. we do not want this, it really really
     # messes with GNU make and other buildsystems that look at timestamps.
+    # set NO_FAKE_STAT=1 avoids this.
     return build.add_env('NO_FAKE_STAT', '1').prepend_to_build_command('faketime', faket)
 
 def umask(ctx, build, vary):
@@ -459,6 +466,7 @@ VARIATIONS = collections.OrderedDict([
                     # but also as close to the build command as possible, (i.e. earlier in this list)
                     # otherwise other variations below can affect the address layout
     ('num_cpus', num_cpus),
+    ('time', faketime), # needs to go before sudo (user_group), closer to the build command
     ('user_group', user_group),
     ('fileordering', fileordering),
     ('domain_host', domain_host), # needs to run after all other mounts have been set
@@ -467,12 +475,22 @@ VARIATIONS = collections.OrderedDict([
     # ('namespace', namespace),
     ('exec_path', exec_path),
     # ('shell', shell),
-    ('time', faketime),
     ('timezone', timezone),
     ('umask', umask),
 ])
 
 
+def auto_source_date_epoch(source_root):
+    # Get the latest modification date of all the files in the source root.
+    # This tries hard to avoid bad interactions with faketime and make(1) etc.
+    # However if you're building this too soon after changing one of the source
+    # files then the effect of this variation is not very great.
+    filemtimes = (os.lstat(os.path.join(root, f)).st_mtime
+                  for root, dirs, files in os.walk(source_root)
+                  for f in files)
+    return int(max(filemtimes, default=1))
+
+
 class TimeVariation(collections.namedtuple('_TimeVariation', 'faketimes auto_faketimes')):
     @classmethod
     def default(cls):
@@ -482,20 +500,6 @@ class TimeVariation(collections.namedtuple('_TimeVariation', 'faketimes auto_fak
     def empty(cls):
         return cls(mdiffconf.strlist_set(";"), mdiffconf.strlist_set(";"))
 
-    def apply_dynamic_defaults(self, source_root):
-        new_faketimes = []
-        for a in self.auto_faketimes:
-            if a == "SOURCE_DATE_EPOCH":
-                # Get the latest modification date of all the files in the source root.
-                # This tries hard to avoid bad interactions with faketime and make(1) etc.
-                # However if you're building this too soon after changing one of the source
-                # files then the effect of this variation is not very great.
-                filemtimes = (os.lstat(os.path.join(root, f)).st_mtime for root, dirs, files in os.walk(source_root) for f in files)
-                new_faketimes.append("@%d" % int(max(filemtimes, default=0)))
-            else:
-                raise ValueError("unrecognized auto_faketime: %s" % a)
-        return self.empty()._replace(faketimes=self.faketimes + new_faketimes)
-
 
 class EnvironmentVariation(collections.namedtuple("_EnvironmentVariation", "variables")):
     @classmethod
@@ -559,17 +563,11 @@ class VariationSpec(mdiffconf.ImmutableNamespace):
     def actions(self):
         return [(k, k in self.__dict__, v) for k, v in VARIATIONS.items()]
 
-    def apply_dynamic_defaults(self, source_root):
-        return self.__class__(**{
-            k: v.apply_dynamic_defaults(source_root) if hasattr(v, "apply_dynamic_defaults") else v
-            for k, v in self.__dict__.items()
-        })
-
 
-class Variations(collections.namedtuple('_Variations', 'spec verbosity min_cpus')):
+class Variations(collections.namedtuple('_Variations', 'spec verbosity min_cpus base_faketime')):
     @classmethod
-    def of(cls, *specs, zero=VariationSpec.empty(), verbosity=0, min_cpus=1):
-        return [cls(spec, verbosity, min_cpus) for spec in [zero] + list(specs)]
+    def of(cls, *specs, zero=VariationSpec.empty(), verbosity=0, min_cpus=1, base_faketime="@0"):
+        return [cls(spec, verbosity, min_cpus, base_faketime) for spec in [zero] + list(specs)]
 
     @property
     def replace(self):
@@ -635,4 +633,4 @@ if __name__ == "__main__":
         d = d.extend([s])
         print(s)
         print(">>>", d)
-    print("result", d.apply_dynamic_defaults("."))
+    print("result", d)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/reprotest.git



More information about the Reproducible-commits mailing list