Twitterで@ruimoさんがつぶやかれていたので色々試してみた結果をまとめておきます。
経緯
@ruimo 単にdependenciesで対象ライブラリをtestRuntimeの方に追加する、ではだめですか?
— Yasuharu Nakano (@nobeans) 2014, 4月 17
@ruimo なるほどー。 http://t.co/erMFnUOFyp とか http://t.co/bCT0piyH8F の辺が参考になりそうな気がします。
— Yasuharu Nakano (@nobeans) 2014, 4月 17
@ruimo http://t.co/bCT0piyH8F の質問にあるコードのようにwarプロジェクトのdependenciesの中で、子プロジェクトごとのexclude設定をすれば除外できますが、それも数の問題で煩雑、という話ですかね
— Yasuharu Nakano (@nobeans) 2014, 4月 17
@ruimo あーなるほど。結局war化するときに除外したいだけなのにexcludeがサポートされてない(?)ので困った感じですね
— Yasuharu Nakano (@nobeans) 2014, 4月 17
解決案
案1 標準APIで一応あるはずのexclude/excludes (効かない)
warタスクでもexcludesによるパターン除外が効けば良いんですけど、どうもできない感じですね...。パターン指定の仕方が悪いのかしら。Antパターン指定とかで試してみたんですが、何にも変化なしな感じ。
war {
excludes = ["**/hoge*"]
}
案2 warタスクで自前でフィルタリング
結局「warに入らなくしたいだけ」なので、余計なruntimeなどをいじるのではなくwarに入れる処理のことろで自前フィルタリングするのが良さげです。
war {
// 自前でフィルタリングすると一応除外できた。
// この場合、推移的依存関係は使えないのですべての除外Jarを指定しなければならない。
def myExcludes = ["hogehoge", "foofoo"]
classpath = classpath.collect { file ->
// 除外リスト内のパタンと部分一致に入っているものはnullにしておいて後でfindAllで除外する。
myExcludes.any { file =~ it } ? null : file
}.findAll { it } // null除外
}
どうでもいいけど、Groovyコレクション操作でfindAllの反対が欲しいなぁ。removeAllはJava標準APIに引きずられて破壊的なのが駄目な感じ。
で、この方式でWarに入るJarは除外できます。ただし、これだと推移的依存関係は辿れなくて、ファイル名マッチングをするしかないので、すべての除外Jarを指定しなければならないのが面倒です。上記でfoofooが実はbarbarにも依存してたりすると、Warの中にbarbarだけが含まれてしまいます。
案3 warタスクで自前でフィルタリング(推移的除外関係の自動解決版) (ただし、中途半端)
で、ちょっとトリッキーだけど解決策としてこんなのを考えてみました。
configurations {
excludeFromWar
}
dependencies {
//...
// warタスクでの自前フィルタリングで、推移的依存関係を全て列挙したくないので、
// このように除外目的のconfigurationsを作ってしまうと楽な気がする。
excludeFromWar 'hoge:hogehoge:1.0'
excludeFromWar 'foo:foofoo:1.2'
}
war {
// 推移的依存関係を自動的に辿れるように除外用のconfigurationsを作ると便利。
classpath = classpath.collect { file ->
// 除外リスト内のパタンと部分一致に入っているものはnullにしておいて後でfindAllで除外する。
configurations.excludeFromWar.contains(file) ? null : file
}.findAll { it } // null除外
}
こうすると、たとえfoofooが実はbarbarに依存していても、foofooの除外指定からたどられて除外対象に含まれるのでbarbarも除外できます。
でも、これもまだ半端な実装になっていて、Warに含めたいbazbazがbarbarに依存していると、foofooの推移的除外関係から単純にbarbarを除外しようとするので、Warの中にbarbarが含まれず、bazbazがまともに動かなくなってしまいます。もうちょっとその辺まじめに実装すればよいかもしれませんが、特定プロジェクトの都合で確実に除外指定したいブツが分かってるなら、案2あたりで止めておく方がよさげ。
案4 providedRuntimeを使う方法 (ボツ)
Warプラグインで用意されているprovidedRutimeを使えば、もしかしてこれだけで良かったりする?
War プラグインは providedCompile と providedRuntime の2つの依存構成を追加します。 これらの構成は WAR アーカイブには追加されないという点を除けば、それぞれ compile、runtime と同じスコープを持ちます。 provided 構成が推移的に機能することは特筆すべき点です。 http://gradle.monochromeroad.com/docs/userguide/war_plugin.html
configurations {
// まずはwarで不要なものをruntimeから除外してしまう。
runtime.exclude module: 'hogehoge'
runtime.exclude module: 'foofoo'
}
dependencies {
// warには不要であるがruntime/testRuntimeで必要なJarをあらためてprovidedRuntimeで宣言しなおす。
providedRuntime 'hoge:hogehoge:1.0'
providedRuntime 'foo:foofoo:1.2'
}
ruimoさんのユースケースだとこれで良いような気がしてます(動作確認してない)。
案5 providedRuntimeを使う方法(DRY版) (ボツ)
だいぶシンプルになりましたが、除外設定とprovidedRuntime指定の2箇所でJarの情報が分散してるのがDRYではありませんね。でも、そこは心配ありません。Groovyなのでこんな書き方ができます。
// こうして一元管理もできる。そう、Groovyならね。
def excludesFromWar = [
[group: 'hoge', module: 'hogehoge', version: '1.0'],
[group: 'foo', module: 'foofoo', version: '1.2'],
]
configurations {
// まずはwarで不要なものをruntimeから除外してしまう。
excludesFromWar.each { runtime.exclude it }
}
dependencies {
// warには不要であるがruntime/testRuntimeで必要なJarをあらためてprovidedRuntimeで宣言しなおす。
excludesFromWar.each { providedRuntime "${it.group}:${it.module}:${it.version}" }
}
案6 直接testRuntime/testCompileに再設定する方法
providedRuntimeはテスト実行時(testRuntime)には入ってこないようです。 と、よく考えたらテストで使えれば良いのだから直接testRuntime、コンパイル時も必要ならtestCompileに設定すればいいじゃないか、と気がつきました。なお、testCompileにいれておけばtestRuntimeにも含まれます。
ということで、たぶんこれでFAな気がするんですがどうでしょうかね。
// こうして一元管理もできる。そう、Groovyならね。
def excludesFromWar = [
[group: 'org.codehaus.groovy', module: 'groovy-all', version: '2.1.6'],
[group: 'org.spockframework', module: 'spock-core', version: '0.7-groovy-2.0'],
]
configurations {
// まずはwarで不要なものをruntimeから除外してしまう。
excludesFromWar.each { runtime.exclude it }
}
dependencies {
compile project(':subproject-a')
// warには不要であるがtestCompileで必要なJarをあらためて追加する。
excludesFromWar.each { testCompile "${it.group}:${it.module}:${it.version}" }
}
できあがるWarファイルの中身: (groovy-allもspock-coreも含まれていない)
Length Date Time Name
-------- ---- ---- ----
0 04-17-14 21:36 META-INF/
25 04-17-14 21:36 META-INF/MANIFEST.MF
0 04-17-14 21:36 WEB-INF/
0 04-17-14 21:36 WEB-INF/lib/
2329 04-17-14 21:35 WEB-INF/lib/subproject-a.jar
-------- -------
2354 5 files
Gradle上の依存関係:
$ gradle dep
:dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
archives - Configuration for archive artifacts.
No dependencies
compile - Compile classpath for source set 'main'.
\--- project :subproject-a
\--- org.codehaus.groovy:groovy-all:2.1.6
default - Configuration for default artifacts.
\--- project :subproject-a
groovy - The Groovy libraries to be used for this Groovy project. (Deprecated)
No dependencies
providedCompile - Additional compile classpath for libraries that should not be part of the WAR archive.
No dependencies
providedRuntime - Additional runtime classpath for libraries that should not be part of the WAR archive.
No dependencies
runtime - Runtime classpath for source set 'main'.
\--- project :subproject-a
testCompile - Compile classpath for source set 'test'.
+--- project :subproject-a
| \--- org.codehaus.groovy:groovy-all:2.1.6
+--- org.codehaus.groovy:groovy-all:2.1.6
\--- org.spockframework:spock-core:0.7-groovy-2.0
+--- junit:junit-dep:4.10
| \--- org.hamcrest:hamcrest-core:1.1 -> 1.3
+--- org.codehaus.groovy:groovy-all:2.0.5 -> 2.1.6
\--- org.hamcrest:hamcrest-core:1.3
testRuntime - Runtime classpath for source set 'test'.
\--- project :subproject-a
参考コード
実際にテストまで動くサンプルは https://github.com/nobeans/gradle-war-excludes-jar-sample/ においてあります。