2

I'm trying to implement in Java a protoc plugin. This requires a standalone self-executing jar since a CLASSPATH can't be passed to protoc.

I've found many articles talking about how to create a self-executing jar (eg http://en.newinstance.it/2012/04/17/self-executing-jar-files/). None, though, take into account the bundling of the dependencies along with the self-executing jar. This means that the self-executing jar isn't standalone. How can this be done?

Noel Yap
  • 18,822
  • 21
  • 92
  • 144
  • 1
    Possibly related: https://stackoverflow.com/q/3445825/1531971 –  Sep 15 '17 at 19:36
  • Yes, that's related but insufficient. The problem is that a jar can't have a `CLASSPATH` that references jars inside itself. – Noel Yap Sep 15 '17 at 20:23
  • Oh, right. That is ringing some bells. Being a Gradle wrangler is thankless. –  Sep 15 '17 at 20:25
  • @VinceEmigh, from what I understand from https://stackoverflow.blog/2011/08/19/the-future-of-community-wiki/, Community Wiki wouldn't provide the advantages you claim it would. – Noel Yap Sep 16 '17 at 19:31

1 Answers1

0

The following was copied from an existing project and may use some things not included in the snippet (eg Nebula plugins):

apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'

mainClassName = 'com.example.Main'

jar {
  manifest {
    attributes 'Main-Class': 'com.example.Main'
  }
}

publishing {
  publications {
    nebulaIvy(IvyPublication) {
      artifact(tasks.findByName('createJarExe').outputs.getFiles().getSingleFile()) {
        classifier '_-jdk8'
        extension 'exe'
        type 'exe'
      }
    }
  }
}

task createJarExe(dependsOn: installShadowDist) {
  final outputFile = file("${buildDir}/install/${project.name}-shadow/bin/${project.name}-${project.version}.exe")

  doLast {
    outputFile.text = '#!/bin/bash\n'
    outputFile.append('exec java -jar "$0" "$@"')
    outputFile.append(inputs.files.first().readBytes())
    outputFile.setExecutable(true)
  }

  inputs.files "${tasks.findByName('installShadowDist').outputs.getFiles().getSingleFile()}/lib/${project.name}-${project.version}-all.jar"
  outputs.file outputFile
  doFirst {
    mkdir outputFile.getParent()
  }
}

// wire the dependencies
tasks.whenTaskAdded { task ->
  if (task.name == 'publishNebulaIvyPublicationToDistIvyRepository') {
    task.dependsOn('createJarExe')
  }
}

Notes:

  • com.github.johnrengelman.shadow is used to create the uber-jar.
  • com.example.Main is the Java entry point for execution.
  • Specifying the classifier is more for the protoc Gradle plugin (which includes the OS in the classifier by default) and isn't strictly needed. Clients of the published protoc plugin must use versions 0.8.3 or higher of the protoc Gradle plugin which supports overriding the classifier.
  • createJarExe uses the uber-jar and follows the post on self-executing JAR files
  • wire the dependencies ensures that the uber-jar is created before distribution
Noel Yap
  • 18,822
  • 21
  • 92
  • 144