Skip to contentSkip to conversation

toolchain/wrapper: fake __DATE_ and __TIME__ for older gcc

PATCH New Arnout Vandecappelle (Essensium/Mind) 3 participants 3 messages Oct 21, 2017 – Oct 22, 2017

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 toolchain/wrapper: fake __DATE_ and __TIME__ for older gcc

From: "Yann E. MORIN" <[email protected]>

Starting with version 7, gcc automatically recognises and enforces the environment variable SOURCE_DATE_EPOCH, and fakes __DATE__ and __TIME__ accordingly, to produce reproducible builds (at least in regards to date and time).

However, older gcc versions do not offer this feature.

So, we use our toolchain wrapper to force-feed __DATE__ and __TIME__ as macros, which will take precedence over those that gcc may compute itself. We compute them according to the specs:
https://reproducible-builds.org/specs/source-date-epoch/
https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

Since we define macros otherwise internal to gcc, we have to tell it not to warn about that. The -Wno-builtin-macro-redefined flag was introduced in gcc-4.4.0. Therefore, we make BR2_REPRODUCIBLE depend on GCC >= 4.4.

gcc-7 will ignore SOURCE_DATE_EPOCH when __DATE__ and __TIME__ are user-defined. Anyway, this is of no consequence: whether __DATE__ and __TIME__ or SOURCE_DATE_EPOCH takes precedence, it would yield the exact same end result since we use the same logic to compute it. Note that we didn't copy the code for it from gcc so using the same logic doesn't imply that we're inheriting GPL-3.0.

Cc J?r?me Pouiller <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Arnout Vandecappelle <[email protected]>
Cc Peter Korsgaard <[email protected]>
[Arnout: rewrite commit message]
---
 Config.in                     |  2 ++
 toolchain/toolchain-wrapper.c | 75 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 75 insertions(+), 2 deletions(-)
Config.in +2 -0
index 9bdb0b857a..8920b727ce 100644
@@ -712,6 +712,8 @@ config BR2_COMPILER_PARANOID_UNSAFE_PATH
 
 config BR2_REPRODUCIBLE
 	bool "Make the build reproducible (experimental)"
+	# SOURCE_DATE_EPOCH support in toolchain-wrapper requires GCC 4.4
+	depends on BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
 	help
 	  This option will remove all sources of non-reproducibility
 	  from the build process. For a given Buildroot configuration,
toolchain/toolchain-wrapper.c +73 -2

109 lines not on this page · raw message

Signature · 1 line
2.15.0.rc1
#1 lore raw

PATCH 1/1 toolchain/wrapper: fake __DATE_ and __TIME__ for older gcc

Arnout, All,

On 2017-10-21 22:31 +0200, Arnout Vandecappelle (Essensium/Mind) spake thusly:

Quoted 19 lines

From: "Yann E. MORIN" <[email protected]>

Starting with version 7, gcc automatically recognises and enforces the environment variable SOURCE_DATE_EPOCH, and fakes __DATE__ and __TIME__ accordingly, to produce reproducible builds (at least in regards to date and time).

However, older gcc versions do not offer this feature.

So, we use our toolchain wrapper to force-feed __DATE__ and __TIME__ as macros, which will take precedence over those that gcc may compute itself. We compute them according to the specs:
https://reproducible-builds.org/specs/source-date-epoch/
https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

Since we define macros otherwise internal to gcc, we have to tell it not to warn about that. The -Wno-builtin-macro-redefined flag was introduced in gcc-4.4.0. Therefore, we make BR2_REPRODUCIBLE depend on GCC >= 4.4.

Quoted 6 lines

gcc-7 will ignore SOURCE_DATE_EPOCH when __DATE__ and __TIME__ are user-defined. Anyway, this is of no consequence: whether __DATE__ and __TIME__ or SOURCE_DATE_EPOCH takes precedence, it would yield the exact same end result since we use the same logic to compute it. Note that we didn't copy the code for it from gcc so using the same logic doesn't imply that we're inheriting GPL-3.0.

Thnaks for re-spinning this! :-)

