From d9704e2964c0ff6d32fa8a1261ff7c0e191cdca9 Mon Sep 17 00:00:00 2001 From: Zlika Date: Thu, 2 Jan 2025 18:42:41 +0100 Subject: [PATCH] Write Properties files in a reproducible way (cherry picked from commit 30a6a70e5374058cb602cbc2d2a59a0bb5f52e27) --- .../transport/SharedHttpCacheStorage.java | 10 ++- .../DefaultEquinoxInstallationFactory.java | 14 +---- .../org/eclipse/tycho/ReproducibleUtils.java | 54 ++++++++++++++++ .../repository/module/ModuleArtifactMap.java | 12 +--- .../tycho/p2resolver/P2GeneratorImpl.java | 12 +--- .../p2tools/MirrorApplicationServiceImpl.java | 7 +-- .../reproducible/ReproducibleBuildTest.java | 62 ++++++++++++++----- .../p2/repository/MavenP2SiteMojo.java | 5 +- ...avenCentralArtifactCoordinateResolver.java | 8 +-- .../eclipse/tycho/source/OsgiSourceMojo.java | 9 +-- .../tycho/source/SourceFeatureMojo.java | 14 +---- .../surefire/AbstractEclipseTestMojo.java | 8 +-- .../eclipse/tycho/surefire/BndTestMojo.java | 8 +-- 13 files changed, 130 insertions(+), 93 deletions(-) create mode 100644 tycho-api/src/main/java/org/eclipse/tycho/ReproducibleUtils.java diff --git a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java index 543583723e..07fa2532cc 100644 --- a/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java +++ b/p2-maven-plugin/src/main/java/org/eclipse/tycho/p2maven/transport/SharedHttpCacheStorage.java @@ -40,6 +40,7 @@ import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.Logger; import org.eclipse.equinox.internal.p2.repository.AuthenticationFailedException; +import org.eclipse.tycho.ReproducibleUtils; @Component(role = HttpCache.class) public class SharedHttpCacheStorage implements HttpCache { @@ -409,12 +410,9 @@ protected void updateHeader(Headers response, int code) throws IOException, File header.put(key, value.stream().collect(Collectors.joining(","))); } } - FileUtils.forceMkdir(file.getParentFile()); - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(headerFile))) { - // we store the header here, this might be a 404 response or (permanent) - // redirect we probably need to work with later on - header.store(out, null); - } + // we store the header here, this might be a 404 response or (permanent) + // redirect we probably need to work with later on + ReproducibleUtils.storeProperties(header, headerFile.toPath()); } private synchronized Date pareHttpDate(String input) { diff --git a/sisu-osgi/sisu-equinox-launching/src/main/java/org/eclipse/sisu/equinox/launching/internal/DefaultEquinoxInstallationFactory.java b/sisu-osgi/sisu-equinox-launching/src/main/java/org/eclipse/sisu/equinox/launching/internal/DefaultEquinoxInstallationFactory.java index a767a42df4..b372fd5dd9 100644 --- a/sisu-osgi/sisu-equinox-launching/src/main/java/org/eclipse/sisu/equinox/launching/internal/DefaultEquinoxInstallationFactory.java +++ b/sisu-osgi/sisu-equinox-launching/src/main/java/org/eclipse/sisu/equinox/launching/internal/DefaultEquinoxInstallationFactory.java @@ -12,12 +12,9 @@ *******************************************************************************/ package org.eclipse.sisu.equinox.launching.internal; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -45,6 +42,7 @@ import org.eclipse.sisu.equinox.launching.EquinoxInstallation; import org.eclipse.sisu.equinox.launching.EquinoxInstallationDescription; import org.eclipse.sisu.equinox.launching.EquinoxInstallationFactory; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.TychoConstants; import org.osgi.framework.Constants; @@ -148,12 +146,8 @@ public EquinoxInstallation createInstallation(EquinoxInstallationDescription des } File configIni = new File(location, TychoConstants.CONFIG_INI_PATH); + ReproducibleUtils.storeProperties(p, configIni.toPath()); File configurationLocation = configIni.getParentFile(); - configurationLocation.mkdirs(); - try (OutputStream fos = new BufferedOutputStream(new FileOutputStream(configIni))) { - p.store(fos, null); - } - return new DefaultEquinoxInstallation(description, location, configurationLocation); } catch (IOException e) { throw new RuntimeException("Exception creating test eclipse runtime", e); @@ -210,9 +204,7 @@ private String createDevProperties(File location, Map devEntries File file = new File(location, "dev.properties"); Properties properties = new Properties(); properties.putAll(devEntries); - try (OutputStream os = new BufferedOutputStream(new FileOutputStream(file))) { - properties.store(os, null); - } + ReproducibleUtils.storeProperties(properties, file.toPath()); return file.toURI().toURL().toExternalForm(); } diff --git a/tycho-api/src/main/java/org/eclipse/tycho/ReproducibleUtils.java b/tycho-api/src/main/java/org/eclipse/tycho/ReproducibleUtils.java new file mode 100644 index 0000000000..8cbaacf883 --- /dev/null +++ b/tycho-api/src/main/java/org/eclipse/tycho/ReproducibleUtils.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.tycho; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * Utility methods for reproducible builds. + */ +public class ReproducibleUtils { + private ReproducibleUtils() { + } + + /** + * Writes the property list to the output stream in a reproducible way. The java.util.Properties + * class writes the lines in a non-reproducible order, adds a non-reproducible timestamp and + * uses platform-dependent new line characters. + * + * @param properties + * the properties object to write to the file. + * @param file + * the file to write to. All the missing parent directories are also created. + * @throws IOException + * if writing the property list to the specified output stream throws an + * IOException. + */ + public static void storeProperties(Properties properties, Path file) throws IOException { + final Path folder = file.getParent(); + if (folder != null) { + Files.createDirectories(folder); + } + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + properties.store(baos, null); + final String content = baos.toString(StandardCharsets.ISO_8859_1).lines().filter(line -> !line.startsWith("#")) + .sorted().collect(Collectors.joining("\n", "", "\n")); + try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(file))) { + os.write(content.getBytes(StandardCharsets.ISO_8859_1)); + } + } +} diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2/repository/module/ModuleArtifactMap.java b/tycho-core/src/main/java/org/eclipse/tycho/p2/repository/module/ModuleArtifactMap.java index f4a78de6ca..4abd0e0b1e 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2/repository/module/ModuleArtifactMap.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2/repository/module/ModuleArtifactMap.java @@ -14,12 +14,9 @@ import static org.eclipse.tycho.p2.repository.BundleConstants.BUNDLE_ID; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -29,6 +26,7 @@ import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.equinox.p2.core.ProvisionException; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.TychoConstants; import org.eclipse.tycho.p2.repository.MavenRepositoryCoordinates; import org.eclipse.tycho.p2.repository.RepositoryReader; @@ -151,7 +149,7 @@ private void store() throws ProvisionException { } try { - writeProperties(outputProperties, mapFile); + ReproducibleUtils.storeProperties(outputProperties, mapFile.toPath()); } catch (IOException e) { String message = "I/O error while writing repository to " + mapFile; int code = ProvisionException.REPOSITORY_FAILED_WRITE; @@ -160,10 +158,4 @@ private void store() throws ProvisionException { } } - - private static void writeProperties(Properties properties, File outputFile) throws IOException { - try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFile))) { - properties.store(outputStream, null); - } - } } diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2GeneratorImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2GeneratorImpl.java index 5b09893faf..915b649484 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2GeneratorImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/P2GeneratorImpl.java @@ -13,12 +13,9 @@ *******************************************************************************/ package org.eclipse.tycho.p2resolver; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -69,6 +66,7 @@ import org.eclipse.tycho.OptionalResolutionAction; import org.eclipse.tycho.PackagingType; import org.eclipse.tycho.ReactorProject; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.TargetEnvironment; import org.eclipse.tycho.TychoConstants; import org.eclipse.tycho.core.osgitools.BundleReader; @@ -483,13 +481,7 @@ static void writeArtifactLocations(File outputFile, Map artifactLo } } - writeProperties(outputProperties, outputFile); - } - - private static void writeProperties(Properties properties, File outputFile) throws IOException { - try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFile))) { - properties.store(outputStream, null); - } + ReproducibleUtils.storeProperties(outputProperties, outputFile.toPath()); } /** diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2tools/MirrorApplicationServiceImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2tools/MirrorApplicationServiceImpl.java index e9565dac03..bb1931545a 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2tools/MirrorApplicationServiceImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2tools/MirrorApplicationServiceImpl.java @@ -17,7 +17,6 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URLConnection; @@ -72,6 +71,7 @@ import org.eclipse.tycho.ArtifactType; import org.eclipse.tycho.BuildDirectory; import org.eclipse.tycho.DependencySeed; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.core.shared.StatusTool; import org.eclipse.tycho.p2.repository.GAV; import org.eclipse.tycho.p2.repository.RepositoryLayoutHelper; @@ -523,9 +523,8 @@ private void writeP2Index(File repositoryDestination) throws FacadeException { properties.setProperty("version", "1"); properties.setProperty("artifact.repository.factory.order", "artifacts.xml,!"); properties.setProperty("metadata.repository.factory.order", "content.xml,!"); - try (OutputStream stream = new BufferedOutputStream( - new FileOutputStream(new File(repositoryDestination, P2_INDEX_FILE)))) { - properties.store(stream, null); + try { + ReproducibleUtils.storeProperties(properties, new File(repositoryDestination, P2_INDEX_FILE).toPath()); } catch (IOException e) { throw new FacadeException("writing index file failed", e); } diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/reproducible/ReproducibleBuildTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/reproducible/ReproducibleBuildTest.java index 81563aa4cd..8b5770ad5c 100644 --- a/tycho-its/src/test/java/org/eclipse/tycho/test/reproducible/ReproducibleBuildTest.java +++ b/tycho-its/src/test/java/org/eclipse/tycho/test/reproducible/ReproducibleBuildTest.java @@ -9,6 +9,7 @@ package org.eclipse.tycho.test.reproducible; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -25,6 +26,9 @@ import org.junit.Assert; import org.junit.Test; +/** + * Tests that the build artifacts produced by Tycho are reproducible. + */ public class ReproducibleBuildTest extends AbstractTychoIntegrationTest { // The ZipEntry.getLastModifiedTime() method uses the default timezone to // convert date and time fields to Instant, so we also use the default timezone @@ -32,19 +36,30 @@ public class ReproducibleBuildTest extends AbstractTychoIntegrationTest { private static final String EXPECTED_TIMESTAMP_STRING = "2023-01-01T00:00:00"; private static final Instant EXPECTED_TIMESTAMP_INSTANT = LocalDateTime.parse(EXPECTED_TIMESTAMP_STRING) .toInstant(OffsetDateTime.now().getOffset()); + Verifier verifier; /** - * Check that the build is reproducible. + * Run the maven integration tests related to reproducible builds. + * + * @throws Exception */ @Test - public void test() throws Exception { - Verifier verifier = getVerifier("reproducible-build"); + public void testReproducible() throws Exception { + verifier = getVerifier("reproducible-build"); verifier.executeGoals(List.of("clean", "verify")); verifier.verifyErrorFreeLog(); - // Check that the timestamp of the files inside the produced archives is equal - // to the one specified in the "project.build.outputTimestamp" property of the - // pom file. + checkArchiveTimestamps(); + testBuildQualifier(); + testPropertiesFiles(); + } + + /** + * Checks that the timestamp of the files inside the produced archives is equal + * to the one specified in the "project.build.outputTimestamp" property of the + * pom file. + */ + private void checkArchiveTimestamps() throws Exception { checkTimestamps(verifier.getBasedir() + "/reproducible.bundle/target/reproducible.bundle-1.0.0.jar"); checkTimestamps(verifier.getBasedir() + "/reproducible.bundle/target/reproducible.bundle-1.0.0-attached.jar"); checkTimestamps(verifier.getBasedir() + "/reproducible.bundle/target/reproducible.bundle-1.0.0-sources.jar"); @@ -55,11 +70,8 @@ public void test() throws Exception { checkTimestamps(verifier.getBasedir() + "/reproducible.iu/target/reproducible.iu-1.0.0.zip"); checkTimestamps(verifier.getBasedir() + "/reproducible.repository/target/reproducible.repository-1.0.0.zip"); checkTimestamps(verifier.getBasedir() + "/reproducible.repository/target/p2-site.zip"); - - // Check that the build qualifier uses the timestamp specified in the - // "project.build.outputTimestamp" property of the pom file. - checkBuildQualifier(verifier.getBasedir() - + "/reproducible.buildqualifier/target/reproducible.buildqualifier-1.0.0-SNAPSHOT.jar"); + checkTimestamps( + verifier.getBasedir() + "/reproducible.repository/target/products/main.product.id-linux.gtk.x86.zip"); } private void checkTimestamps(String file) throws IOException { @@ -72,11 +84,33 @@ private void checkTimestamps(String file) throws IOException { } } - private void checkBuildQualifier(String file) throws IOException { + /** + * Checks that the build qualifier uses the timestamp specified in the + * "project.build.outputTimestamp" property of the pom file. + * + * @throws IOException + */ + private void testBuildQualifier() throws IOException { + final String file = verifier.getBasedir() + + "/reproducible.buildqualifier/target/reproducible.buildqualifier-1.0.0-SNAPSHOT.jar"; try (FileSystem fileSystem = FileSystems.newFileSystem(Path.of(file))) { - Path manifest = fileSystem.getPath("META-INF/MANIFEST.MF"); - List lines = Files.readAllLines(manifest); + final Path manifest = fileSystem.getPath("META-INF/MANIFEST.MF"); + final List lines = Files.readAllLines(manifest); Assert.assertTrue(lines.stream().anyMatch(l -> l.equals("Bundle-Version: 1.0.0.202301010000"))); } } + + /** + * Checks that the generated properties files are reproducible. + * + * @throws IOException + */ + private void testPropertiesFiles() throws IOException { + final String file = verifier.getBasedir() + "/reproducible.bundle/target/reproducible.bundle-1.0.0-sources.jar"; + try (FileSystem fileSystem = FileSystems.newFileSystem(Path.of(file))) { + final Path propFile = fileSystem.getPath("OSGI-INF/l10n/bundle-src.properties"); + final String content = Files.readString(propFile, StandardCharsets.ISO_8859_1); + Assert.assertEquals("bundleName=Reproducible-bundle Source\n" + "bundleVendor=unknown\n", content); + } + } } diff --git a/tycho-p2-repository-plugin/src/main/java/org/eclipse/tycho/plugins/p2/repository/MavenP2SiteMojo.java b/tycho-p2-repository-plugin/src/main/java/org/eclipse/tycho/plugins/p2/repository/MavenP2SiteMojo.java index ba05443d6c..efe6893f3d 100644 --- a/tycho-p2-repository-plugin/src/main/java/org/eclipse/tycho/plugins/p2/repository/MavenP2SiteMojo.java +++ b/tycho-p2-repository-plugin/src/main/java/org/eclipse/tycho/plugins/p2/repository/MavenP2SiteMojo.java @@ -72,6 +72,7 @@ import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; import org.eclipse.tycho.PackagingType; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.TychoConstants; import org.eclipse.tycho.core.PGPService; import org.eclipse.tycho.p2maven.tools.TychoFeaturesAndBundlesPublisherApplication; @@ -484,9 +485,7 @@ protected File createMavenAdvice(Artifact artifact) throws MojoExecutionExceptio addProvidesAndProperty(properties, TychoConstants.PROP_EXTENSION, artifact.getType(), cnt++); addProvidesAndProperty(properties, TychoConstants.PROP_CLASSIFIER, artifact.getClassifier(), cnt++); addProvidesAndProperty(properties, "maven-scope", artifact.getScope(), cnt++); - try (OutputStream os = new BufferedOutputStream(new FileOutputStream(p2))) { - properties.store(os, null); - } + ReproducibleUtils.storeProperties(properties, p2.toPath()); return p2; } catch (IOException e) { throw new MojoExecutionException("failed to generate p2.inf", e); diff --git a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/reverseresolve/MavenCentralArtifactCoordinateResolver.java b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/reverseresolve/MavenCentralArtifactCoordinateResolver.java index c1bae1b283..8aecb84dfb 100644 --- a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/reverseresolve/MavenCentralArtifactCoordinateResolver.java +++ b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/reverseresolve/MavenCentralArtifactCoordinateResolver.java @@ -12,13 +12,10 @@ *******************************************************************************/ package org.eclipse.tycho.packaging.reverseresolve; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.file.Files; import java.security.MessageDigest; import java.util.List; @@ -36,6 +33,7 @@ import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.Logger; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.core.shared.MavenContext; import org.eclipse.tycho.p2.repository.GAV; import org.eclipse.tycho.p2.repository.RepositoryLayoutHelper; @@ -160,9 +158,7 @@ private void cacheResult(File cacheFile, Dependency dependency) { properties.setProperty(KEY_ARTIFACT_ID, dependency.getArtifactId()); properties.setProperty(KEY_VERSION, dependency.getVersion()); properties.setProperty(KEY_TYPE, dependency.getType()); - try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(cacheFile))) { - properties.store(stream, null); - } + ReproducibleUtils.storeProperties(properties, cacheFile.toPath()); } catch (IOException e) { // can't create cache file then... } diff --git a/tycho-source-plugin/src/main/java/org/eclipse/tycho/source/OsgiSourceMojo.java b/tycho-source-plugin/src/main/java/org/eclipse/tycho/source/OsgiSourceMojo.java index 7fc40f9717..bad7cd8253 100644 --- a/tycho-source-plugin/src/main/java/org/eclipse/tycho/source/OsgiSourceMojo.java +++ b/tycho-source-plugin/src/main/java/org/eclipse/tycho/source/OsgiSourceMojo.java @@ -20,12 +20,9 @@ import static org.osgi.framework.Constants.BUNDLE_VENDOR; import static org.osgi.framework.Constants.BUNDLE_VERSION; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -58,6 +55,7 @@ import org.eclipse.tycho.BuildPropertiesParser; import org.eclipse.tycho.PackagingType; import org.eclipse.tycho.ReactorProject; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.TychoProperties; import org.eclipse.tycho.core.TychoProject; import org.eclipse.tycho.core.osgitools.BundleReader; @@ -276,9 +274,8 @@ static Resource generateL10nFile(MavenProject project, Path basedir, UnaryOperat sourceL10nProps.setProperty(I18N_KEY_BUNDLE_NAME, sourceBundleName); sourceL10nProps.setProperty(I18N_KEY_BUNDLE_VENDOR, bundleVendor); File l10nPropsFile = new File(l10nOutputDir, MANIFEST_BUNDLE_LOCALIZATION_FILENAME); - l10nPropsFile.getParentFile().mkdirs(); - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(l10nPropsFile))) { - sourceL10nProps.store(out, "Source Bundle Localization"); + try { + ReproducibleUtils.storeProperties(sourceL10nProps, l10nPropsFile.toPath()); } catch (IOException e) { throw new MojoExecutionException("error while generating source bundle localization file", e); } diff --git a/tycho-source-plugin/src/main/java/org/eclipse/tycho/source/SourceFeatureMojo.java b/tycho-source-plugin/src/main/java/org/eclipse/tycho/source/SourceFeatureMojo.java index 718dcee81b..72aaa7d844 100644 --- a/tycho-source-plugin/src/main/java/org/eclipse/tycho/source/SourceFeatureMojo.java +++ b/tycho-source-plugin/src/main/java/org/eclipse/tycho/source/SourceFeatureMojo.java @@ -14,12 +14,9 @@ *******************************************************************************/ package org.eclipse.tycho.source; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -54,6 +51,7 @@ import org.eclipse.tycho.BuildProperties; import org.eclipse.tycho.BuildPropertiesParser; import org.eclipse.tycho.PackagingType; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.TargetEnvironment; import org.eclipse.tycho.TargetPlatform; import org.eclipse.tycho.TychoConstants; @@ -249,7 +247,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { Properties sourceFeatureTemplateProps = readSourceTemplateFeatureProperties(); Properties mergedSourceFeatureProps = mergeFeatureProperties(sourceFeatureTemplateProps); File sourceFeatureXml = generateSourceFeatureXml(mergedSourceFeatureProps, sourceFeatureTemplateProps); - writeProperties(mergedSourceFeatureProps, getMergedSourceFeaturePropertiesFile()); + ReproducibleUtils.storeProperties(mergedSourceFeatureProps, + getMergedSourceFeaturePropertiesFile().toPath()); MavenArchiver archiver = new MavenArchiver(); archiver.setArchiver(jarArchiver); // configure for Reproducible Builds based on outputTimestamp value @@ -359,13 +358,6 @@ private static Properties readPropertiesIfExists(File propertiesFile) throws IOE return properties; } - private static void writeProperties(Properties props, File propertiesFile) throws IOException { - propertiesFile.getParentFile().mkdirs(); - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(propertiesFile))) { - props.store(out, ""); - } - } - /** * This only create the new feature skeleton by setting labels and other not-structural values * that don't require platform resolution. diff --git a/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/AbstractEclipseTestMojo.java b/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/AbstractEclipseTestMojo.java index ce2bda839d..67363571a9 100644 --- a/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/AbstractEclipseTestMojo.java +++ b/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/AbstractEclipseTestMojo.java @@ -19,11 +19,8 @@ ******************************************************************************/ package org.eclipse.tycho.surefire; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -81,6 +78,7 @@ import org.eclipse.tycho.OptionalResolutionAction; import org.eclipse.tycho.PlatformPropertiesUtils; import org.eclipse.tycho.ReactorProject; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.TargetEnvironment; import org.eclipse.tycho.TychoConstants; import org.eclipse.tycho.core.BundleProject; @@ -979,9 +977,7 @@ private void storeProperties(Map propertiesMap, File file) throw Properties p = new Properties(); p.putAll(propertiesMap); try { - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { - p.store(out, null); - } + ReproducibleUtils.storeProperties(p, file.toPath()); } catch (IOException e) { throw new MojoExecutionException("Can't write test launcher properties file", e); } diff --git a/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/BndTestMojo.java b/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/BndTestMojo.java index 0ea221bc00..89fc8fd40f 100644 --- a/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/BndTestMojo.java +++ b/tycho-surefire/tycho-surefire-plugin/src/main/java/org/eclipse/tycho/surefire/BndTestMojo.java @@ -12,10 +12,7 @@ ******************************************************************************/ package org.eclipse.tycho.surefire; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; @@ -49,6 +46,7 @@ import org.eclipse.tycho.IllegalArtifactReferenceException; import org.eclipse.tycho.MavenArtifactKey; import org.eclipse.tycho.PackagingType; +import org.eclipse.tycho.ReproducibleUtils; import org.eclipse.tycho.ResolvedArtifactKey; import org.eclipse.tycho.TargetPlatform; import org.eclipse.tycho.TychoConstants; @@ -232,9 +230,7 @@ protected void runTests(ScanResult scanResult) throws MojoExecutionException, Mo properties.setProperty(Constants.RUNFW, runfw); properties.setProperty(Constants.RUNPROPERTIES, buildRunProperties()); try { - try (OutputStream out = new BufferedOutputStream(new FileOutputStream(runfile))) { - properties.store(out, null); - } + ReproducibleUtils.storeProperties(properties, runfile.toPath()); String javaExecutable = getJavaExecutable(); int returncode = container.execute(runfile, "testing", work, (file, bndrun, run) -> { if (new File(javaExecutable).isFile()) {