Skip to content

Commit

Permalink
8264449: Enable reproducible builds with SOURCE_DATE_EPOCH
Browse files Browse the repository at this point in the history
8238650: Allow to override buildDate with SOURCE_DATE_EPOCH

Co-authored-by: Bernhard M. Wiedemann <javabmw@lsmod.de>
Reviewed-by: kcr, jvos
  • Loading branch information
jgneff and Bernhard M. Wiedemann committed Jun 20, 2023
1 parent 4232183 commit 0d9dcf3
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 31 deletions.
130 changes: 103 additions & 27 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,20 @@
*/
defaultTasks = ["sdk"]

import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.concurrent.CountDownLatch
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream

/******************************************************************************
* Utility methods *
Expand Down Expand Up @@ -256,6 +266,50 @@ void fail(String msg) {
throw new GradleException("FAIL: " + msg);
}

/**
* Rewrites the ZIP or JAR archive, setting the timestamp of each entry to
* the local date and time in UTC of the instant provided.
*
* Note that using the method 'eachFile(Closure)' of the Zip Task for this
* purpose fails to modify the JAR entries for META-INF and MANIFEST.MF
* because the manifest file is generated by Gradle instead of copied.
*
* Also note that the ZIP format has no notion of time zone. The UTC date
* and time can be set in optional extra fields, but only in addition to
* the local "MS-DOS date and time." To avoid depending on the time zone
* of the build machine, this method provides the local date and time in
* UTC by calling 'setTimeLocal(LocalDateTime)'.
*
* An alternative solution is to set the default time zone of the
* JVM temporarily to UTC while providing the UTC date and time with
* 'setLastModifiedTime(FileTime)'. This solution stores the precise instant
* on the time-line, but it also increases the size of the archive by adding
* an extra Extended Timestamp field for each entry.
*
* @param archive the ZIP or JAR archive file to rewrite
* @param instant the instant for the timestamp of each entry
*/
void setFileTimestamps(Provider<RegularFile> archive, Instant instant) {
def dosTime = LocalDateTime.ofInstant(instant, ZoneOffset.UTC)
def oldFile = archive.get().getAsFile()
def zipFile = new ZipFile(oldFile)
def newName = oldFile.getName() + ".new"
def newFile = new File(oldFile.getParentFile(), newName)
def output = new ZipOutputStream(new FileOutputStream(newFile))
zipFile.entries().each { ZipEntry entry ->
def clone = new ZipEntry(entry)
def input = zipFile.getInputStream(entry)
clone.setTimeLocal(dosTime)
output.putNextEntry(clone)
input.transferTo(output)
output.closeEntry()
input.close()
}
output.close()
zipFile.close()
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
}