Quoted 7 lines
Cc J?r?me Pouiller <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Arnout Vandecappelle <[email protected]>
Cc Peter Korsgaard <[email protected]>
[Arnout: rewrite commit message]

Regards,
Yann E. MORIN.

Quoted 138 lines
---
 Config.in                     |  2 ++
 toolchain/toolchain-wrapper.c | 75 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 75 insertions(+), 2 deletions(-)
Config.in +2 -0
index 9bdb0b857a..8920b727ce 100644
@@ -712,6 +712,8 @@ config BR2_COMPILER_PARANOID_UNSAFE_PATH
 
 config BR2_REPRODUCIBLE
 	bool "Make the build reproducible (experimental)"
+	# SOURCE_DATE_EPOCH support in toolchain-wrapper requires GCC 4.4
+	depends on BR2_TOOLCHAIN_GCC_AT_LEAST_4_4
 	help
 	  This option will remove all sources of non-reproducibility
 	  from the build process. For a given Buildroot configuration,
toolchain/toolchain-wrapper.c +73 -2
index dd77c11131..2928ea42d0 100644
@@ -22,12 +22,19 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <time.h>
+#include <stdbool.h>
 
 #ifdef BR_CCACHE
 static char ccache_path[PATH_MAX];
 #endif
 static char path[PATH_MAX];
 static char sysroot[PATH_MAX];
+/* As would be defined by gcc:
+ *   https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
+ * sizeof() on string literals includes the terminating \0. */
+static char _time_[sizeof("-D__TIME__=\"HH:MM:SS\"")];
+static char _date_[sizeof("-D__DATE__=\"MMM DD YYYY\"")];
 
 /**
  * GCC errors out with certain combinations of arguments (examples are
@@ -35,12 +42,15 @@ static char sysroot[PATH_MAX];
  * that we only pass the predefined one to the real compiler if the inverse
  * option isn't in the argument list.
  * This specifies the worst case number of extra arguments we might pass
- * Currently, we have:
+ * Currently, we may have:
  * 	-mfloat-abi=
  * 	-march=
  * 	-mcpu=
+ * 	-D__TIME__=
+ * 	-D__DATE__=
+ * 	-Wno-builtin-macro-redefined
  */
-#define EXCLUSIVE_ARGS	3
+#define EXCLUSIVE_ARGS	6
 
 static char *predef_args[] = {
 #ifdef BR_CCACHE
@@ -160,6 +170,60 @@ static void check_unsafe_path(const char *arg,
 	}
 }
 
+/* Returns false if SOURCE_DATE_EPOCH was not defined in the environment.
+ *
+ * Returns true if SOURCE_DATE_EPOCH is in the environment and represent
+ * a valid timestamp, in which case the timestamp is formatted into the
+ * global variables _date_ and _time_.
+ *
+ * Aborts if SOURCE_DATE_EPOCH was set in the environment but did not
+ * contain a valid timestamp.
+ *
+ * Valid values are defined in the spec:
+ *     https://reproducible-builds.org/specs/source-date-epoch/
+ * but we further restrict them to be positive or null.
+ */
+bool parse_source_date_epoch_from_env(void)
+{
+	char *epoch_env, *endptr;
+	time_t epoch;
+	struct tm epoch_tm;
+
+	if ((epoch_env = getenv("SOURCE_DATE_EPOCH")) == NULL)
+		return false;
+	errno = 0;
+	epoch = (time_t) strtoll(epoch_env, &endptr, 10);
+	/* We just need to test if it is incorrect, but we do not
+	 * care why it is incorrect.
+	 */
+	if ((errno != 0) || !*epoch_env || *endptr || (epoch < 0)) {
+		fprintf(stderr, "%s: invalid SOURCE_DATE_EPOCH='%s'\n",
+			program_invocation_short_name,
+			epoch_env);
+		exit(1);
+	}
+	tzset(); /* For localtime_r(), below. */
+	if (localtime_r(&epoch, &epoch_tm) == NULL) {
+		fprintf(stderr, "%s: cannot parse SOURCE_DATE_EPOCH=%s\n",
+				program_invocation_short_name,
+				getenv("SOURCE_DATE_EPOCH"));
+		exit(1);
+	}
+	if (!strftime(_time_, sizeof(_time_), "-D__TIME__=\"%T\"", &epoch_tm)) {
+		fprintf(stderr, "%s: cannot set time from SOURCE_DATE_EPOCH=%s\n",
+				program_invocation_short_name,
+				getenv("SOURCE_DATE_EPOCH"));
+		exit(1);
+	}
+	if (!strftime(_date_, sizeof(_date_), "-D__DATE__=\"%b %e %Y\"", &epoch_tm)) {
+		fprintf(stderr, "%s: cannot set date from SOURCE_DATE_EPOCH=%s\n",
+				program_invocation_short_name,
+				getenv("SOURCE_DATE_EPOCH"));
+		exit(1);
+	}
+	return true;
+}
+
 int main(int argc, char **argv)
 {
 	char **args, **cur, **exec_args;
@@ -289,6 +353,13 @@ int main(int argc, char **argv)
 	}
 #endif /* ARCH || CPU */
 
