[Jython-checkins] jython: Rework Gradle build to obtain distributable JAR and POM, both correct.

jeff.allen jython-checkins at python.org
Mon Jul 30 01:23:12 EDT 2018


https://hg.python.org/jython/rev/121e7ca91b4a
changeset:   8177:121e7ca91b4a
user:        Jeff Allen <ja.py at farowl.co.uk>
date:        Sun Jul 29 23:11:43 2018 +0100
summary:
  Rework Gradle build to obtain distributable JAR and POM, both correct.

Command "./gradlew publishToMavenLocal" will post a JAR and POM locally that may
be used experimentally in Maven-based projects.

files:
  build.gradle |  253 ++++++++++++++++----------------------
  1 files changed, 104 insertions(+), 149 deletions(-)


diff --git a/build.gradle b/build.gradle
--- a/build.gradle
+++ b/build.gradle
@@ -52,16 +52,26 @@
 
 // ---------------- Miscellaneous configuration --------------------------------
 
+// Support Java 7 onwards
+sourceCompatibility = '1.7'
+targetCompatibility = '1.7'
 
 // Separate the Gradle build from that of Ant
 buildDir = file('build2')
 ext {
     buildDate = new Date()
+    /*
+     * The directory structure supporting the build has separate locations for
+     * several intermediate stages.
+     */
     // Java source generated by ANTLR
     antlrGenDir = "$buildDir/gensrc/org/python/antlr"
-    // This is where we assemble the standard library pre-JAR
+    // Intermediate locations for compiled classes
+    unexposedDir = "$buildDir/unexposed"
+    exposedDir = "$buildDir/exposed"
+    // The standard library may safely be assembled in-place as a resource
     buildLibDir = "$buildDir/resources/main/Lib/"
-    compiledLibDir = "$buildDir/classes/main/Lib/"
+    compiledLibDir = "$buildDir/resources/main/Lib/"
 }
 
 
@@ -87,11 +97,8 @@
             exclude 'com/**'    // for now
         }
 
-        // output.resourcesDir = project.ext.assemblyDir
-
         resources {
-            // Resources in project root, but this invites an explosion:
-            // srcDirs = ['']
+            // Resources in project root, but this invites an explosion.
             // ... so claim no sources:
             srcDirs = []
             // and fix it in task processResources
@@ -131,7 +138,7 @@
     implementation group: 'org.ow2.asm', name: 'asm-commons', version: '5.2'
     implementation group: 'org.ow2.asm', name: 'asm-util', version: '5.2'
 
-    implementation group: 'com.google.guava', name: 'guava', version: '22.0-android'
+    api group: 'com.google.guava', name: 'guava', version: '22.0-android'
     implementation group: 'com.ibm.icu', name: 'icu4j', version: '59.1'
 
     implementation group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.5'
@@ -164,6 +171,11 @@
  * Jython brings several files we could treat as resources, but they do not sit
  * in the Gradle-conventional 'main/resources' directory, rather are in the
  * project root or rub shoulders with the java source. Pick them individually.
+ *
+ * Several tasks defined below declare that processResources depends on them,
+ * with the objective that at the end of processResources all generated
+ * resources and the stdlib (but not the compiled stdlib) should be in place
+ * in $buildDir/resources/main.
  */
 processResources {
     from(file('.')) {
@@ -182,24 +194,29 @@
     outputDirectory = file(project.ext.antlrGenDir)
 }
 
+// ---------------- compleJava Task -------------------------------------------------
+
+compileJava {
+    // Divert compiled classes to intermediate location pre-exposure.
+    destinationDir = file(project.ext.unexposedDir)
+    println "compileJava.destinationDir = $destinationDir}"
+}
+
 // ---------------- Expose Task ------------------------------------------------
 
 /*
- * The exposer operates between two (somewhat fixed) directories. We follow
- * the Gradle-conventional directory structure (not the legacy one).
+ * The exposer operates between the output of compileJava (unexposed directory)
+ * and a second intermediate location (exposed directory). These two the
+ * mergeExposed task will finally combine in the Gradle-standard classes
+ * directory used as input by the jar task.
  */
-ext {
-    compileDir = "$buildDir/classes/java/main/"
-    exposedDir = "$buildDir/classes/exposed/main/"
-}
-
 configurations {
     expose.extendsFrom(implementation)
 }
 
 dependencies {
-    // Put our compiled classes on the path of the expose (Ant) task
-    expose files("$buildDir/classes/java/main")
+    // The expose (Ant) task depends on classes compiled to here:
+    expose files(project.ext.unexposedDir)
 }
 
 // A (Gradle) task to run the Ant task 'expose'.
@@ -208,7 +225,7 @@
     description = 'Expose Java types to Python using their annotations.'
 
     // Allow Gradle to infer the need to regenreate the outputs
-    inputs.files(fileTree("${project.ext.compileDir}/org/python"))
+    inputs.files(fileTree("${project.ext.unexposedDir}/org/python"))
     outputs.dir(project.ext.exposedDir)
 
     doLast {
@@ -224,13 +241,27 @@
 
         // Use the Gradle-conventional directory structure (not the legacy one).
         ant.expose(
-            srcdir: file(project.ext.compileDir),
+            srcdir: file(project.ext.unexposedDir),
             destdir: mkdir(file(project.ext.exposedDir)),
             includesfile: file('CoreExposed.includes')
         )
     }
 }
 
+// Task to merge the exposed and unexposed classes
+task mergeExposed(group: 'Custom', type:Copy, dependsOn: expose) {
+    description = 'Copy exposed Java types to classes.'
+    // Exposed version will take precedence
+    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+    from file(exposedDir)
+    from file(unexposedDir)
+    into sourceSets.main.output.classesDirs.singleFile
+}
+
+// Attach to the classes task the placing of all compiled and exposed classes.
+classes.dependsOn(mergeExposed)
+
+
 // ---------------- Version-related file generation ----------------------------
 
 /*
@@ -375,11 +406,12 @@
     libJython = 'Lib'
 }
 
-
 /*
  * Copy the Python standard library. We take this from a distribution of
- * CPython, but take only the files specified in CPythonLib.includes in the
- * project root.
+ * CPython, but take only the files specified in CPythonLib.includes.
+ * The Jython version of the standard library will be copied to the same place.
+ * Files from the Jython library having the same name (relative path) as one
+ * in CPythonLib.includes thereby take precedence.
  */
 task copyLib(
         type: Copy,
@@ -411,28 +443,17 @@
         }
         // Copy the subset as specified by the list
         project.copy {
+            into project.ext.buildLibDir
             from file(project.ext.libPython)
             include cPythonLibIncludes
             exclude '**/*.pyc', '**/*.pyd'
-            into project.ext.buildLibDir
             duplicatesStrategy = DuplicatesStrategy.EXCLUDE
         }
     }
