From eecbb88e7f4647ddde9e24d39fbb97785e446b4a Mon Sep 17 00:00:00 2001 From: Magnus Ihse Bursie Date: Thu, 7 May 2020 17:47:11 +0200 Subject: [PATCH] 8244592: Start supporting SOURCE_DATE_EPOCH Reviewed-by: erikj --- make/Init.gmk | 3 + make/InitSupport.gmk | 9 ++ make/autoconf/configure.ac | 1 + make/autoconf/jdk-options.m4 | 73 ++++++++++ make/autoconf/jdk-version.m4 | 2 +- make/autoconf/spec.gmk.in | 3 + make/autoconf/util.m4 | 249 ++++++++++++++++++++++++++++++++++- 7 files changed, 336 insertions(+), 4 deletions(-) diff --git a/make/Init.gmk b/make/Init.gmk index 6897c343219..3ed46780ffe 100644 --- a/make/Init.gmk +++ b/make/Init.gmk @@ -226,6 +226,9 @@ else # HAS_SPEC=true # Parse COMPARE_BUILD (for makefile development) $(eval $(call ParseCompareBuild)) + # Setup reproducible build environment + $(eval $(call SetupReproducibleBuild)) + # If no LOG= was given on command line, but we have a non-standard default # value, use that instead and re-parse log level. ifeq ($(LOG), ) diff --git a/make/InitSupport.gmk b/make/InitSupport.gmk index baefc8941b3..9c11e2c2bcd 100644 --- a/make/InitSupport.gmk +++ b/make/InitSupport.gmk @@ -303,6 +303,15 @@ else # $(HAS_SPEC)=true topdir=$(TOPDIR) endif + # Setup the build environment to match the requested specification on + # level of reproducible builds + define SetupReproducibleBuild + ifeq ($$(SOURCE_DATE), updated) + SOURCE_DATE := $$(shell $$(DATE) +"%s") + endif + export SOURCE_DATE_EPOCH := $$(SOURCE_DATE) + endef + # Parse COMPARE_BUILD into COMPARE_BUILD_* # Syntax: COMPARE_BUILD=CONF=:PATCH=: # MAKE=:COMP_OPTS=: diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index c015f7312ab..393e80405b7 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -235,6 +235,7 @@ JDKOPT_ENABLE_DISABLE_FAILURE_HANDLER JDKOPT_ENABLE_DISABLE_GENERATE_CLASSLIST JDKOPT_EXCLUDE_TRANSLATIONS JDKOPT_ENABLE_DISABLE_MANPAGES +JDKOPT_SETUP_REPRODUCIBLE_BUILD ############################################################################### # diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 43a7b5617c8..fc02940d0c2 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -683,3 +683,76 @@ AC_DEFUN([JDKOPT_ALLOW_ABSOLUTE_PATHS_IN_OUTPUT], AC_SUBST(ALLOW_ABSOLUTE_PATHS_IN_OUTPUT) ]) + +################################################################################ +# +# Check and set options related to reproducible builds. +# +AC_DEFUN_ONCE([JDKOPT_SETUP_REPRODUCIBLE_BUILD], +[ + AC_ARG_WITH([source-date], [AS_HELP_STRING([--with-source-date], + [how to set SOURCE_DATE_EPOCH ('updated', 'current', 'version' a timestamp or an ISO-8601 date) @<:@updated@:>@])], + [with_source_date_present=true], [with_source_date_present=false]) + + AC_MSG_CHECKING([what source date to use]) + + if test "x$with_source_date" = xyes; then + AC_MSG_ERROR([--with-source-date must have a value]) + elif test "x$with_source_date" = xupdated || test "x$with_source_date" = x; then + # Tell the makefiles to update at each build + SOURCE_DATE=updated + AC_MSG_RESULT([determined at build time, from 'updated']) + elif test "x$with_source_date" = xcurrent; then + # Set the current time + SOURCE_DATE=$($DATE +"%s") + AC_MSG_RESULT([$SOURCE_DATE, from 'current']) + elif test "x$with_source_date" = xversion; then + # Use the date from version-numbers + UTIL_GET_EPOCH_TIMESTAMP(SOURCE_DATE, $DEFAULT_VERSION_DATE) + if test "x$SOURCE_DATE" = x; then + AC_MSG_RESULT([unavailable]) + AC_MSG_ERROR([Cannot convert DEFAULT_VERSION_DATE to timestamp]) + fi + AC_MSG_RESULT([$SOURCE_DATE, from 'version']) + else + # It's a timestamp, an ISO-8601 date, or an invalid string + # Additional [] needed to keep m4 from mangling shell constructs. + if [ [[ "$with_source_date" =~ ^[0-9][0-9]*$ ]] ] ; then + SOURCE_DATE=$with_source_date + AC_MSG_RESULT([$SOURCE_DATE, from timestamp on command line]) + else + UTIL_GET_EPOCH_TIMESTAMP(SOURCE_DATE, $with_source_date) + if test "x$SOURCE_DATE" != x; then + AC_MSG_RESULT([$SOURCE_DATE, from ISO-8601 date on command line]) + else + AC_MSG_RESULT([unavailable]) + AC_MSG_ERROR([Cannot parse date string "$with_source_date"]) + fi + fi + fi + + REPRODUCIBLE_BUILD_DEFAULT=$with_source_date_present + + if test "x$OPENJDK_BUILD_OS" = xwindows && \ + test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = xfalse; then + # To support banning absolute paths on Windows, we must use the -pathmap + # method, which requires reproducible builds. + REPRODUCIBLE_BUILD_DEFAULT=true + fi + + UTIL_ARG_ENABLE(NAME: reproducible-build, DEFAULT: $REPRODUCIBLE_BUILD_DEFAULT, + RESULT: ENABLE_REPRODUCIBLE_BUILD, + DESC: [enable reproducible builds (not yet fully functional)], + DEFAULT_DESC: [enabled if --with-source-date is given or on Windows without absolute paths]) + + if test "x$OPENJDK_BUILD_OS" = xwindows && \ + test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = xfalse && \ + test "x$ENABLE_REPRODUCIBLE_BUILD" = xfalse; then + AC_MSG_NOTICE([On Windows it is not possible to combine --disable-reproducible-builds]) + AC_MSG_NOTICE([with --disable-absolute-paths-in-output.]) + AC_MSG_ERROR([Cannot continue]) + fi + + AC_SUBST(SOURCE_DATE) + AC_SUBST(ENABLE_REPRODUCIBLE_BUILD) +]) diff --git a/make/autoconf/jdk-version.m4 b/make/autoconf/jdk-version.m4 index 2620b013572..f0d1edfcbfd 100644 --- a/make/autoconf/jdk-version.m4 +++ b/make/autoconf/jdk-version.m4 @@ -36,7 +36,7 @@ AC_DEFUN([JDKVER_CHECK_AND_SET_NUMBER], [ # Additional [] needed to keep m4 from mangling shell constructs. - if [ ! [[ "$2" =~ ^0*([1-9][0-9]*)|(0)$ ]] ] ; then + if [ ! [[ "$2" =~ ^0*([1-9][0-9]*)$|^0*(0)$ ]] ] ; then AC_MSG_ERROR(["$2" is not a valid numerical value for $1]) fi # Extract the version number without leading zeros. diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 1746be3aa88..51dad49d1ac 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -126,6 +126,9 @@ RELEASE_FILE_OS_NAME:=@RELEASE_FILE_OS_NAME@ RELEASE_FILE_OS_ARCH:=@RELEASE_FILE_OS_ARCH@ RELEASE_FILE_LIBC:=@RELEASE_FILE_LIBC@ +SOURCE_DATE := @SOURCE_DATE@ +ENABLE_REPRODUCIBLE_BUILD := @ENABLE_REPRODUCIBLE_BUILD@ + LIBM:=@LIBM@ LIBDL:=@LIBDL@ diff --git a/make/autoconf/util.m4 b/make/autoconf/util.m4 index c768e4264e4..bb6bbe1c2da 100644 --- a/make/autoconf/util.m4 +++ b/make/autoconf/util.m4 @@ -52,7 +52,7 @@ m4_include([util_paths.m4]) AC_DEFUN([UTIL_DEFUN_NAMED], [ AC_DEFUN($1, [ - m4_foreach(arg, m4_split($2), [ + m4_foreach(arg, m4_split(m4_normalize($2)), [ m4_if(m4_bregexp(arg, [^\*]), -1, [ m4_set_add(legal_named_args, arg) @@ -65,11 +65,12 @@ AC_DEFUN([UTIL_DEFUN_NAMED], ]) m4_foreach([arg], [$3], [ + m4_if(m4_bregexp(arg, [: ]), -1, m4_define([arg], m4_bpatsubst(arg, [:], [: ]))) m4_define(arg_name, m4_substr(arg, 0, m4_bregexp(arg, [: ]))) - m4_set_contains(legal_named_args, arg_name, [],[AC_MSG_ERROR([Internal error: arg_name is not a valid named argument to [$1]. Valid arguments are 'm4_set_contents(legal_named_args, [ ])'.])]) + m4_set_contains(legal_named_args, arg_name, [],[AC_MSG_ERROR([Internal error: m4_if(arg_name, , arg, arg_name) is not a valid named argument to [$1]. Valid arguments are 'm4_set_contents(defined_args, [ ]) m4_set_contents(legal_named_args, [ ])'.])]) m4_set_remove(required_named_args, arg_name) m4_set_remove(legal_named_args, arg_name) - m4_pushdef([ARG_][]arg_name, m4_substr(arg, m4_incr(m4_incr(m4_bregexp(arg, [: ]))))) + m4_pushdef([ARG_][]arg_name, m4_bpatsubst(m4_substr(arg, m4_incr(m4_incr(m4_bregexp(arg, [: ])))), [^\s*], [])) m4_set_add(defined_args, arg_name) m4_undefine([arg_name]) ]) @@ -94,6 +95,83 @@ AC_DEFUN([UTIL_DEFUN_NAMED], ]) ]) +############################################################################### +# Assert that a programmatic condition holds. If not, exit with an error message. +# Check that a shell expression gives return code 0 +# +# $1: The shell expression to evaluate +# $2: A message to describe the expression in case of failure +# $2: An message to print in case of failure [optional] +# +AC_DEFUN([UTIL_ASSERT_SHELL_TEST], +[ + ASSERTION_MSG="m4_normalize([$3])" + if $1; then + $ECHO Assertion failed: $2 + if test "x$3" != x; then + $ECHO Assertion message: "$3" + fi + exit 1 + fi +]) + + +############################################################################### +# Assert that a programmatic condition holds. If not, exit with an error message. +# Check that two strings are equal. +# +# $1: The actual string found +# $2: The expected string +# $3: An message to print in case of failure [optional] +# +AC_DEFUN([UTIL_ASSERT_STRING_EQUALS], +[ + UTIL_ASSERT_SHELL_TEST( + [test "x[$1]" != "x[$2]"], + [Actual value '[$1]' \("[$1]"\) did not match expected value '[$2]' \("[$2]"\)], + $3) +]) + +############################################################################### +# Assert that a programmatic condition holds. If not, exit with an error message. +# Check that two strings not are equal. +# +# $1: The actual string found +# $2: The expected string +# $3: An message to print in case of failure [optional] +# +AC_DEFUN([UTIL_ASSERT_STRING_NOT_EQUALS], +[ + UTIL_ASSERT_SHELL_TEST( + [test "x[$1]" = "x[$2]"], + [Actual value '[$1]' \("[$1]"\) unexpectedly matched '[$2]' \("[$2]"\)], + $3) +]) + +############################################################################### +# Assert that a programmatic condition holds. If not, exit with an error message. +# Check that the given expression evaluates to the string 'true' +# +# $1: The expression to evaluate +# $2: An message to print in case of failure [optional] +# +AC_DEFUN([UTIL_ASSERT_TRUE], +[ + UTIL_ASSERT_STRING_EQUALS($1, true, $3) +]) + +############################################################################### +# Assert that a programmatic condition holds. If not, exit with an error message. +# Check that the given expression does not evaluate to the string 'true' +# +# $1: The expression to evaluate +# $2: An message to print in case of failure [optional] +# +AC_DEFUN([UTIL_ASSERT_NOT_TRUE], +[ + UTIL_ASSERT_STRING_NOT_EQUALS($1, true, $3) +]) + ############################################################################### # Check if a list of space-separated words are selected only from a list of # space-separated legal words. Typical use is to see if a user-specified @@ -148,6 +226,29 @@ AC_DEFUN([UTIL_GET_MATCHING_VALUES], fi ]) +############################################################################### +# Converts an ISO-8601 date/time string to a unix epoch timestamp. If no +# suitable conversion method was found, an empty string is returned. +# +# Sets the specified variable to the resulting list. +# +# $1: result variable name +# $2: input date/time string +AC_DEFUN([UTIL_GET_EPOCH_TIMESTAMP], +[ + timestamp=$($DATE --utc --date=$2 +"%s" 2> /dev/null) + if test "x$timestamp" = x; then + # GNU date format did not work, try BSD date options + timestamp=$($DATE -j -f "%F %T" "$2" "+%s" 2> /dev/null) + if test "x$timestamp" = x; then + # Perhaps the time was missing + timestamp=$($DATE -j -f "%F %T" "$2 00:00:00" "+%s" 2> /dev/null) + # If this did not work, we give up and return the empty string + fi + fi + $1=$timestamp +]) + ############################################################################### # Sort a space-separated list, and remove duplicates. # @@ -226,3 +327,145 @@ AC_DEFUN([UTIL_ALIASED_ARG_ENABLE], translit(patsubst($2, --), -, _)="$[enable_]translit($1, -, _)" ]) ]) + +############################################################################### +# Creates a command-line option using the --enable-* pattern. Will return a +# value of 'true' or 'false' in the RESULT variable, depending on whether the +# option was enabled or not by the user. The option can not be turned on if it +# is not available, as specified by AVAILABLE and/or CHECK_AVAILABLE. +# +# Arguments: +# NAME: The base name of this option (i.e. what follows --enable-). Required. +# RESULT: The name of the variable to set to the result. Defaults to +# _ENABLED. +# DEFAULT: The default value for this option. Can be true, false or auto. +# Defaults to true. +# AVAILABLE: If true, this option is allowed to be selected. Defaults to true. +# DESC: A description of this option. Defaults to a generic and unhelpful +# string. +# DEFAULT_DESC: A message describing the default value, for the help. Defaults +# to the literal value of DEFAULT. +# CHECKING_MSG: The message to present to user when checking this option. +# Defaults to a generic message. +# CHECK_AVAILABLE: An optional code block to execute to determine if the +# option should be available. Must set AVAILABLE to 'false' if not. +# IF_GIVEN: An optional code block to execute if the option was given on the +# command line (regardless of the value). +# IF_NOT_GIVEN: An optional code block to execute if the option was not given +# on the command line (regardless of the value). +# IF_ENABLED: An optional code block to execute if the option is turned on. +# IF_DISABLED: An optional code block to execute if the option is turned off. +# +UTIL_DEFUN_NAMED([UTIL_ARG_ENABLE], + [*NAME RESULT DEFAULT AVAILABLE DESC DEFAULT_DESC CHECKING_MSG + CHECK_AVAILABLE IF_GIVEN IF_NOT_GIVEN IF_ENABLED IF_DISABLED], [$@], +[ + ########################## + # Part 1: Set up m4 macros + ########################## + + # If DEFAULT is not specified, set it to 'true'. + m4_define([ARG_DEFAULT], m4_if(ARG_DEFAULT, , true, ARG_DEFAULT)) + + # If AVAILABLE is not specified, set it to 'true'. + m4_define([ARG_AVAILABLE], m4_if(ARG_AVAILABLE, , true, ARG_AVAILABLE)) + + # If DEFAULT_DESC is not specified, calculate it from DEFAULT. + m4_define([ARG_DEFAULT_DESC], m4_if(ARG_DEFAULT_DESC, , m4_if(ARG_DEFAULT, true, enabled, m4_if(ARG_DEFAULT, false, disabled, ARG_DEFAULT)), ARG_DEFAULT_DESC)) + + # If RESULT is not specified, set it to 'ARG_NAME[_ENABLED]'. + m4_define([ARG_RESULT], m4_if(ARG_RESULT, , m4_translit(ARG_NAME, [a-z-], [A-Z_])[_ENABLED], ARG_RESULT)) + # Construct shell variable names for the option + m4_define(ARG_OPTION, [enable_]m4_translit(ARG_NAME, [-], [_])) + m4_define(ARG_GIVEN, m4_translit(ARG_NAME, [a-z-], [A-Z_])[_GIVEN]) + + # If DESC is not specified, set it to a generic description. + m4_define([ARG_DESC], m4_if(ARG_DESC, , [Enable the ARG_NAME feature], m4_normalize(ARG_DESC))) + + # If CHECKING_MSG is not specified, set it to a generic description. + m4_define([ARG_CHECKING_MSG], m4_if(ARG_CHECKING_MSG, , [for --enable-ARG_NAME], m4_normalize(ARG_CHECKING_MSG))) + + # If the code blocks are not given, set them to the empty statements to avoid + # tripping up bash. + m4_define([ARG_CHECK_AVAILABLE], m4_if(ARG_CHECK_AVAILABLE, , :, ARG_CHECK_AVAILABLE)) + m4_define([ARG_IF_GIVEN], m4_if(ARG_IF_GIVEN, , :, ARG_IF_GIVEN)) + m4_define([ARG_IF_NOT_GIVEN], m4_if(ARG_IF_NOT_GIVEN, , :, ARG_IF_NOT_GIVEN)) + m4_define([ARG_IF_ENABLED], m4_if(ARG_IF_ENABLED, , :, ARG_IF_ENABLED)) + m4_define([ARG_IF_DISABLED], m4_if(ARG_IF_DISABLED, , :, ARG_IF_DISABLED)) + + ########################## + # Part 2: Set up autoconf shell code + ########################## + + # Check that DEFAULT has a valid value + if test "[x]ARG_DEFAULT" != xtrue && test "[x]ARG_DEFAULT" != xfalse && \ + test "[x]ARG_DEFAULT" != xauto ; then + AC_MSG_ERROR([Internal error: Argument DEFAULT to [UTIL_ARG_ENABLE] can only be true, false or auto, was: 'ARG_DEFAULT']) + fi + + # Check that AVAILABLE has a valid value + if test "[x]ARG_AVAILABLE" != xtrue && test "[x]ARG_AVAILABLE" != xfalse; then + AC_MSG_ERROR([Internal error: Argument AVAILABLE to [UTIL_ARG_ENABLE] can only be true or false, was: 'ARG_AVAILABLE']) + fi + + AC_ARG_ENABLE(ARG_NAME, AS_HELP_STRING([--enable-]ARG_NAME, + [ARG_DESC [ARG_DEFAULT_DESC]]), [ARG_GIVEN=true], [ARG_GIVEN=false]) + + # Check if the option is available + AVAILABLE=ARG_AVAILABLE + # Run the available check block (if any), which can overwrite AVAILABLE. + ARG_CHECK_AVAILABLE + + # Check if the option should be turned on + AC_MSG_CHECKING(ARG_CHECKING_MSG) + if test x$ARG_GIVEN = xfalse; then + if test ARG_DEFAULT = auto; then + # If not given, and default is auto, set it to true iff it's available. + ARG_RESULT=$AVAILABLE + REASON="from default 'auto'" + else + ARG_RESULT=ARG_DEFAULT + REASON="default" + fi + else + if test x$ARG_OPTION = xyes; then + ARG_RESULT=true + REASON="from command line" + elif test x$ARG_OPTION = xno; then + ARG_RESULT=false + REASON="from command line" + elif test x$ARG_OPTION = xauto; then + if test ARG_DEFAULT = auto; then + # If both given and default is auto, set it to true iff it's available. + ARG_RESULT=$AVAILABLE + else + ARG_RESULT=ARG_DEFAULT + fi + REASON="from command line 'auto'" + else + AC_MSG_ERROR([Option [--enable-]ARG_NAME can only be 'yes', 'no' or 'auto']) + fi + fi + + if test x$ARG_RESULT = xtrue; then + AC_MSG_RESULT([enabled, $REASON]) + if test x$AVAILABLE = xfalse; then + AC_MSG_ERROR([Option [--enable-]ARG_NAME is not available]) + fi + else + AC_MSG_RESULT([disabled, $REASON]) + fi + + # Execute result payloads, if present + if test x$ARG_GIVEN = xtrue; then + ARG_IF_GIVEN + else + ARG_IF_NOT_GIVEN + fi + + if test x$ARG_RESULT = xtrue; then + ARG_IF_ENABLED + else + ARG_IF_DISABLED + fi +])