/******************************************************************************
* *
* Definition of project properties *
Expand Down Expand Up @@ -578,14 +632,25 @@ if (jfxReleasePatchVersion == "0") {
defineProperty("RELEASE_VERSION", relVer)
defineProperty("RELEASE_VERSION_PADDED", "${jfxReleaseMajorVersion}.${jfxReleaseMinorVersion}.${jfxReleaseSecurityVersion}.${jfxReleasePatchVersion}")

def buildDate = new java.util.Date()
def buildTimestamp = new java.text.SimpleDateFormat("yyyy-MM-dd-HHmmss").format(buildDate)
def buildInstant = Instant.now().truncatedTo(ChronoUnit.SECONDS)
def sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH")
if (sourceDateEpoch != null) {
def epochSeconds = Long.parseLong(sourceDateEpoch)
buildInstant = Instant.ofEpochSecond(epochSeconds)
}
// Creates the timestamp in UTC using the ISO 8601 extended format
def buildTimestamp = buildInstant.toString()
defineProperty("BUILD_TIMESTAMP", buildTimestamp)
def relSuffix = ""
def relOpt = ""
if (HUDSON_JOB_NAME == "not_hudson") {
// The version OPT field matches the regular expression "([-a-zA-Z0-9.]+)".
// For the ISO 8601 basic format, use the pattern "yyyyMMdd'T'HHmmssX".
def zonedTime = ZonedDateTime.ofInstant(buildInstant, ZoneOffset.UTC)
def formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HHmmss")
String versionTimestamp = zonedTime.format(formatter)
relSuffix = "-internal"
relOpt = "-${buildTimestamp}"
relOpt = "-${versionTimestamp}"
} else {
relSuffix = IS_MILESTONE_FCS ? "" : jfxReleaseSuffix
}
Expand Down Expand Up @@ -1392,6 +1457,7 @@ logger.quiet("HUDSON_JOB_NAME: $HUDSON_JOB_NAME")
logger.quiet("HUDSON_BUILD_NUMBER: $HUDSON_BUILD_NUMBER")
logger.quiet("PROMOTED_BUILD_NUMBER: $PROMOTED_BUILD_NUMBER")
logger.quiet("PRODUCT_NAME: $PRODUCT_NAME")
logger.quiet("BUILD_TIMESTAMP: $BUILD_TIMESTAMP")
logger.quiet("RELEASE_VERSION: $RELEASE_VERSION")
logger.quiet("RELEASE_SUFFIX: $RELEASE_SUFFIX")
logger.quiet("RELEASE_VERSION_SHORT: $RELEASE_VERSION_SHORT")
Expand Down Expand Up @@ -3596,6 +3662,11 @@ project(":web") {
if (t.name == "win") {
// To enable ninja build on Windows
environment(WINDOWS_NATIVE_COMPILE_ENVIRONMENT)
// Adds compiler and linker flags if present
def cFlags = webkitProperties.ccFlags?.join(' ') ?: ''
def lFlags = webkitProperties.linkFlags?.join(' ') ?: ''
cmakeArgs = "$cmakeArgs -DCMAKE_C_FLAGS='${cFlags}' -DCMAKE_CXX_FLAGS='${cFlags}'"
cmakeArgs = "$cmakeArgs -DCMAKE_SHARED_LINKER_FLAGS='${lFlags}'"
} else if (t.name == "mac") {
cmakeArgs = " $cmakeArgs -DCMAKE_OSX_DEPLOYMENT_TARGET=$MACOSX_MIN_VERSION -DCMAKE_OSX_SYSROOT=$MACOSX_SDK_PATH"
} else if (t.name == "linux") {
Expand Down Expand Up @@ -4006,6 +4077,17 @@ allprojects {
}
} // tasks with javaCompile

// Normalizes the ZIP and JAR archives
tasks.withType(Zip) {
if (sourceDateEpoch != null) {
preserveFileTimestamps = false
reproducibleFileOrder = true
doLast {
setFileTimestamps(archiveFile, buildInstant)
}
}
}

// If I am a module....
if (project.hasProperty('moduleSourcePath') &&
(project.hasProperty('buildModule') && project.buildModule)) {
Expand Down Expand Up @@ -4354,21 +4436,9 @@ compileTargets { t ->
)
}

// FIXME: do we really need the index task for this modular jar?
def javafxSwtIndexTask = task("javafxSwtIndex$t.capital") {
//the following is a workaround for the lack of indexing in gradle 1.4 through 1.7
dependsOn(javafxSwtTask)

doLast() {
def destDir = javafxSwtTask.destinationDirectory.get()
def afName = javafxSwtTask.archiveFileName.get()
ant.jar (update: true, index: true, destfile: "${destDir}/${afName}")
}
}

def sdkTask = task("sdk$t.capital") {
group = "Basic"
dependsOn(javafxSwtIndexTask)
dependsOn(javafxSwtTask)
}

sdk.dependsOn(sdkTask)
Expand Down Expand Up @@ -4515,16 +4585,17 @@ compileTargets { t ->
}
zips.dependsOn(zipsTask)

// Use native zip tool so that file permissions are preserved on Windows
def zipSdkTask = task("zipSdk$t.capital", dependsOn: publicExportsTask) {
doLast {
def outZipFile = "${bundlesDir}/${sdkBundleName}.zip"
mkdir bundlesDir
exec {
workingDir(artifactsDir)
commandLine("zip", "-q", "-r", outZipFile, sdkBundleName)
}
def zipSdkTask = task("zipSdk$t.capital", type: Zip, dependsOn: publicExportsTask) {
destinationDirectory = file("${bundlesDir}")
archiveFileName = "${sdkBundleName}.zip"
includeEmptyDirs = false
// Sets directory and file permissions in archive for Windows
if (IS_WINDOWS && IS_USE_CYGWIN) {
dirMode = 0755
fileMode = 0755
}
from sdkArtifactsDir
into "${sdkBundleName}"
}
zipsTask.dependsOn(zipSdkTask)

Expand Down Expand Up @@ -5346,8 +5417,8 @@ compileTargets { t ->
dependsOn(buildModuleGraphicsTask) // we copy to the graphics module

if (COMPILE_SWT) {
def javafxSwtIndexTask = tasks.getByName("javafxSwtIndex${t.capital}");
dependsOn(javafxSwtIndexTask)
def javafxSwtTask = tasks.getByName("javafxSwt$t.capital");
dependsOn(javafxSwtTask)
//enabled = COMPILE_SWT
}

Expand Down Expand Up @@ -5558,6 +5629,11 @@ compileTargets { t ->
}
args("--legal-notices")
args(srcLegalDir)
// https://bugs.openjdk.org/browse/JDK-8278766
def status = compareJdkVersion(jdkVersion, "19")
if (sourceDateEpoch != null && status >= 0) {
args("--date", buildTimestamp)
}
args(jmodFile)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class LinkTask extends DefaultTask {
args("$lib");
}
// Exclude parfait files (.bc)
args(objectDir.listFiles().findAll{ !it.getAbsolutePath().endsWith(".bc") });
args(objectDir.listFiles().sort().findAll{ !it.getAbsolutePath().endsWith(".bc") });
if (project.IS_WINDOWS) {
args("/out:$lib");
} else {
Expand Down
12 changes: 12 additions & 0 deletions buildSrc/win.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ if (winVsVer >= 120) {
if (IS_DEBUG_NATIVE) ccDebugFlags += "/FS"
}

// Enables reproducible builds when defined
def sourceDateEpoch = System.getenv("SOURCE_DATE_EPOCH")

// Common set of flags for all modules
def ccFlags = ["/nologo", "/W3", "/EHsc", "/c",
Expand All @@ -123,13 +125,19 @@ def ccFlags = ["/nologo", "/W3", "/EHsc", "/c",
"/I$JDK_HOME/include", "/I$JDK_HOME/include/win32",
ccDebugFlags].flatten();
if (IS_STATIC_BUILD) ccFlags.add("/DSTATIC_BUILD")
if (sourceDateEpoch != null) {
ccFlags.add("/experimental:deterministic")
}

def linkFlags = ["/nologo"]
if (!IS_STATIC_BUILD) {
linkFlags += ["/dll", "/manifest", "/opt:REF", "/incremental:no", "/dynamicbase", "/nxcompat"]
}
if (!IS_64) linkFlags.add("/safeseh");
if (IS_DEBUG_NATIVE) linkFlags.add("/debug");
if (sourceDateEpoch != null) {
linkFlags.add("/experimental:deterministic")
}

// Remove C++ static linking if not on VS2010
if (WINDOWS_VS_VER != "100") {
Expand Down Expand Up @@ -461,6 +469,10 @@ WIN.webkit.compiler = compiler
WIN.webkit.linker = linker
WIN.webkit.rcCompiler = rcCompiler
WIN.webkit.rcSource = defaultRcSource
if (sourceDateEpoch != null) {
WIN.webkit.ccFlags = ["/experimental:deterministic"].flatten()
WIN.webkit.linkFlags = ["/experimental:deterministic"].flatten()
}
WIN.webkit.rcFlags = ["/d", "JFX_FNAME=jfxwebkit.dll", "/d", "JFX_INTERNAL_NAME=webkit", rcFlags].flatten();

String getWinArch(String arch) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ CFLAGS = -DWIN32 \
-D_WINDLL \
-D_MBCS \
-DINITGUID \
$(INCLUDES) \
$(INCLUDES) \
$(COMPILER_FLAGS)

OBJECTS = $(patsubst %.cpp,$(OBJBASE_DIR)/%.obj,$(CPP_SOURCES)) $(patsubst %.c,$(OBJBASE_DIR)/%.obj,$(C_SOURCES))
Expand All @@ -113,6 +113,12 @@ else
LDFLAGS += -MACHINE:x64
endif

# Enables reproducible builds when defined
ifdef SOURCE_DATE_EPOCH
CFLAGS += -experimental:deterministic
LDFLAGS += -experimental:deterministic
endif

LIBS = $(addprefix $(BUILD_DIR)/,$(MODULES))

export BASECLASSES_DIR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ SYSTEM_LIBS = Ws2_32.lib \
shell32.lib \
advapi32.lib \
ole32.lib \
Winmm.lib
Winmm.lib

LDFLAGS = -out:$(shell cygpath -ma $(TARGET)) -nologo -incremental:no -libpath:$(shell cygpath -ma $(BUILD_DIR)) -dll $(SYSTEM_LIBS) \
-manifest -manifestfile:$(MANIFEST) -manifestuac:"level='asInvoker' uiAccess='false'" \
Expand All @@ -47,6 +47,11 @@ else
LDFLAGS += -MACHINE:x64
endif

# Enables reproducible builds when defined
ifdef SOURCE_DATE_EPOCH
LDFLAGS += -experimental:deterministic
endif

LIBS = $(addprefix $(BUILD_DIR)/,$(MODULES))

.PHONY: default list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ else
LDFLAGS += -MACHINE:x64
endif

# Enables reproducible builds when defined
ifdef SOURCE_DATE_EPOCH
LDFLAGS += -experimental:deterministic
endif

LIBS = $(addprefix $(BUILD_DIR)/,$(MODULES))

.PHONY: default $(MODULES) list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ CFLAGS = -DWIN32 \
-DGSTREAMER_LITE \
-D_WINDLL \
-D_MBCS \
$(INCLUDES) \
$(INCLUDES) \
$(CL_COMPILER_FLAGS)

ifeq ($(VS_VER), 100)
Expand Down Expand Up @@ -120,6 +120,12 @@ else
LDFLAGS += -MACHINE:x64
endif

# Enables reproducible builds when defined
ifdef SOURCE_DATE_EPOCH
CFLAGS += -experimental:deterministic
LDFLAGS += -experimental:deterministic
endif

CPP_SOURCES = \
jni/com_sun_media_jfxmedia_logging_Logger.cpp \
jni/JavaBandsHolder.cpp \
Expand Down

1 comment on commit 0d9dcf3

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.