How can I create a pathing jar in Gradle

13,409

Solution 1

I finally got the "pathing jar" idea to work. I consider this to be a permanent workaround. This could be considered a solution if it is made part of gradle itself.

The original pathing jar code was provided by Peter, but it didn't work. The problem: classpath elements referenced in the pathing jar must be relative to the location of the pathing jar. So, this appears to work for me.

task pathingJar(type: Jar , dependsOn: 'cleanPathingJar') {
/**
 * If the gradle_user_home env var has been set to 
     * C:\ on a Win7 machine, we may not have permission to write the jar to
 * this directory, so we will write it to the caches subdir instead.  
     * This assumes a caches subdir containing the jars
 * will always exist.
 */
gradleUserHome = new File(gradle.getGradleUserHomeDir(), "caches")

relativeClasspathEntries = configurations.compile.files.collect {
    new File(gradleUserHome.getAbsolutePath()).toURI().
                  relativize(new File(it.getAbsolutePath()).toURI()).getPath()
}
appendix = "pathing"
destinationDir = gradleUserHome
doFirst {
    manifest {
        attributes "Class-Path": relativeClasspathEntries.join(" ")
    }
}
}

compileGroovy {
    dependsOn(pathingJar)
    classpath = files(pathingJar.archivePath)
}

Solution 2

Here is a tested solution:

task pathingJar(type: Jar) {
  appendix = "pathing"
  doFirst {
    manifest {
      attributes "Class-Path": configurations.compile.files.join(" ")
    }
  }
}

compileGroovy {
    dependsOn(pathingJar)
    classpath = files(pathingJar.archivePath)
}    

Depending on your exact requirements, you might have to tweak this a bit. For example, if you have tests written in Groovy, you will also need a pathing Jar for the test compile class path. In this case you'll need to repeat above configuration as follows:

task testPathingJar(type: Jar) {
  appendix = "testPathing"
  doFirst {
    manifest {
      attributes "Class-Path": configurations.testCompile.files.join(" ")
    }
  }
}

compileTestGroovy {
    dependsOn(testPathingJar)
    classpath = files(testPathingJar.archivePath)
}    
Share:
13,409
Ray Nicholus
Author by

Ray Nicholus

http://raynicholus.com Author of Beyond jQuery: a book that aims to educate web developers and give them the confidence to abandon the crutch that is jQuery and embrace the power of the web API and JavaScript. Author of "You Don't Need jQuery!": a series of blog posts that blossomed into "Beyond jQuery", which was published in November of 2016. Fine Uploader lead developer. We're hiring @ Widen!

Updated on June 18, 2022

Comments

  • Ray Nicholus
    Ray Nicholus about 2 years

    When running groovyc in a Windows env, I am running into issues due to the length of the classpath, in my situation. I would like to work around this by creating a pathing jar, and then put that jar on the cp. How can I create a pathing jar w/ all of the classpath entries specified automatically in gradle and then add that jar to the cp?

  • Ray Nicholus
    Ray Nicholus about 13 years
    Follow-up question: this doesn't appear to work as expected. The pathing jar is created, but when I add the pathing jar to the classpath when compiling, for some reason, the compile fails and complains about missing jars that are already referenced in the pathing jar...
  • Ray Nicholus
    Ray Nicholus about 13 years
    I'm thinking that this will not work, as this specifies absolute paths of the jars in the manifest. It isn't clear that absolute paths are valid in this case.
  • Peter Niederwieser
    Peter Niederwieser about 13 years
    The problem wasn't the absolute paths but that colon instead of space was used as the path separator. I've updated the code and tested it to make sure it works.
  • Ray Nicholus
    Ray Nicholus about 13 years
    I seem to remember testing with a space without any luck. I'll give it a shot again today and report back.
  • Ray Nicholus
    Ray Nicholus about 13 years
    Yep, this is not working for me. When I used the pathing jar, the compile fails due to an inability to locate dependencies.
  • Peter Niederwieser
    Peter Niederwieser about 13 years
    Strange, works for me. I even had a solution that used relative paths but discarded it after I found that absolute paths weren't the cause of the problem. How big is your project that you run into this kind of problem?
  • Ray Nicholus
    Ray Nicholus about 13 years
    I'd say we have a fairly large group of libraries/dependencies. Any idea what might be wrong? If I bypass gradle entirely and attempt to compile from the command line using javac & the pathing jar, I still run into the same problem.
  • Peter Niederwieser
    Peter Niederwieser about 13 years
    Which problem? That the pathing Jar doesn't work or that some class path is too long? Maybe absolute paths in the Class-Path attribute work on some platforms but not on others. I tested it on Mac OS X.
  • Peter Niederwieser
    Peter Niederwieser about 13 years
    Another way to tackle this problem would be to shorten the class path by substituting common path parts with an environment variable (say GRADLE_USER_HOME).
  • Ray Nicholus
    Ray Nicholus about 13 years
    I'll admit I've only tested this on Windows. Actually, I only need to test it on Windows as Windows is the reason for the use of a pathing jar in the first place, due to the single-command size limit. Regarding the env variable suggestion, I have considered that, but see the pathing jar idea as more elegant. That's a moot point because the pathing jar idea doesn't seem to work for me though. In the meantime, we have a workaround. Thanks for your input on this Peter.
  • default_avatar
    default_avatar about 11 years
    Class-path entries are ALWAYS relative see the jar spec: The value of this attribute specifies the relative URLs of the extensions or libraries that this application or extension needs [cited: docs.oracle.com/javase/1.4.2/docs/guide/jar/jar.html]
  • ed9w2in6
    ed9w2in6 over 10 years
    But this doesn't seem to contain currently project's jar file in the classpath?
  • Damir Arh
    Damir Arh over 8 years
    This didn't work in Groovy versions between 3.0.5 in 3.0.8. The linked Grails issue also includes a full workaround, using this approach.
  • JimLohse
    JimLohse over 8 years
    @RichardJohnson updated link for malformed/old link in previous comment: docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html
  • Philippe
    Philippe about 5 years
    If using Spring Boot and gradle 4.x or later, and attempting to start the application with the bootRun task, then the configuration should be "runtimeClasspath."
  • Constantino Cronemberger
    Constantino Cronemberger over 3 years
    I'm new to Gradle, but when I tried this solution it complained about gradleUserHome and relativeClasspathEntries not being defined, so I have added a def declaration in each line.