https://bz.apache.org/bugzilla/show_bug.cgi?id=61079master
@@ -41,10 +41,12 @@ to indicate, for example, the release date. The best place for this task is | |||
probably in an initialization target.</p> | |||
<p><em>Since Ant 1.10.2</em> the magic | |||
property <code>ant.tstamp.now</code> can be used to specify a fixed | |||
date value in order to create reproducible builds. Its value must be | |||
a number and is interpreted as seconds since the epoch (midnight | |||
1970-01-01).</p> | |||
property <code>ant.tstamp.now</code> can be used to specify a fixed | |||
date value in order to create reproducible builds. Its value must be | |||
a number and is interpreted as seconds since the epoch (midnight | |||
1970-01-01). With <code>ant.tstamp.now.iso</code> you could also specify that | |||
value in ISO-8601 format (<code>1972-04-17T08:07:00Z</code>). If you specify a value | |||
in an invalid format an INFO message will be logged and the value will be ignored.</p> | |||
<h3>Parameters</h3> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
@@ -490,6 +490,10 @@ org.apache.tools.ant.Executor implementation specified here. | |||
<td>number, seconds since the epoch (midnight 1970-01-01)</td> | |||
<td>The value to use as current time and date for <tstamp></td> | |||
</tr> | |||
<tr> | |||
<td><code>ant.tstamp.now.iso</code></td> | |||
<td>ISO-8601 timestamp string like <code>1972-04-17T08:07:00Z</code></td> | |||
</tr> | |||
</table> | |||
<p> | |||
@@ -308,5 +308,18 @@ public final class MagicNames { | |||
* @since Ant 1.10.2 | |||
*/ | |||
public static final String TSTAMP_NOW = "ant.tstamp.now"; | |||
/** | |||
* Magic property that can be set to contain a value for tstamp's | |||
* "now" in order to make builds that use the task create | |||
* reproducible results. | |||
* | |||
* <p>The value is expected to be in ISO time format | |||
* (<i>1972-04-17T08:07</i>)</p> | |||
* | |||
* Value: {@value} | |||
* @since Ant 1.10.2 | |||
*/ | |||
public static final String TSTAMP_NOW_ISO = "ant.tstamp.now.iso"; | |||
} | |||
@@ -19,6 +19,7 @@ | |||
package org.apache.tools.ant.taskdefs; | |||
import java.text.SimpleDateFormat; | |||
import java.time.Instant; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
@@ -26,9 +27,12 @@ import java.util.List; | |||
import java.util.Locale; | |||
import java.util.Map; | |||
import java.util.NoSuchElementException; | |||
import java.util.Optional; | |||
import java.util.StringTokenizer; | |||
import java.util.TimeZone; | |||
import java.util.Vector; | |||
import java.util.function.BiFunction; | |||
import java.util.function.Function; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.Location; | |||
@@ -111,16 +115,45 @@ public class Tstamp extends Task { | |||
* Return the {@link Date} instance to use as base for DSTAMP, TSTAMP and TODAY. | |||
*/ | |||
protected Date getNow() { | |||
String magicNow = getProject().getProperty(MagicNames.TSTAMP_NOW); | |||
if (magicNow != null && magicNow.length() > 0) { | |||
Optional<Date> now = getNow( | |||
MagicNames.TSTAMP_NOW_ISO, | |||
s -> Date.from(Instant.parse(s)), | |||
(k, v) -> "magic property " + k + " ignored as '" + v + "' is not in valid ISO pattern" | |||
); | |||
if (now.isPresent()) { | |||
return now.get(); | |||
} | |||
now = getNow( | |||
MagicNames.TSTAMP_NOW, | |||
s -> new Date(1000 * Long.parseLong(s)), | |||
(k, v) -> "magic property " + k + " ignored as " + v + " is not a valid number" | |||
); | |||
if (now.isPresent()) { | |||
return now.get(); | |||
} | |||
return new Date(); | |||
} | |||
/** | |||
* Checks and returns a Date if the specified property is set. | |||
* @param propertyName name of the property to check | |||
* @param map convertion of the property value as string to Date | |||
* @param log supplier of the log message containg the property name and value if | |||
* the convertion fails | |||
* @return Optional containing the Date or null | |||
*/ | |||
protected Optional<Date> getNow(String propertyName, Function<String, Date> map, BiFunction<String, String, String> log) { | |||
String property = getProject().getProperty(propertyName); | |||
if (property != null && property.length() > 0) { | |||
try { | |||
return new Date(1000 * Long.parseLong(magicNow)); | |||
} catch (NumberFormatException ex) { | |||
log("magic property " + MagicNames.TSTAMP_NOW + " ignored as " | |||
+ magicNow + " is not a valid number"); | |||
return Optional.ofNullable(map.apply(property)); | |||
} catch (Exception e) { | |||
log(log.apply(propertyName, property)); | |||
} | |||
} | |||
return new Date(); | |||
return Optional.empty(); | |||
} | |||
/** | |||
@@ -24,4 +24,21 @@ | |||
<tstamp/> | |||
<au:assertPropertyEquals name="DSTAMP" value="19700102"/> | |||
</target> | |||
<target name="testMagicPropertyIso"> | |||
<local name="ant.tstamp.now.iso"/> | |||
<property name="ant.tstamp.now.iso" value="1972-04-17T08:07:00Z"/> | |||
<tstamp/> | |||
<au:assertPropertyEquals name="DSTAMP" value="19720417"/> | |||
</target> | |||
<target name="testMagicPropertyBoth"> | |||
<local name="ant.tstamp.now"/> | |||
<local name="ant.tstamp.now.iso"/> | |||
<property name="ant.tstamp.now" value="100000"/> | |||
<property name="ant.tstamp.now.iso" value="1972-04-17T08:07:22Z"/> | |||
<tstamp/> | |||
<!-- 'iso' overrides 'simple' --> | |||
<au:assertPropertyEquals name="DSTAMP" value="19720417"/> | |||
</target> | |||
</project> |