Distributions plugin cannot rerun installNameDist

I am working on a continuous integration system that uses Gradle to launch Vagrant to start and provision a virtual machine so that integration tests (etc.).

In working with it, I created a distribution that adds itself as a dependency to all RPM tasks’ output from the rest of the project so that it can install all of the RPMs into a single location (vm-subproject/build/install/projectname-rpms). That installation is copied during the Vagrant provisioning phase into an unmounted directory, which is then converted into a local Yum repository. In doing so, my locally built RPMs can be used in the tests that are provisioned to create the local yum repo within the virtual machine. This all works quite well.

However, I ran into an issue that occurs every time that I cause any of the RPMs to be recreated, thereby causing the installRpmsDist task to be re-run. The issue is that installRpmsDist always fails because it is missing something in the installation directory, which is a “lib” and “bin” directory.

This is a very easy problem to reproduce given a simple build environment with no code and not involving RPMs.

  • conf/file.txt [empty] - build.gradle

The build.gradle file:

apply plugin: "distribution"
  distributions {
  conf {
    contents {
      from { "conf" }
    }
  }
}

Run:

  1. gradle installConfDist 2. echo “something” >> conf/file.txt [avoid being UP-TO-DATE] 3. gradle installConfDist

Step #2 can be replaced with anything that results in the installConfDist task being re-run.

This is the problem copied from DistributionPlugin.groovy:

installTask.doFirst {
  if (destinationDir.directory) {
    if (!new File(destinationDir, 'lib').directory || !new File(destinationDir, 'bin').directory) {
      throw new GradleException("The specified installation directory '${destinationDir}' is neither empty nor does it contain an installation for '${distribution.name}'.\n" +
          "If you really want to install to this directory, delete it and run the install task again.\n" +
          "Alternatively, choose a different installation directory."
      )
    }
  }
}

Shy of simply removing the the Gradle code as is done in my pull request ( https://github.com/gradle/gradle/pull/280 ), there is a work-around that can be added to distributions. It can be made more generic if necessary, but if you only face the problem on an individual distribution (as I do), then you can add this after creating the distribution:

installConfDist {
  def installDir = file("${buildDir}/install/${project.name}-conf")
  def libDir = new File(installDir, "lib")
  def binDir = new File(installDir, "bin")
    // make the wrongly required directories before they are checked for
  doFirst {
    mkdir installDir
    mkdir libDir
    mkdir binDir
  }
  // remove them after the check
  doLast {
     libDir.deleteDir()
    binDir.deleteDir()
  }
}

Note: installConfDist is only the name of my task because “conf” is the name of my distribution. If your distribution’s name was “rpms” (for example), then you would use ‘installRpmsDist’ and ‘file("${buildDir}/install/${project.name}-rpms")’. Also, it’s worth noting that this adds the “lib” and “bin” directory before the check takes place (we put ourselves ahead of the internally added doFirst) and then we subsequently remove them afterward. If you wanted to keep either directory (e.g., they already exist in the distribution), then don’t invoke the associated mkdir and deleteDir calls. Also, this does not effect the UP-TO-DATE status of the task because those folders do not exist in the actual contents, so it’s a convenient workaround.

This has been resolved via a merged pull request.