-
-    /*
-    <target name="copy-cpythonlib">
-        <copy todir="${dist.dir}/Lib">
-            <fileset dir="${python.lib}" excludes="** /*.pyc, ** /*.pyo"
-                    includesfile="${jython.base.dir}/CPythonLib.includes">
-                <!-- The include file gets all of lib-python/2.7's test directory, but we only want the ones from Jython's Lib.   -->
-                <present present="srconly" targetdir="${jython.base.dir}/Lib"/>
-            </fileset>
-        </copy>
-    </target>
-    */
 }
 
-
+// Attach this task to processResources
+processResources.dependsOn(copyLib)
 
 
 // ---------------- Jython-Compile Python --------------------------------------
@@ -450,7 +471,6 @@
 
 dependencies {
     // Jython as built so far should be on the path of the jycompile (Ant) task
-    pycompile files("$buildDir/classes/exposed/main")
     pycompile files("$buildDir/classes/java/main")
     pycompile files("$buildDir/resources/main")
 }
@@ -460,9 +480,9 @@
         group: 'Custom',
         description: 'Compile the Python modules to .class files for the JAR') {
 
-    dependsOn compileJava
-    dependsOn expose
-    dependsOn copyLib
+    // Compiler depends on rest of Jython being fully assembled in 'classes'
+    dependsOn classes
+    // Note that classes depends on processResources (Java plug-in).
 
     // Allow Gradle to infer the need to regenerate the outputs
     inputs.dir project.ext.buildLibDir
@@ -516,39 +536,32 @@
 
 /*
  * The default behaviour of the Java plug-in is to make a JAR of the classes in
- * the "main" source set. We need a more complex operation that provides users
- * with exposed classes instead of their plain counterparts, and also various
- * configuration files and the Python library.
- *
- * Much of the assembly has taken place already in selective copying to the
- * build directory by tasks processResources and copyLib.
+ * the "main" source set and its resources. Having carefully substituted/added
+ * exposed classes in the assembled classes directory, and having prepared the
+ * (compiled) stdlib as a resource, this is close to what we need, with a few
+ * adjustments as noted.
  */
-task jar(type: Jar, overwrite: true) {
+jar {
 
+    // Ensure that compiled stdlib is part of the resources to JAR.
     dependsOn pycompile
 
-    /*
-     * This is a custom replacement Jar task so that we may control the
-     * order of adding classes. Exposed versions of identically-named classes
-     * supersede (by arriving first) those directly from compilation.
-     */
-    exclude 'org/python/expose/generate/**' // The expose tool itself
-
-    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+    // It is important for import that X$py.class be newer than X.py
     preserveFileTimestamps = true
 
-    // Add the exposed classes to the JAR
-    from expose
-
-    // Add other compiled classes
-    from compileJava
+    // We don't JAR the expose tool itself
+    exclude 'org/python/expose/generate/**'
 
-    // Add the resources (includes merged Python library)
-    from processResources
+    // Exclude tests and test material from the main JAR
+    exclude 'Lib/distutils/tests/'
+    exclude 'Lib/email/test/'
+    exclude 'Lib/json/tests/'
+    exclude 'Lib/lib2to3/tests/'
+    exclude 'Lib/unittest/tests/'
+    exclude 'Lib/test/'
+    // XXX is this not more naturally done by having separate test resources?
 
-    // Add compiled Python library
-    from file(project.ext.compiledLibDir).parent
-
+    // Build a custom manifest
     manifest {
         // These attribute values are based on inspecting the ant build
         attributes ([
@@ -559,11 +572,15 @@
         attributes( [ // Build-Info section
             'version': project.version,
             'build-compiler': 'modern',
-            'jdk-target-version': '1.7',
+            'jdk-target-version': project.targetCompatibility,
             'debug': true,
         ], 'Build-Info' )
     }
-
+    
+    doFirst {
+        println "jar: for ${archiveName}"
+    }
+    
 }
 
 
@@ -607,17 +624,13 @@
     publications {
         // The production JAR we expect to be cited as a dependency by users
         main(MavenPublication) {
-            
-            // Correctly-constructed jar but a .pom without dependencies:
-            artifact jar
 
-            // Nice .pom but the jar is the default built by the Java plugin:
-            //from components.java
+            from components.java
             
             // Also provide the source.
             artifact sourcesJar
-            // Also provide the docs. (Some javadoc errors currntly.)
-            artifact javadocJar
+            // Also provide the docs. (Some javadoc errors currently.)
+            //artifact javadocJar
         }
     }
 
@@ -663,7 +676,6 @@
     failFast = true
 
     // Properties as defined in Ant target javatest-basepath
-    //systemProperty 'python.home', project.ext.distDir
     // XXX Not sure of all that python.home is used for in tests.
     systemProperty 'python.home', file(copyLib.destinationDir).parent
     systemProperty 'python.test.source.dir', project.ext.testSourceDir
@@ -766,81 +778,24 @@
     }
 }
 
-task dumpCompo {
+task dumpSS {
     doLast {
-        println('components:')
-        components.each { println it }
-        println('components.java:')
-        components.java.each { println it }
-    }
-}
-
-task dumpArt {
-    doLast {
-        println('artifacts:')
-        artifacts.each { println it }
-    }
-}
-
-task dumpArch {
-    doLast {
-        println('archives:')
-        configurations.archives.each { println it }
+        println '*** source sets ***'
+        for (ss in sourceSets) {
+            String name = ss.name
+            println ss
+            println "  ${name}.compileConfigurationName = ${ss.compileConfigurationName}"
+            println "  ${name}.implementationConfigurationName = ${ss.implementationConfigurationName}"
+            println "  ${name}.runtimeConfigurationName = ${ss.runtimeConfigurationName}"
+            println "  ${name}.java.srcDirs = ${ss.java.srcDirs}"
+            println "  ${name}.antlr.srcDirs = ${ss.antlr.srcDirs}"
+            println "  ${name}.resources.srcDirs = ${ss.resources.srcDirs}"
+            println "  ${name}.output.dirs = ${ss.output.dirs.files}"
+            println "  ${name}.output.classesDirs = ${ss.output.classesDirs.files}"
+            println "  ${name}.output.resourcesDir = ${ss.output.resourcesDir}"
+            println "  ${name}.classesTaskName = ${ss.classesTaskName}"
+            println "  ${name}.compileJavaTaskName = ${ss.compileJavaTaskName}"
+            println "  ${name}.jarTaskName = ${ss.jarTaskName}"
+        }
     }
 }
-
-task dumpex {
-    // Legacy approach to obtaining files to JAR
-    doLast {
-        int n = project.ext.exposedDir.length()
-        Set<String> exposedClasses = new TreeSet()
-        //println "*** files in ${project.ext.exposedDir}:"
-        for (f in fileTree(project.ext.exposedDir)) {
-            //println project.relativePath(f).substring(n)
-            exposedClasses.add( project.relativePath(f).substring(n) )
-        }
-        //for (f in exposedClasses) { println f }
-
-        println "${fileTree(project.ext.compileDir).size()} compiled classes."
-
-        n = project.ext.compileDir.length()
-        int countx = 0
-        for (f in fileTree(project.ext.compileDir)) {
-            //println project.relativePath(f).substring(n)
-            String name = project.relativePath(f).substring(n)
-            if (name in exposedClasses) { countx += 1 }
-        }
-        println "${exposedClasses.size()} classes from ${countx} exposed."
-
-        def compiledToJar = fileTree(project.ext.compileDir).filter({
-            File f -> !(project.relativePath(f).substring(n) in exposedClasses)
-        })
-
-        println "${compiledToJar.size()} to be jarred (after filtering)."
-
-        int counti = 0
-        for (f in fileTree(project.ext.compileDir)) {
-            String name = project.relativePath(f).substring(n)
-            String exposed = (name in exposedClasses) ? "EXPOSED" : ""
-            // println "${name} ${exposed}"
-            if (exposed) { counti += 1 }
-        }
-
-        println "${counti} in overlap."
-    }
-}
-
-
-task dumpSS {
-    doLast {
-        // Debug
-        println '*** source sets ***'
-        for ( sourceSet in sourceSets ) {
-            println "  ${sourceSet}"
-            // for ( name in sourceSet.asMap.keys ) {
-                // sourceDirectorySet = sourceSet.asMap[name]
-                // println "    $name = $sourceDirectorySet"
-            // }
-        }
-    }
-}
\ No newline at end of file

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list