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()) {
