Uploaded image for project: 'Gradle'
  1. Gradle
  2. GRADLE-1885

Jar task should not create build/tmp/x directory when it's not executed

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Resolved
    • Resolution: Fixed
    • Affects Version/s: 1.0-milestone-3
    • Fix Version/s: 1.0-milestone-7

      Description

      SYMPTOM
      Given the following build.gradle in an empty directory:

      build.gradle
        task bar(type: Jar) {
          from "somewhere"
          into "somewhereelse"
        }
      

      and the following gradle execution:

      command line
       $ gradle tasks
      

      we get the following resulting directory structure:

      result
       
      $ tree
      .
      ├── build
      │   └── tmp
      │       └── bar
      └── build.gradle
      
      3 directories, 1 file
      

      note that we started from an empty directory.

      CAUSE
      Digging into the gradle source a bit we find the following code:

      Jar.groovy
      //Jar.groovy in gradle source
      public class Jar extends Zip {
          <snip>
          Jar() {
              extension = DEFAULT_EXTENSION
              manifest = new DefaultManifest(project.fileResolver)
              // Add these as separate specs, so they are not affected by the changes to the main spec
              metaInf = copyAction.rootSpec.addFirst().into('META-INF')
              metaInf.addChild().from {
                  MapFileTree manifestSource = new MapFileTree(temporaryDir)
                  manifestSource.add('MANIFEST.MF') {OutputStream outstr ->
                      Manifest manifest = getManifest() ?: new DefaultManifest(null)
                      manifest.writeTo(new OutputStreamWriter(outstr))
                  }
                  return new FileTreeAdapter(manifestSource)
              }
              copyAction.mainSpec.eachFile { FileCopyDetails details ->
                  if (details.path.equalsIgnoreCase('META-INF/MANIFEST.MF')) {
                      details.exclude()
                  }
              }
          }
      

      note among other things the 'temporaryDir' variable access. This comes from a super class a bunch of inheritance levels up:

      AbstractTask.java
          public File getTemporaryDir() {
              File dir = getServices().get(TemporaryFileProvider.class).newTemporaryFile(getName());
              dir.mkdirs();
              return dir;
          }
      

      notice the 'dir.mkdirs()'. So to summarize: just by instantiating the Jar task class we will create the directory structure described above. This is independent of whether we have elected do disable the jar task entirely, excluded it from the execution graph, added an onlyIf clause etc etc.

      Note that we get the same result with the following build file:

      build.gradle
      apply plugin: 'java'
      

      and just executing 'gradle tasks'.

      I think this behavior is unexpected and undesirable. In large multi-project builds where we want to leave directories without sources or resources well alone, this becomes a serious issue.

      In addition, creating a clean workaround by selectively deleting the build directory only when it was erroneously created by the jar task is actually non-trivial. How do you figure out if the Jar task erroneously created the directory? And if it did, where do you add the code to remove the directory? jar.doLast doesn't work as (in my specific example) I have disabled the jar task and jar.doLast is therefore not executed. Adding this to assemble.doLast works but gets hairy with the state management to only do this when the jar task did the wrong thing.

      WORKAROUND
      As this is really not clean I hesitate to even share this, but perhaps it can help somebody out there. I ended up with the following simplistic workaround:

      stupid workaround
        assemble.doLast {
          if (!fileTree(dir: buildDir).find { it.isFile() } ) {
            println "no files present in ${project.path} build dir $buildDir, deleting it"
            delete(buildDir)
          }
        }
      

      POSSIBLE SOLUTION
      Can we make all the code after the two first lines in the Jar class constructor get executed on first-run or first-access (of say getMetaInf etc) instead of in the constructor?

        Attachments

          Activity

            People

            Assignee:
            ldaley Luke Daley
            Reporter:
            mbjarland Matias Bjarland
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: