Skip to content
This repository was archived by the owner on May 19, 2019. It is now read-only.

Commit 71ac3fb

Browse files
committed
Incubating support for executable Ruby-based JARs
1 parent 995db10 commit 71ac3fb

File tree

5 files changed

+149
-17
lines changed

5 files changed

+149
-17
lines changed

README.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jruby-gradle-jar-plugin
66
Plugin for creating JRuby-based java archives
77

88

9-
## Compatilibity
9+
## Compatibility
1010

1111
This plugin requires Gradle 2.0 or better.
1212

@@ -48,14 +48,8 @@ jar {
4848
// Can be called more than once for additional directories
4949
gemDir '/path/to/my/gemDir'
5050
51-
// Make the JAR executable and use the default main class
52-
defaultMainClass()
53-
54-
// Make the JAR executable by supplying your own main class
55-
mainClass 'my.own.main.'
56-
57-
// Equivalent to calling defaultGems() and defaultMainClass()
58-
defaults 'gem, 'mainClass'
51+
// Equivalent to calling defaultGems()
52+
defaults 'gem'
5953
6054
}
6155
@@ -72,9 +66,36 @@ task myJar (type :Jar) {
7266
7367
```
7468

69+
## Executable JARs
70+
71+
Please note that executable JARs are still an incubating feature. At this point appropriate libs will be copied
72+
to the `META-INF/lib` directory, but a working `init.rb` is not available. It is still the responsibility of the
73+
the user to craft an appropriate `init.rb` and copy it to `META-INF` via the provided the [metaInf {}](http://www.gradle.org/docs/current/dsl/org.gradle.api.tasks.bundling.Jar.html) closure.
74+
75+
```groovy
76+
jar {
77+
jruby {
78+
79+
// Make the JAR executable and use the default main class
80+
defaultMainClass()
81+
82+
// Make the JAR executable by supplying your own main class
83+
mainClass 'my.own.main.'
84+
85+
// Equivalent to calling defaultMainClass()
86+
defaults 'mainClass'
87+
88+
// Adds dependencies from this configuration into `META-INF/lib`
89+
// If none are specified, the plugin will default to 'jrubyJar','compile' & 'runtime'
90+
configurations 'myConfig'
91+
}
92+
}
93+
```
94+
7595
Using the default main class method `defaultMainClass()` will include class files from
7696
[warbler-bootstrap](https://github.com/jruby-gradle/warbler-bootstrap)
7797

98+
7899
## Controlling the version of warbler-bootstrap
79100

80101
By default the version is set to `1.+` meaning anything version 1.0 or beyond. If your project wants to lock

build.gradle

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ buildscript {
88
}
99

1010
group = 'com.github.jruby-gradle'
11-
version = '0.1.0'
11+
version = '0.1.1'
1212

1313
if (System.env.RELEASE != '1') {
1414
version = "${version}-SNAPSHOT"
@@ -28,12 +28,22 @@ configurations {
2828
dependencies {
2929
compile gradleApi()
3030
compile localGroovy()
31-
compile 'com.github.jruby-gradle:jruby-gradle-plugin:0.1.2'
31+
compile 'com.github.jruby-gradle:jruby-gradle-plugin:0.1.2+'
3232

3333
testCompile ("org.spockframework:spock-core:0.7-groovy-${gradle.gradleVersion.startsWith('1.')?'1.8':'2.0'}") {
3434
exclude module : 'groovy-all'
3535
}
36-
testWarbler group: 'com.github.jruby-gradle', name: 'warbler-bootstrap', version: '0.1.+'
36+
37+
// For the testWarbler tests I am locking the versions, instead of a open version, as it makes
38+
// unit testing easier, This does not affect the final artifact.
39+
// If you change values here, you need to update JRubyJarPluginSpec as well.
40+
testWarbler 'com.github.jruby-gradle:warbler-bootstrap:0.1.0'
41+
testWarbler ('org.jruby:jruby-complete:1.7.15') {
42+
transitive = false
43+
}
44+
testWarbler ("org.spockframework:spock-core:0.7-groovy-2.0") {
45+
transitive = false
46+
}
3747

3848
}
3949

src/main/groovy/com/github/jrubygradle/jar/JRubyJarConfigurator.groovy

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.github.jrubygradle.jar
22

33
import groovy.transform.PackageScope
4+
import org.gradle.api.Incubating
45
import org.gradle.api.Project
6+
import org.gradle.api.Task
7+
import org.gradle.api.artifacts.Configuration
8+
import org.gradle.api.file.FileCollection
59
import org.gradle.api.tasks.bundling.Jar
610
import com.github.jrubygradle.GemUtils
711

@@ -25,10 +29,10 @@ class JRubyJarConfigurator {
2529
@PackageScope
2630
static void afterEvaluateAction( Project project ) {
2731
project.tasks.withType(Jar) { t ->
28-
if(t.manifest.attributes.containsKey('Main-Class')) {
29-
if(t.manifest.attributes.'Main-Class' == JRubyJarConfigurator.DEFAULT_MAIN_CLASS) {
32+
if (t.manifest.attributes.containsKey('Main-Class')) {
33+
if (t.manifest.attributes.'Main-Class' == JRubyJarConfigurator.DEFAULT_MAIN_CLASS) {
3034
t.with {
31-
from({project.configurations.jrubyEmbeds.collect {project.zipTree(it)}}) {
35+
from({ project.configurations.jrubyEmbeds.collect { project.zipTree(it) } }) {
3236
include '**'
3337
exclude '**/WarMain.class'
3438
}
@@ -57,11 +61,17 @@ class JRubyJarConfigurator {
5761
*
5862
* @param className Name of main class
5963
*/
64+
@Incubating
6065
void mainClass(final String className) {
66+
maybeAddExtraManifest()
6167
archive.with {
6268
manifest {
6369
attributes 'Main-Class': className
6470
}
71+
metaInf {
72+
from { this.getDependencies() }
73+
into 'lib'
74+
}
6575
}
6676
}
6777

@@ -90,14 +100,73 @@ class JRubyJarConfigurator {
90100
/** Makes the executable by adding a default main class
91101
*
92102
*/
103+
@Incubating
93104
void defaultMainClass() {
94105
mainClass(DEFAULT_MAIN_CLASS)
95106
}
96107

108+
/** Adds a configuration to the list of dependencies that will be packed when creating an executable jar
109+
* This method is ignored if {@code mainClass} is not set.
110+
*
111+
* @param name Name of configuration
112+
*/
113+
@Incubating
114+
void configuration(String name) {
115+
this.configurations.add(archive.project.configurations.getByName(name))
116+
}
117+
118+
/** Adds a configuration to the list of dependencies that will be packed when creating an executable jar
119+
* This method is ignored if {@code mainClass} is not set.
120+
*
121+
* @param name Configuration
122+
*/
123+
@Incubating
124+
void configuration(Configuration config) {
125+
this.configurations.add(config)
126+
}
97127

98128
private JRubyJarConfigurator(Jar a) {
99129
archive = a
100130
}
101131

132+
private Set<File> getDependencies() {
133+
Set<File> tmp = []
134+
if(configurations == null) {
135+
archive.project.configurations.each { Configuration cfg ->
136+
if( ['jrubyJar','compile','runtime'].contains(cfg.name) ) {
137+
tmp.addAll(cfg.files)
138+
}
139+
}
140+
} else {
141+
configurations.each {
142+
tmp.addAll(it.files)
143+
}
144+
}
145+
return tmp
146+
}
147+
148+
private void maybeAddExtraManifest() {
149+
if(extraManifest == null) {
150+
extraManifest = new File(archive.project.buildDir,extraManifestName)
151+
String taskName = "${archive.name}ExtraManifest"
152+
Task task = archive.project.tasks.create(taskName)
153+
task << {
154+
String libs = task.inputs.files.collect { File f -> "lib/${f.name}" }.join(' ')
155+
extraManifest.parentFile.mkdirs()
156+
extraManifest.text = "Class-Path: ${libs}\n"
157+
}
158+
task.outputs.file(extraManifest)
159+
task.inputs.files({this.getDependencies()})
160+
archive.dependsOn task
161+
archive.manifest.from({task.outputs.files.singleFile})
162+
}
163+
}
164+
165+
private String getExtraManifestName() {
166+
"tmp/${archive.name}-extraManifest.mf"
167+
}
168+
102169
private Jar archive
170+
private List<Configuration> configurations
171+
private File extraManifest
103172
}

src/main/groovy/com/github/jrubygradle/jar/JRubyJarPlugin.groovy

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class JRubyJarPlugin implements Plugin<Project> {
1515

1616
project.apply plugin : 'com.github.jruby-gradle.base'
1717
project.configurations.maybeCreate('jrubyEmbeds')
18+
project.configurations.maybeCreate('jrubyJar')
1819

1920
// In order to update the testing cycle we need to tell unit tests where to
2021
// find GEMs. We are assuming that if someone includes this plugin, that they
@@ -58,6 +59,12 @@ class JRubyJarPlugin implements Plugin<Project> {
5859
project.afterEvaluate {
5960
WarblerBootstrap.addDependency(project)
6061
JRubyJarConfigurator.afterEvaluateAction(project)
62+
63+
project.dependencies {
64+
jrubyJar group: 'org.jruby', name: 'jruby-complete', version: project.jruby.defaultVersion
65+
}
66+
67+
6168
}
6269
}
6370

src/test/groovy/com/github/jrubygradle/jar/JRubyJarPluginSpec.groovy

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ class JRubyJarPluginSpec extends Specification {
4242
jarTask = project.task('JarJar', type: Jar)
4343
}
4444

45+
def "Checking configurations exist"() {
46+
given:
47+
def cfg = project.configurations
48+
49+
expect:
50+
cfg.getByName('jrubyEmbeds')
51+
cfg.getByName('jrubyJar')
52+
}
53+
4554
def "Adding a fake file as if it is a gem layout"() {
4655
when: 'We configure the jar task with jruby data'
4756
new File(TESTROOT,'fake.txt').text = 'fake.content'
@@ -124,17 +133,20 @@ class JRubyJarPluginSpec extends Specification {
124133

125134
def "Building a Jar"() {
126135
given: "A local repository"
136+
final String jrubyTestVersion = '1.7.15'
127137
File expectedDir= new File(TESTROOT,'libs/')
128138
expectedDir.mkdirs()
129139
File expectedJar= new File(expectedDir,'test.jar')
130140
project.jruby.gemInstallDir = new File(TESTROOT,'fakeGemDir').absolutePath
141+
131142
new File(project.jruby.gemInstallDir,'gems').mkdirs()
132143
new File(project.jruby.gemInstallDir,'gems/fake.txt').text = 'fake.content'
133144

134145
project.with {
135146
jruby {
136147
defaultRepositories = false
137148
warblerBootstrapVersion = '0.1.0'
149+
defaultVersion = jrubyTestVersion
138150
}
139151
repositories {
140152
ivy {
@@ -144,6 +156,9 @@ class JRubyJarPluginSpec extends Specification {
144156
}
145157
}
146158
}
159+
dependencies {
160+
jrubyJar 'org.spockframework:spock-core:0.7-groovy-2.0'
161+
}
147162
}
148163

149164
when: "I set the default main class"
@@ -158,11 +173,21 @@ class JRubyJarPluginSpec extends Specification {
158173
project.evaluate()
159174

160175
and: "I actually build the JAR"
176+
project.tasks.getByName("${jarTask.name}ExtraManifest").execute()
177+
161178
jarTask.copy()
179+
def builtJar = fileNames(project.zipTree(expectedJar))
162180

163181
then: "I expect to see the JarMain.class embedded in the JAR"
164182
expectedJar.exists()
165-
fileNames(project.zipTree(expectedJar)).contains('com/lookout/jruby/JarMain.class')
166-
!fileNames(project.zipTree(expectedJar)).contains('com/lookout/jruby/WarMain.class')
183+
builtJar.contains('com/lookout/jruby/JarMain.class')
184+
!builtJar.contains('com/lookout/jruby/WarMain.class')
185+
186+
and: "I expect to see jruby-complete packed in libs"
187+
builtJar.contains("META-INF/lib/jruby-complete-${jrubyTestVersion}.jar".toString())
188+
189+
and: "I expect to see manifest to include it"
190+
jarTask.manifest.effectiveManifest.attributes['Class-Path']?.contains("lib/jruby-complete-${jrubyTestVersion}.jar".toString())
191+
167192
}
168193
}

0 commit comments

Comments
 (0)