Here's an updated patch for this issue. The difference relative to my previous patch is that, rather than taking an environment variable to disable timestamp output altogether, this takes an environment variable to force a specific time: this is intended to be used by build systems to e.g. ensure that all documents produced by a given build have a creation date corresponding to the changelog date of the overall package, rather than whatever time the build system happened to run.
Notwithstanding Werner's comments, I think that an environment variable is a clearly better approach to this, because it's much easier to arrange for it to be passed through build systems. But I hope that switching to the timestamp-based variable means that it's unlikely to be set permanently in users' environments. Also, SOURCE_DATE_EPOCH is an emerging standard which is supported in an increasing number of other packages, which means that build systems only need to set one variable rather than kludging around things in a dozen different places. The reason why filtering PDF output and the like is an inferior solution, even though it's certainly possible (strip-nondeterminism etc.; https://reproducible-builds.org/docs/timestamps/), is that it's much more complicated and less transparent. With a reproducible build toolchain it is possible to build an entire package multiple times on entirely different systems and get a bitwise-identical .deb; if you're relying on postprocessing then it's much less obvious that nothing else has sneaked in and you need a complicated pile of stuff to unpack two packages and do a deep (possibly recursive!) comparison of files within them. Yes, we did without this for 20+ years, and it's not absolutely the end of the world not to have it, but that doesn't mean it isn't useful to work on it now. More details on SOURCE_DATE_EPOCH (and there are many other useful things on the same site): https://reproducible-builds.org/specs/source-date-epoch/ -- Colin Watson [cjwat...@debian.org]
>From c111bb75c4793bbde55d173969492828d83496db Mon Sep 17 00:00:00 2001 From: Colin Watson <cjwat...@debian.org> Date: Wed, 27 Aug 2014 09:06:26 +0100 Subject: [PATCH] Implement `SOURCE_DATE_EPOCH' for reproducible builds. * src/include/curtime.h: New file. * src/libs/libgroff/curtime.cpp: New file. * src/libs/libgroff/libgroff.am (libgroff_a_SOURCES): Add src/libs/libgroff/curtime.cpp. * src/roff/troff/input.cpp (init_registers): Use `current_time' instead of `time(0)'. * src/devices/grohtml/post-html.cpp (html_printer::do_file_components): Likewise. (html_printer::~html_printer): Likewise. * src/devices/grops/ps.cpp (ps_printer::~ps_printer): Likewise. * src/devices/gropdf/gropdf.pl: Use `$ENV{SOURCE_DATE_EPOCH}` if available in preference to `time`. * doc/groff.texi (Environment): Document `SOURCE_DATE_EPOCH'. * src/devices/grohtml/grohtml.1.man (ENVIRONMENT): Likewise. * src/devices/gropdf/gropdf.1.man (ENVIRONMENT): Likewise. * src/devices/grops/grops.1.man (ENVIRONMENT): Likewise. --- ChangeLog | 23 ++++++++++++++++++ doc/groff.texi | 6 +++++ src/devices/grohtml/grohtml.1.man | 7 ++++++ src/devices/grohtml/post-html.cpp | 5 ++-- src/devices/gropdf/gropdf.1.man | 7 ++++++ src/devices/gropdf/gropdf.pl | 3 ++- src/devices/grops/grops.1.man | 7 ++++++ src/devices/grops/ps.cpp | 3 ++- src/include/curtime.h | 23 ++++++++++++++++++ src/libs/libgroff/curtime.cpp | 51 +++++++++++++++++++++++++++++++++++++++ src/libs/libgroff/libgroff.am | 1 + src/roff/troff/input.cpp | 3 ++- 12 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 src/include/curtime.h create mode 100644 src/libs/libgroff/curtime.cpp diff --git a/ChangeLog b/ChangeLog index 2333859..98eac45 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2015-11-05 Colin Watson <cjwat...@debian.org> + + Implement `SOURCE_DATE_EPOCH' for reproducible builds. + + * src/include/curtime.h: New file. + * src/libs/libgroff/curtime.cpp: New file. + * src/libs/libgroff/libgroff.am (libgroff_a_SOURCES): Add + src/libs/libgroff/curtime.cpp. + + * src/roff/troff/input.cpp (init_registers): Use `current_time' + instead of `time(0)'. + * src/devices/grohtml/post-html.cpp + (html_printer::do_file_components): Likewise. + (html_printer::~html_printer): Likewise. + * src/devices/grops/ps.cpp (ps_printer::~ps_printer): Likewise. + * src/devices/gropdf/gropdf.pl: Use `$ENV{SOURCE_DATE_EPOCH}` if + available in preference to `time`. + + * doc/groff.texi (Environment): Document `SOURCE_DATE_EPOCH'. + * src/devices/grohtml/grohtml.1.man (ENVIRONMENT): Likewise. + * src/devices/gropdf/gropdf.1.man (ENVIRONMENT): Likewise. + * src/devices/grops/grops.1.man (ENVIRONMENT): Likewise. + 2015-10-27 Deri James <d...@chuzzlewit.myzen.co.uk> gropdf was choking on -I flag passed by groff, now uses diff --git a/doc/groff.texi b/doc/groff.texi index 00a0f6d..9dc63eb 100644 --- a/doc/groff.texi +++ b/doc/groff.texi @@ -1453,6 +1453,12 @@ default directory (on Unix and GNU/Linux systems, this is usually @item GROFF_TYPESETTER @tindex GROFF_TYPESETTER@r{, environment variable} The default output device. + +@item SOURCE_DATE_EPOCH +@tindex SOURCE_DATE_EPOCH@r{, environment variable} +A timestamp (expressed as seconds since the Unix epoch) to use in place of +the current time when initializing time-based built-in registers such as +@code{\n[seconds]}. @end table Note that MS-DOS and MS-Windows ports of @code{groff} use semi-colons, diff --git a/src/devices/grohtml/grohtml.1.man b/src/devices/grohtml/grohtml.1.man index 2efdd81..f8c45dd 100644 --- a/src/devices/grohtml/grohtml.1.man +++ b/src/devices/grohtml/grohtml.1.man @@ -419,6 +419,13 @@ and for more details. . . +.TP +.SM +.B SOURCE_DATE_EPOCH +A timestamp (expressed as seconds since the Unix epoch) to use as the +creation timestamp in place of the current time. +. +. .\" -------------------------------------------------------------------- .SH BUGS .\" -------------------------------------------------------------------- diff --git a/src/devices/grohtml/post-html.cpp b/src/devices/grohtml/post-html.cpp index fefbf01..b5fc516 100644 --- a/src/devices/grohtml/post-html.cpp +++ b/src/devices/grohtml/post-html.cpp @@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "html.h" #include "html-text.h" #include "html-table.h" +#include "curtime.h" #include <time.h> @@ -5013,7 +5014,7 @@ void html_printer::do_file_components (void) .put_string(Version_string) .end_comment(); - t = time(0); + t = current_time(); html.begin_comment("CreationDate: ") .put_string(ctime(&t), strlen(ctime(&t))-1) .end_comment(); @@ -5126,7 +5127,7 @@ html_printer::~html_printer() .put_string(Version_string) .end_comment(); - t = time(0); + t = current_time(); html.begin_comment("CreationDate: ") .put_string(ctime(&t), strlen(ctime(&t))-1) .end_comment(); diff --git a/src/devices/gropdf/gropdf.1.man b/src/devices/gropdf/gropdf.1.man index 25287a6..4a06c0f 100644 --- a/src/devices/gropdf/gropdf.1.man +++ b/src/devices/gropdf/gropdf.1.man @@ -1024,6 +1024,13 @@ and for more details. . . +.TP +.SM +.B SOURCE_DATE_EPOCH +A timestamp (expressed as seconds since the Unix epoch) to use as the +creation timestamp in place of the current time. +. +. .\" -------------------------------------------------------------------- .SH FILES .\" -------------------------------------------------------------------- diff --git a/src/devices/gropdf/gropdf.pl b/src/devices/gropdf/gropdf.pl index 0744378..b95169e 100644 --- a/src/devices/gropdf/gropdf.pl +++ b/src/devices/gropdf/gropdf.pl @@ -259,13 +259,14 @@ elsif (exists($ppsz{$papersz})) @defaultmb=@mediabox=(0,0,$ppsz{$papersz}->[0],$ppsz{$papersz}->[1]); } -my (@dt)=localtime(time); +my (@dt)=localtime($ENV{SOURCE_DATE_EPOCH} || time); my $dt=PDFDate(\@dt); my %info=('Creator' => "(groff version $cfg{GROFF_VERSION})", 'Producer' => "(gropdf version $cfg{GROFF_VERSION})", 'ModDate' => "($dt)", 'CreationDate' => "($dt)"); + while (<>) { chomp; diff --git a/src/devices/grops/grops.1.man b/src/devices/grops/grops.1.man index e7064d2..55b26f7 100644 --- a/src/devices/grops/grops.1.man +++ b/src/devices/grops/grops.1.man @@ -1423,6 +1423,13 @@ and for more details. . . +.TP +.SM +.B SOURCE_DATE_EPOCH +A timestamp (expressed as seconds since the Unix epoch) to use as the +creation timestamp in place of the current time. +. +. .\" -------------------------------------------------------------------- .SH FILES .\" -------------------------------------------------------------------- diff --git a/src/devices/grops/ps.cpp b/src/devices/grops/ps.cpp index 745a503..03e6537 100644 --- a/src/devices/grops/ps.cpp +++ b/src/devices/grops/ps.cpp @@ -28,6 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "cset.h" #include "nonposix.h" #include "paper.h" +#include "curtime.h" #include "ps.h" #include <time.h> @@ -1390,7 +1391,7 @@ ps_printer::~ps_printer() #else time_t #endif - t = time(0); + t = current_time(); fputs(ctime(&t), out.get_file()); } for (font_pointer_list *f = font_list; f; f = f->next) { diff --git a/src/include/curtime.h b/src/include/curtime.h new file mode 100644 index 0000000..a410519 --- /dev/null +++ b/src/include/curtime.h @@ -0,0 +1,23 @@ +/* Copyright (C) 2015 Free Software Foundation, Inc. + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation, either version 2 of the License, or +(at your option) any later version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +The GNU General Public License version 2 (GPL2) is available in the +internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */ + +#ifdef LONG_FOR_TIME_T +long +#else +time_t +#endif +current_time(); diff --git a/src/libs/libgroff/curtime.cpp b/src/libs/libgroff/curtime.cpp new file mode 100644 index 0000000..00821b7 --- /dev/null +++ b/src/libs/libgroff/curtime.cpp @@ -0,0 +1,51 @@ +/* Copyright (C) 2015 Free Software Foundation, Inc. + +This file is part of groff. + +groff is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation, either version 2 of the License, or +(at your option) any later version. + +groff is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +The GNU General Public License version 2 (GPL2) is available in the +internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */ + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "errarg.h" +#include "error.h" + +#ifdef LONG_FOR_TIME_T +long +#else +time_t +#endif +current_time() +{ + char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + + if (source_date_epoch) { + errno = 0; + char *endptr; + long epoch = strtol(source_date_epoch, &endptr, 10); + + if ((errno == ERANGE && (epoch == LONG_MAX || epoch == LONG_MIN)) || + (errno != 0 && epoch == 0)) + fatal("$SOURCE_DATE_EPOCH: strtol: %1", strerror(errno)); + if (endptr == source_date_epoch) + fatal("$SOURCE_DATE_EPOCH: no digits found: %1", endptr); + if (*endptr != '\0') + fatal("$SOURCE_DATE_EPOCH: trailing garbage: %1", endptr); + return epoch; + } else + return time(0); +} diff --git a/src/libs/libgroff/libgroff.am b/src/libs/libgroff/libgroff.am index 78e3394..e215f2e 100644 --- a/src/libs/libgroff/libgroff.am +++ b/src/libs/libgroff/libgroff.am @@ -33,6 +33,7 @@ libgroff_a_SOURCES = \ src/libs/libgroff/cmap.cpp \ src/libs/libgroff/color.cpp \ src/libs/libgroff/cset.cpp\ + src/libs/libgroff/curtime.cpp \ src/libs/libgroff/device.cpp \ src/libs/libgroff/errarg.cpp \ src/libs/libgroff/error.cpp \ diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp index 9594f07..f7d2c18 100644 --- a/src/roff/troff/input.cpp +++ b/src/roff/troff/input.cpp @@ -36,6 +36,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "input.h" #include "defs.h" #include "unicode.h" +#include "curtime.h" // Needed for getpid() and isatty() #include "posix.h" @@ -8138,7 +8139,7 @@ static void init_registers() #else /* not LONG_FOR_TIME_T */ time_t #endif /* not LONG_FOR_TIME_T */ - t = time(0); + t = current_time(); // Use struct here to work around misfeature in old versions of g++. struct tm *tt = localtime(&t); set_number_reg("seconds", int(tt->tm_sec)); -- 2.6.2