Package: libmaven-archiver-java Version: 2.5-1 Severity: wishlist Tags: patch User: reproducible-bui...@lists.alioth.debian.org Usertags: toolchain
Hi! While working on the “reproducible builds” effort [1], we have noticed that many Java packages would not build reproducibly because Maven emits a build date in the "pom.properties" file generated by maven-archiver (as used by maven-jar-plugin) [2]. Ideally, this date would be removed. I believe that it's there by accident: * it's in a comment so it's hard (but not impossible) for people to access * it's written by java.lang.Properties, not intentionally by Maven code * it's not updated by any of the code in maven-archiver to update the file However, in order to maintain nearly total backwards compatability, the attached patch causes the original behaviour to be maintained unless a BUILD_DATE environment variable is set. The plan is for this to (eventually) be set by something like javahelper. Patch should apply to 2.5, 2.6 and trunk at time of writing (2.7-SNAPSHOT). Your thoughts would be appreciated. There are some alternative solutions being gathered on the wiki [3]. Cheers. [1]: https://wiki.debian.org/ReproducibleBuilds [2]: https://reproducible.debian.net/issues/timestamps_in_maven_pom_files_issue.html [3]: https://wiki.debian.org/ReproducibleBuilds/TimestampsInMavenPomProperties
>From d725546a99257c1fe6b03a7321142e6d564665d7 Mon Sep 17 00:00:00 2001 From: "Chris West (Faux)" <g...@goeswhere.com> Date: Fri, 9 Jan 2015 21:19:43 +0000 Subject: [PATCH] honour BUILD_DATE when generating pom.properties --- .../apache/maven/archiver/PomPropertiesUtil.java | 88 +++++++++++++++++++++- .../maven/archiver/PomPropertiesUtilTest.java | 34 +++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/apache/maven/archiver/PomPropertiesUtilTest.java diff --git a/src/main/java/org/apache/maven/archiver/PomPropertiesUtil.java b/src/main/java/org/apache/maven/archiver/PomPropertiesUtil.java index 5e0a41b..605a2f1 100644 --- a/src/main/java/org/apache/maven/archiver/PomPropertiesUtil.java +++ b/src/main/java/org/apache/maven/archiver/PomPropertiesUtil.java @@ -25,6 +25,17 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.io.BufferedWriter; +import java.text.SimpleDateFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Map; import java.util.Properties; import org.apache.maven.project.MavenProject; @@ -85,7 +96,7 @@ public class PomPropertiesUtil OutputStream os = new FileOutputStream( outputFile ); try { - properties.store( os, GENERATED_BY_MAVEN ); + storeWithCustomTimestamp(properties, os); os.close(); // stream is flushed but not closed by Properties.store() os = null; } @@ -117,4 +128,79 @@ public class PomPropertiesUtil archiver.addFile( pomPropertiesFile, "META-INF/maven/" + groupId + "/" + artifactId + "/pom.properties" ); } + + /** + * Java always writes the date in a comment. We have to reimplement the method to change the date. + */ + static void storeWithCustomTimestamp(Properties properties, OutputStream os) throws IOException { + final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "8859_1")); + try { + writer.append("#" + GENERATED_BY_MAVEN); + writer.newLine(); + writer.append("#" + computeBuildDate().toString()); + writer.newLine(); + final List<Map.Entry<Object, Object>> entries = + new ArrayList<Map.Entry<Object, Object>>(properties.entrySet()); + Collections.sort(entries, new Comparator<Map.Entry<Object, Object>>() { + public int compare(Map.Entry<Object, Object> left, Map.Entry<Object, Object> right) { + return String.valueOf(left.getKey()).compareTo(String.valueOf(right.getKey())); + } + }); + for (Map.Entry<Object, Object> property : entries) { + writer.write(property.getKey() + "=" + escapeForProperties(String.valueOf(property.getValue()))); + writer.newLine(); + } + } finally { + writer.flush(); + writer.close(); + } + } + + private static Date computeBuildDate() { + final String envVariable = System.getenv("BUILD_DATE"); + if (null != envVariable) { + try { + return new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz").parse(envVariable); + } catch (ParseException e) { + System.err.println("maven-archiver: BUILD_DATE not in recognised format"); + e.printStackTrace(); + // fall through + } + } + return new Date(); + } + + static String escapeForProperties(String input) { + final StringBuilder output = new StringBuilder(input.length()); + + for (char c : input.toCharArray()) { + switch (c) { + case '\t': + output.append('\\'); + output.append('t'); + break; + case '\n': + output.append('\\'); + output.append('n'); + break; + case '=': + case ':': + case '#': + case '!': + case '\\': + output.append('\\'); + output.append(c); + break; + default: + if (c < ' ' || c > 127) { + output.append("\\u"); + output.append(String.format("%04x", (int) c)); + } else { + output.append(c); + } + } + } + + return output.toString(); + } } diff --git a/src/test/java/org/apache/maven/archiver/PomPropertiesUtilTest.java b/src/test/java/org/apache/maven/archiver/PomPropertiesUtilTest.java new file mode 100644 index 0000000..b69321d --- /dev/null +++ b/src/test/java/org/apache/maven/archiver/PomPropertiesUtilTest.java @@ -0,0 +1,34 @@ +package org.apache.maven.archiver; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import junit.framework.TestCase; + +import static org.apache.maven.archiver.PomPropertiesUtil.escapeForProperties; + +public class PomPropertiesUtilTest extends TestCase { + public void testEscape() { + assertEquals("hello world", escapeForProperties("hello world")); + assertEquals("a \\u001f b", escapeForProperties("a \u001f b")); + assertEquals("hello\\nworld", escapeForProperties("hello\nworld")); + assertEquals("hello \\# \\! \\\\ \\: \\t \\u0100 \\u000c world", + escapeForProperties("hello # ! \\ : \t \u0100 \f world")); + } +} \ No newline at end of file -- 2.1.0