Turn `jarjar` into a cacheable task

This commit moves the logic of creating a jarjar archive into its own class. 
This
makes it possible to cache it, unlike the traditional `Jar` task from Gradle.


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/044b7874
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/044b7874
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/044b7874

Branch: refs/heads/GROOVY_2_6_X
Commit: 044b7874e6616cccc209cb9c6fda6dcfb5a43e50
Parents: e7ee1f2
Author: Cedric Champeau <[email protected]>
Authored: Sat Dec 16 20:49:34 2017 +0100
Committer: Cedric Champeau <[email protected]>
Committed: Sun Dec 17 14:53:29 2017 +0100

----------------------------------------------------------------------
 .../codehaus/groovy/gradle/CacheableJar.groovy  |  29 ----
 .../codehaus/groovy/gradle/JarJarTask.groovy    | 142 +++++++++++++++++++
 gradle/assemble.gradle                          | 132 +++++++----------
 gradle/upload.gradle                            |   6 +-
 4 files changed, 200 insertions(+), 109 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/044b7874/buildSrc/src/main/groovy/org/codehaus/groovy/gradle/CacheableJar.groovy
----------------------------------------------------------------------
diff --git 
a/buildSrc/src/main/groovy/org/codehaus/groovy/gradle/CacheableJar.groovy 
b/buildSrc/src/main/groovy/org/codehaus/groovy/gradle/CacheableJar.groovy
deleted file mode 100644
index fa4b5f2..0000000
--- a/buildSrc/src/main/groovy/org/codehaus/groovy/gradle/CacheableJar.groovy
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *  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.
- */
-package org.codehaus.groovy.gradle
-
-import groovy.transform.CompileStatic
-import org.gradle.api.tasks.CacheableTask
-import org.gradle.api.tasks.bundling.Jar
-
-@CompileStatic
-@CacheableTask
-class CacheableJar extends Jar {
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/044b7874/buildSrc/src/main/groovy/org/codehaus/groovy/gradle/JarJarTask.groovy
----------------------------------------------------------------------
diff --git 
a/buildSrc/src/main/groovy/org/codehaus/groovy/gradle/JarJarTask.groovy 
b/buildSrc/src/main/groovy/org/codehaus/groovy/gradle/JarJarTask.groovy
new file mode 100644
index 0000000..4a3c4ca
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/codehaus/groovy/gradle/JarJarTask.groovy
@@ -0,0 +1,142 @@
+/*
+ *  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.
+ */
+package org.codehaus.groovy.gradle
+
+import groovy.transform.CompileStatic
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import org.gradle.api.Action
+import org.gradle.api.java.archives.Manifest
+
+@CacheableTask
+class JarJarTask extends DefaultTask {
+    private final static String JARJAR_CLASS_NAME = 
'org.pantsbuild.jarjar.JarJarTask'
+
+    String description = "Repackages dependencies into a shaded jar"
+
+    private List<Action<? super Manifest>> manifestTweaks = []
+
+    @Input
+    File from
+
+    @Input
+    FileCollection repackagedLibraries
+
+    @Input
+    FileCollection jarjarToolClasspath
+
+    @Input
+    List<String> untouchedFiles
+
+    @Input
+    Map<String, String> patterns
+
+    @Input
+    Map<String, List<String>> excludesPerLibrary
+
+    @Input
+    Map<String, List<String>> includesPerLibrary
+
+    @OutputFile
+    File outputFile
+
+    void withManifest(Action<? super Manifest> action) {
+        manifestTweaks << action
+    }
+
+    String getArchiveName() {
+        outputFile.name
+    }
+
+    @TaskAction
+    void generateDescriptor() {
+        def originalJar = from
+        def tmpJar = new File(temporaryDir, 
"${outputFile.name}.${Integer.toHexString(UUID.randomUUID().hashCode())}.tmp")
+        def manifestFile = new File(temporaryDir, 'MANIFEST.MF')
+
+        // First step is to create a repackaged jar
+        outputFile.parentFile.mkdirs()
+        try {
+            project.ant {
+                taskdef name: 'jarjar', classname: JARJAR_CLASS_NAME, 
classpath: jarjarToolClasspath.asPath
+                jarjar(jarfile: tmpJar, filesonly: true) {
+                    zipfileset(
+                            src: originalJar,
+                            excludes: untouchedFiles.join(','))
+
+                    repackagedLibraries.files.each { File library ->
+                        def libraryName = JarJarTask.baseName(library)
+                        def includes = includesPerLibrary[libraryName]
+                        def excludes = excludesPerLibrary[libraryName]
+                        if (includes) {
+                            zipfileset(src: library, includes: 
includes.join(','))
+                        } else if (excludes) {
+                            zipfileset(src: library, excludes: 
excludes.join(','))
+                        } else {
+                            zipfileset(src: library, excludes: 
excludesPerLibrary['*'].join(','))
+                        }
+                    }
+                    patterns.each { pattern, result ->
+                        rule pattern: pattern, result: result
+                    }
+                }
+            }
+
+            // next step is to generate an OSGI manifest using the newly 
repackaged classes
+            def mf = project.rootProject.convention.plugins.osgi.osgiManifest {
+                symbolicName = project.name
+                instruction 'Import-Package', '*;resolution:=optional'
+                classesDir = tmpJar
+            }
+
+            manifestTweaks.each {
+                it.execute(mf)
+            }
+
+            // then we need to generate the manifest file
+            mf.writeTo(manifestFile)
+
+            // so that we can put it into the final jar
+            project.ant.copy(file: tmpJar, tofile: outputFile)
+            project.ant.jar(destfile: outputFile, update: true, manifest: 
manifestFile) {
+                manifest {
+                    // because we don't want to use JDK 1.8.0_91, we don't 
care and it will
+                    // introduce cache misses
+                    attribute(name:'Created-By', value:'Gradle')
+                }
+                zipfileset(
+                        src: originalJar,
+                        includes: untouchedFiles.join(','))
+            }
+        } finally {
+            manifestFile.delete()
+            project.ant.delete(file: tmpJar, quiet: true, deleteonexit: true)
+        }
+    }
+
+    @CompileStatic
+    private static String baseName(File file) {
+        file.name.substring(0, file.name.lastIndexOf('-'))
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/044b7874/gradle/assemble.gradle
----------------------------------------------------------------------
diff --git a/gradle/assemble.gradle b/gradle/assemble.gradle
index c02659c..212c0c2 100644
--- a/gradle/assemble.gradle
+++ b/gradle/assemble.gradle
@@ -17,6 +17,8 @@
  *  under the License.
  */
 import org.apache.tools.ant.filters.ReplaceTokens
+import org.codehaus.groovy.gradle.JarJarTask
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
 
 apply plugin: 'osgi'
 
@@ -84,6 +86,7 @@ task copy(type: Copy) {
 
 // unnecessary entries which in addition may trigger unnecessary rebuilds
 def excludedFromManifest = [
+        'Ant-Version',
         'Originally-Created-By',
         'Bnd-LastModified',
         'Created-By'
@@ -140,6 +143,8 @@ ext.subprojectOsgiManifest = {
 }
 
 allprojects {
+    boolean isRoot = project == rootProject
+
     def producedJars = [jar]
     jar {
         appendix = 'raw'
@@ -180,87 +185,60 @@ allprojects {
         }
         arch.exclude '**/package-info.class'
 
-        task "jar${arch.name}"(type: Jar, dependsOn: arch) {
-            outputs.cacheIf {
-                // caching JarJar because it's quite expensive to create
-                true
+        task "jar${arch.name}"(type: JarJarTask) {
+            dependsOn arch
+            from = file(arch.archivePath)
+            if (project == rootProject) {
+                repackagedLibraries = 
files(configurations.runtime.incoming.artifactView {
+                    componentFilter { component ->
+                        if (component instanceof ModuleComponentIdentifier) {
+                            return component.module in [
+                                    'antlr', 'antlr-runtime', 'antlr4', 
'antlr4-runtime', 'antlr4-annotations',
+                                    'asm', 'asm-commons', 'asm-tree', 
'asm-util',
+                                    'commons-cli']
+                        }
+                        return false
+                    }
+                }.files)
+            } else {
+                repackagedLibraries = files()
             }
-            destinationDir = arch.destinationDir
-            baseName = arch.baseName
-            classifier = arch.classifier
-            includeEmptyDirs = false
-            def target = new File("${archivePath}.tmp")
-            def targetTmp = new File("${archivePath}.tmp.1.tmp")
-            inputs.file(arch.archivePath)
-
-            doFirst {
-                from zipTree(target)
-
-                def keepUntouched = [
-                        'org/codehaus/groovy/cli/GroovyPosixParser*.class',
-                        'groovy/util/CliBuilder*.class',
-                        'groovy/util/OptionAccessor*.class',
-                        
'org/codehaus/groovy/tools/shell/util/HelpFormatter*.class'
-                ].join(',')
-                boolean isRoot = project == rootProject
-                def gradleProject = project
-                ant {
-                    taskdef name: 'jarjar', classname: jarjarTaskClassName, 
classpath: rootProject.configurations.tools.asPath
-                    jarjar(jarfile: targetTmp) {
-                        zipfileset(
-                                src: arch.archivePath,
-                                excludes: keepUntouched)
-
-                        // only groovy core will include the dependencies 
repackaged
-                        if (isRoot) {
-                            configurations.runtime.files.findAll { file ->
-                                ['antlr', 'asm', 'commons-cli'].any {
-                                    file.name.startsWith(it)
-                                } && ['asm-attr', 'asm-util', 
'asm-analysis'].every { !file.name.startsWith(it) }
-                            }.each { jarjarFile ->
-                                // explanation of excludes:
-                                // GROOVY-7386: stop copy of incorrect maven 
meta info
-                                // GROOVY-8332: stop copy of annotation 
processor which is for some reason included in antlr runtime artifact
-                                // GROOVY-8387: we don't want 
module-info.class from any dependencies
-                                zipfileset(src: jarjarFile,
-                                        excludes: 
'META-INF/maven/**,META-INF/*,META-INF/services/javax.annotation.processing.Processor,module-info.class')
-                            }
-
-                            zipfileset(src: configurations.runtime.files.find 
{ file -> file.name.startsWith('asm-util') },
-                                    includes: 
'org/objectweb/asm/util/Printer.class,org/objectweb/asm/util/Textifier.class,org/objectweb/asm/util/ASMifier.class,org/objectweb/asm/util/Trace*')
+            jarjarToolClasspath = rootProject.configurations.tools
+            untouchedFiles = [
+                    'org/codehaus/groovy/cli/GroovyPosixParser*.class',
+                    'groovy/util/CliBuilder*.class',
+                    'groovy/util/OptionAccessor*.class',
+                    'org/codehaus/groovy/tools/shell/util/HelpFormatter*.class'
+            ]
+            patterns = [
+                    'antlr.**': 'groovyjarjarantlr.@1', // antlr2
+                    'org.antlr.**': 'groovyjarjarantlr4.@1', // antlr4
+                    'org.objectweb.**': 'groovyjarjarasm.@1',
+                    'org.apache.commons.cli.**': 'groovyjarjarcommonscli.@1'
+            ]
+            excludesPerLibrary = [
+                    '*': 
['META-INF/maven/**','META-INF/*','META-INF/services/javax.annotation.processing.Processor','module-info.class']
+            ]
+            includesPerLibrary = [
+                    'asm-util': ['org/objectweb/asm/util/Printer.class',
+                                 'org/objectweb/asm/util/Textifier.class',
+                                 'org/objectweb/asm/util/ASMifier.class',
+                                 'org/objectweb/asm/util/Trace*']
+            ]
+            outputFile = 
file("$buildDir/libs/${arch.baseName}-${arch.version}${arch.classifier?'-'+arch.classifier:''}.jar")
+
+            withManifest {
+                def moduleName = "org.codehaus.${project.name.replace('-', 
'.')}"
+                attributes('Automatic-Module-Name': moduleName)
+                from(allManifest) {
+                    eachEntry { details ->
+                        if (excludedFromManifest.any { it == details.key }) {
+                            details.exclude()
                         }
-                        rule pattern: 'antlr.**', result: 
'groovyjarjarantlr.@1' // antlr2
-                        rule pattern: 'org.antlr.**', result: 
'groovyjarjarantlr4.@1' // antlr4
-                        rule pattern: 'org.objectweb.**', result: 
'groovyjarjarasm.@1'
-                        rule pattern: 'org.apache.commons.cli.**', result: 
'groovyjarjarcommonscli.@1'
                     }
                 }
-
-                def manifestSpec = isRoot ? groovyOsgiManifest : 
subprojectOsgiManifest
-                manifest = osgiManifest {
-                    symbolicName = gradleProject.name
-                    instruction 'Import-Package', '*;resolution:=optional'
-                    classesDir = targetTmp
-                    def moduleName = 
"org.codehaus.${gradleProject.name.replace('-', '.')}"
-                    attributes('Automatic-Module-Name': moduleName)
-                }
-                manifest(manifestSpec)
-
-                def manifestPath = "${temporaryDir}/META-INF/MANIFEST.MF"
-                manifest.writeTo(manifestPath)
-
-                ant.copy(file: targetTmp, tofile: target)
-                ant.jar(destfile: target, update: true, manifest: 
manifestPath) {
-                    zipfileset(
-                            src: arch.archivePath,
-                            includes: keepUntouched)
-                }
-
-            }
-            doLast {
-                target.delete()
-                ant.delete(file: targetTmp, quiet: true, deleteonexit: true)
             }
+            withManifest(isRoot ? groovyOsgiManifest : subprojectOsgiManifest)
         }
 
     }
@@ -280,7 +258,7 @@ allprojects {
                     taskdef name: 'jarjar', classname: jarjarTaskClassName, 
classpath: rootProject.configurations.tools.asPath
                     jarjar(jarfile: target) {
                         zipfileset(dir: "$rootProject.projectDir/notices/", 
includes: isRootProject ? 'NOTICE-GROOIDJARJAR' : 'NOTICE-GROOID', fullpath: 
'META-INF/NOTICE')
-                        zipfileset(src: jarjar.archivePath, excludes: 
'META-INF/NOTICE')
+                        zipfileset(src: jarjar.outputFile, excludes: 
'META-INF/NOTICE')
                         if (isRootProject) {
                             zipfileset(src: 
rootProject.configurations.runtime.files.find {
                                 it.name.startsWith('openbeans')

http://git-wip-us.apache.org/repos/asf/groovy/blob/044b7874/gradle/upload.gradle
----------------------------------------------------------------------
diff --git a/gradle/upload.gradle b/gradle/upload.gradle
index 8588346..c06cad6 100644
--- a/gradle/upload.gradle
+++ b/gradle/upload.gradle
@@ -94,7 +94,7 @@ allprojects {
         }
 
         artifacts {
-            archives jarjar
+            archives jarjar.outputFile
             archives sourceJar
             archives javadocJar
             archives groovydocJar
@@ -105,13 +105,13 @@ allprojects {
         }
 
         [uploadArchives, install]*.with {
-            dependsOn([jar, sourceJar, javadocJar, groovydocJar])
+            dependsOn([grooidjar, jarjar, sourceJar, javadocJar, groovydocJar])
             if (rootProject.indyCapable()) {
                 dependsOn jarjarWithIndy
             }
             doFirst {
                 if (rootProject.indyCapable()) {
-                    project.artifacts.add('archives', jarjarWithIndy)
+                    project.artifacts.add('archives', 
jarjarWithIndy.outputFile)
                 }
                 def grooidJar = rootProject.ext.deriveFile(jar.archivePath, 
'grooid')
                 if (grooidJar.exists()) {

Reply via email to