This is an automated email from the ASF dual-hosted git repository.
davydotcom pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/grails-core.git
The following commit(s) were added to refs/heads/7.0.x by this push:
new 3e8010fd5a reproducible builds - prevent property files from writing
an arbitrary date if SOURCE_DATE_EPOCH is defined (#14675)
3e8010fd5a is described below
commit 3e8010fd5ab0f0dc9b516b52158581248f687428
Author: James Daugherty <[email protected]>
AuthorDate: Mon Apr 28 09:30:00 2025 -0400
reproducible builds - prevent property files from writing an arbitrary date
if SOURCE_DATE_EPOCH is defined (#14675)
---
.../GlobalGrailsClassInjectorTransformation.groovy | 120 +++++++++++++--------
1 file changed, 73 insertions(+), 47 deletions(-)
diff --git
a/grails-core/src/main/groovy/org/grails/compiler/injection/GlobalGrailsClassInjectorTransformation.groovy
b/grails-core/src/main/groovy/org/grails/compiler/injection/GlobalGrailsClassInjectorTransformation.groovy
index ef6cb5d085..f186987f6b 100644
---
a/grails-core/src/main/groovy/org/grails/compiler/injection/GlobalGrailsClassInjectorTransformation.groovy
+++
b/grails-core/src/main/groovy/org/grails/compiler/injection/GlobalGrailsClassInjectorTransformation.groovy
@@ -28,12 +28,17 @@ import grails.util.GrailsNameUtils
import groovy.transform.CompilationUnitAware
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
-import groovy.xml.slurpersupport.GPathResult
-import groovy.xml.XmlSlurper
import groovy.xml.MarkupBuilder
import groovy.xml.StreamingMarkupBuilder
+import groovy.xml.XmlSlurper
+import groovy.xml.slurpersupport.GPathResult
import org.apache.grails.common.compiler.GroovyTransformOrder
-import org.codehaus.groovy.ast.*
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.ast.AnnotationNode
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.ModuleNode
+import org.codehaus.groovy.ast.PropertyNode
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.control.CompilationUnit
import org.codehaus.groovy.control.CompilePhase
@@ -47,6 +52,8 @@ import org.grails.io.support.GrailsResourceUtils
import org.grails.io.support.UrlResource
import java.lang.reflect.Modifier
+import java.nio.charset.StandardCharsets
+import java.time.Instant
/**
* A global transformation that applies Grails' transformations to classes
within a Grails project
@@ -54,7 +61,7 @@ import java.lang.reflect.Modifier
* @author Graeme Rocher
* @since 3.0
*/
-@GroovyASTTransformation(phase= CompilePhase.CANONICALIZATION)
+@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
@CompileStatic
class GlobalGrailsClassInjectorTransformation implements ASTTransformation,
CompilationUnitAware, TransformWithPriority {
@@ -75,8 +82,8 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
URL url = GrailsASTUtils.getSourceUrl(source);
- if(url == null ) return
- if(!GrailsResourceUtils.isProjectSource(new UrlResource(url))) return;
+ if (url == null) return
+ if (!GrailsResourceUtils.isProjectSource(new UrlResource(url))) return;
List<ArtefactHandler> artefactHandlers =
GrailsFactoriesLoader.loadFactories(ArtefactHandler)
ClassInjector[] classInjectors =
GrailsAwareInjectionOperation.getClassInjectors()
@@ -94,7 +101,7 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
for (ClassNode classNode : classes) {
def projectName = classNode.getNodeMetaData("projectName")
def projectVersion = classNode.getNodeMetaData("projectVersion")
- if(projectVersion == null) {
+ if (projectVersion == null) {
projectVersion =
getClass().getPackage().getImplementationVersion()
}
@@ -102,46 +109,46 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
def classNodeName = classNode.name
- if(classNodeName.endsWith("GrailsPlugin") &&
!classNode.isAbstract()) {
+ if (classNodeName.endsWith("GrailsPlugin") &&
!classNode.isAbstract()) {
pluginClassNode = classNode
- if(!classNode.getProperty('version')) {
- classNode.addProperty(new PropertyNode('version',
Modifier.PUBLIC, ClassHelper.make(Object), classNode, new
ConstantExpression(projectVersion.toString()) , null, null))
+ if (!classNode.getProperty('version')) {
+ classNode.addProperty(new PropertyNode('version',
Modifier.PUBLIC, ClassHelper.make(Object), classNode, new
ConstantExpression(projectVersion.toString()), null, null))
}
continue
}
- if(updateGrailsFactoriesWithType(classNode,
ARTEFACT_HANDLER_CLASS, compilationTargetDirectory)) {
+ if (updateGrailsFactoriesWithType(classNode,
ARTEFACT_HANDLER_CLASS, compilationTargetDirectory)) {
continue
}
- if(updateGrailsFactoriesWithType(classNode,
APPLICATION_CONTEXT_COMMAND_CLASS, compilationTargetDirectory)) {
+ if (updateGrailsFactoriesWithType(classNode,
APPLICATION_CONTEXT_COMMAND_CLASS, compilationTargetDirectory)) {
continue
}
- if(updateGrailsFactoriesWithType(classNode, TRAIT_INJECTOR_CLASS,
compilationTargetDirectory)) {
+ if (updateGrailsFactoriesWithType(classNode, TRAIT_INJECTOR_CLASS,
compilationTargetDirectory)) {
continue
}
- if(!GrailsResourceUtils.isGrailsResource(new UrlResource(url)))
continue;
+ if (!GrailsResourceUtils.isGrailsResource(new UrlResource(url)))
continue;
- if(projectName && projectVersion) {
- GrailsASTUtils.addAnnotationOrGetExisting(classNode,
GrailsPlugin, [name:
GrailsNameUtils.getPropertyNameForLowerCaseHyphenSeparatedName(projectName.toString()),
version:projectVersion])
+ if (projectName && projectVersion) {
+ GrailsASTUtils.addAnnotationOrGetExisting(classNode,
GrailsPlugin, [name:
GrailsNameUtils.getPropertyNameForLowerCaseHyphenSeparatedName(projectName.toString()),
version: projectVersion])
}
classNode.getModule().addImport("Autowired",
ClassHelper.make("org.springframework.beans.factory.annotation.Autowired"))
- for(ArtefactHandler handler in artefactHandlers) {
- if(handler.isArtefact(classNode)) {
- if(!classNode.getAnnotations(ARTEFACT_CLASS_NODE)) {
+ for (ArtefactHandler handler in artefactHandlers) {
+ if (handler.isArtefact(classNode)) {
+ if (!classNode.getAnnotations(ARTEFACT_CLASS_NODE)) {
transformedClasses.add classNodeName
def annotationNode = new AnnotationNode(new
ClassNode(Artefact.class))
annotationNode.addMember("value", new
ConstantExpression(handler.getType()))
classNode.addAnnotation(annotationNode)
List<ClassInjector> injectors = cache[handler.type]
- for (ClassInjector injector: injectors) {
+ for (ClassInjector injector : injectors) {
if (injector instanceof CompilationUnitAware) {
-
((CompilationUnitAware)injector).compilationUnit = compilationUnit
+ ((CompilationUnitAware)
injector).compilationUnit = compilationUnit
}
}
ArtefactTypeAstTransformation.performInjection(source,
classNode, injectors)
@@ -150,10 +157,10 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
}
}
- if(!transformedClasses.contains(classNodeName)) {
+ if (!transformedClasses.contains(classNodeName)) {
def globalClassInjectors =
GrailsAwareInjectionOperation.globalClassInjectors
- for(ClassInjector injector in globalClassInjectors) {
+ for (ClassInjector injector in globalClassInjectors) {
injector.performInjection(source, classNode)
}
}
@@ -168,12 +175,12 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
static File resolveCompilationTargetDirectory(SourceUnit source) {
File targetDirectory = null
- if(source.getClass().name ==
'org.codehaus.jdt.groovy.control.EclipseSourceUnit') {
+ if (source.getClass().name ==
'org.codehaus.jdt.groovy.control.EclipseSourceUnit') {
targetDirectory =
GroovyEclipseCompilationHelper.resolveEclipseCompilationTargetDirectory(source)
} else {
- targetDirectory = source.configuration.targetDirectory
- }
- if(targetDirectory == null) {
+ targetDirectory = source.configuration.targetDirectory
+ }
+ if (targetDirectory == null) {
targetDirectory = new File('build/classes/main')
}
return targetDirectory
@@ -181,7 +188,7 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
static boolean updateGrailsFactoriesWithType(ClassNode classNode,
ClassNode superType, File compilationTargetDirectory) {
if (GrailsASTUtils.isSubclassOfOrImplementsInterface(classNode,
superType)) {
- if(Modifier.isAbstract(classNode.getModifiers())) return false
+ if (Modifier.isAbstract(classNode.getModifiers())) return false
def classNodeName = classNode.name
def props = new Properties()
@@ -200,20 +207,39 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
addToProps(props, superTypeName, classNodeName)
- factoriesFile.withWriter { Writer writer ->
+ factoriesFile.withWriter { Writer writer ->
props.store(writer, "Grails Factories File")
}
+
+ makeFactoriesFileReproducible(factoriesFile)
+
return true
}
return false
}
+ private static void makeFactoriesFileReproducible(File factoriesFile) {
+ String sourceDateEpoch = System.getenv('SOURCE_DATE_EPOCH')
+ if (!sourceDateEpoch) {
+ return
+ }
+
+ Instant buildInstant = Instant.ofEpochSecond(sourceDateEpoch as Long)
+ List<String> lines =
factoriesFile.readLines(StandardCharsets.ISO_8859_1.name())
+ lines[1] = "# ${Date.from(buildInstant).toString()}" as String
+ factoriesFile.withWriter { BufferedWriter writer ->
+ lines.each { String line ->
+ writer.writeLine(line)
+ }
+ }
+ }
+
private static void loadFromFile(Properties props, File factoriesFile) {
if (factoriesFile.exists()) {
Properties fileProps = new Properties()
factoriesFile.withInputStream { InputStream input ->
fileProps.load(input)
- fileProps.each { Map.Entry prop->
+ fileProps.each { Map.Entry prop ->
addToProps(props, (String) prop.key, (String) prop.value)
}
}
@@ -260,7 +286,7 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
}
} else if (pluginXmlExists) {
// if the class isn't the *GrailsPlugin class then only update the
plugin.xml if it already exists
- updatePluginXml(null, pluginVersion, pluginXmlFile, pluginClasses)
+ updatePluginXml(null, pluginVersion, pluginXmlFile, pluginClasses)
} else {
// otherwise add it to a list of pending classes to populated when
the plugin.xml is created
pendingPluginClasses.addAll(transformedClasses)
@@ -269,35 +295,35 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
@CompileDynamic
static void writePluginXml(ClassNode pluginClassNode, String
pluginVersion, File pluginXml, Collection<String> artefactClasses) {
- if(pluginClassNode) {
+ if (pluginClassNode) {
PluginAstReader pluginAstReader = new PluginAstReader()
def info = pluginAstReader.readPluginInfo(pluginClassNode)
- pluginXml.withWriter( "UTF-8") { Writer writer ->
+ pluginXml.withWriter("UTF-8") { Writer writer ->
def mkp = new MarkupBuilder(writer)
def pluginName =
GrailsNameUtils.getLogicalPropertyName(pluginClassNode.name, "GrailsPlugin")
def pluginProperties = info.getProperties()
def excludes = pluginProperties.get('pluginExcludes')
- if(excludes instanceof List) {
+ if (excludes instanceof List) {
pluginExcludes.clear()
pluginExcludes.addAll(excludes)
}
def grailsVersion = pluginProperties['grailsVersion'] ?:
getClass().getPackage().getImplementationVersion() + " > *"
- mkp.plugin(name:pluginName, version: pluginVersion,
grailsVersion: grailsVersion) {
+ mkp.plugin(name: pluginName, version: pluginVersion,
grailsVersion: grailsVersion) {
type(pluginClassNode.name)
- for(entry in pluginProperties) {
+ for (entry in pluginProperties) {
delegate."$entry.key"(entry.value)
}
// if there are pending classes to add to the plugin.xml
add those
- if(artefactClasses) {
+ if (artefactClasses) {
def antPathMatcher = new AntPathMatcher()
resources {
- for(String cn in artefactClasses) {
- if (!pluginExcludes.any() { String exc ->
antPathMatcher.match(exc, cn.replace('.','/')) }) {
+ for (String cn in artefactClasses) {
+ if (!pluginExcludes.any() { String exc ->
antPathMatcher.match(exc, cn.replace('.', '/')) }) {
resource cn
}
}
@@ -312,13 +338,13 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
@CompileDynamic
static void updatePluginXml(ClassNode pluginClassNode, String
pluginVersion, File pluginXmlFile, Collection<String> artefactClasses) {
- if(!artefactClasses) return
+ if (!artefactClasses) return
try {
XmlSlurper xmlSlurper = IOUtils.createXmlSlurper()
def pluginXml = xmlSlurper.parse(pluginXmlFile)
- if(pluginClassNode) {
+ if (pluginClassNode) {
def pluginName =
GrailsNameUtils.getLogicalPropertyName(pluginClassNode.name, "GrailsPlugin")
pluginXml.@name = pluginName
pluginXml.@version = pluginVersion
@@ -330,12 +356,12 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
def pluginProperties = info.getProperties()
def grailsVersion = pluginProperties['grailsVersion'] ?:
getClass().getPackage().getImplementationVersion() + " > *"
pluginXml.@grailsVersion = grailsVersion
- for(entry in pluginProperties) {
+ for (entry in pluginProperties) {
pluginXml."$entry.key" = entry.value
}
def excludes = pluginProperties.get('pluginExcludes')
- if(excludes instanceof List) {
+ if (excludes instanceof List) {
pluginExcludes.clear()
pluginExcludes.addAll(excludes)
}
@@ -343,8 +369,8 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
def resources = pluginXml.resources
- for(String cn in artefactClasses) {
- if ( !resources.resource.find { it.text() == cn } ) {
+ for (String cn in artefactClasses) {
+ if (!resources.resource.find { it.text() == cn }) {
resources.appendNode {
resource(cn)
}
@@ -365,7 +391,7 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
} catch (e) {
// corrupt, recreate
- writePluginXml(pluginClassNode,pluginVersion, pluginXmlFile,
artefactClasses)
+ writePluginXml(pluginClassNode, pluginVersion, pluginXmlFile,
artefactClasses)
}
}
@@ -375,7 +401,7 @@ class GlobalGrailsClassInjectorTransformation implements
ASTTransformation, Comp
def antPathMatcher = new AntPathMatcher()
pluginXml.resources.resource.each { res ->
- if (pluginExcludes.any() { String exc ->
antPathMatcher.match(exc, res.text().replace('.','/')) }) {
+ if (pluginExcludes.any() { String exc ->
antPathMatcher.match(exc, res.text().replace('.', '/')) }) {
res.replaceNode {}
}
}