+	if (parse_source_date_epoch_from_env()) {
+		*cur++ = _time_;
+		*cur++ = _date_;
+		/* This has existed since gcc-4.4.0. */
+		*cur++ = "-Wno-builtin-macro-redefined";
+	}
+
 	paranoid_wrapper = getenv("BR_COMPILER_PARANOID_UNSAFE_PATH");
 	if (paranoid_wrapper && strlen(paranoid_wrapper) > 0)
 		paranoid = 1;
Signature · 1 line
2.15.0.rc1
Signature · 6 lines
.-----------------.--------------------.------------------.--------------------.
|  Yann E. MORIN  | Real-Time Embedded | /"\ ASCII RIBBON | Erics' conspiracy: |
| +33 662 376 056 | Software  Designer | \ / CAMPAIGN     |  ___               |
| +33 223 225 172 `------------.-------:  X  AGAINST      |  \e/  There is no  |
| http://ymorin.is-a-geek.org/ | _/*\_ | / \ HTML MAIL    |   v   conspiracy.  |
'------------------------------^-------^------------------^--------------------'
#2 lore raw

PATCH 1/1 toolchain/wrapper: fake __DATE_ and __TIME__ for older gcc

In reply to Arnout Vandecappelle (Essensium/Mind)
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

"Arnout" == Arnout Vandecappelle (Essensium/Mind) <[email protected]> writes:

Hi,

Quoted 25 lines

From: "Yann E. MORIN" <[email protected]>
Starting with version 7, gcc automatically recognises and enforces the environment variable SOURCE_DATE_EPOCH, and fakes __DATE__ and __TIME__ accordingly, to produce reproducible builds (at least in regards to date and time).

However, older gcc versions do not offer this feature.

So, we use our toolchain wrapper to force-feed __DATE__ and __TIME__ as macros, which will take precedence over those that gcc may compute itself. We compute them according to the specs:
https://reproducible-builds.org/specs/source-date-epoch/
https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

Since we define macros otherwise internal to gcc, we have to tell it not to warn about that. The -Wno-builtin-macro-redefined flag was introduced in gcc-4.4.0. Therefore, we make BR2_REPRODUCIBLE depend on GCC >= 4.4.

gcc-7 will ignore SOURCE_DATE_EPOCH when __DATE__ and __TIME__ are user-defined. Anyway, this is of no consequence: whether __DATE__ and __TIME__ or SOURCE_DATE_EPOCH takes precedence, it would yield the exact same end result since we use the same logic to compute it. Note that we didn't copy the code for it from gcc so using the same logic doesn't imply that we're inheriting GPL-3.0.

Quoted 7 lines
Cc J?r?me Pouiller <[email protected]>
Cc Thomas Petazzoni <[email protected]>
Cc Arnout Vandecappelle <[email protected]>
Cc Peter Korsgaard <[email protected]>
[Arnout: rewrite commit message]

Committed, thanks.

Signature · 1 line
Bye, Peter Korsgaard