EclipseClasspathの使い方

2018年7月13日

※GradleのEclipseプラグインをより制御する方法については、GradleのEclipseプラグインの処理結果を変更に記述した。

問題

eclipseプラグインを追加し、「gradle cleanEclipse eclipse」を行えば、勝手に依存ライブラリ等をダウンロードし、それをビルドパスに追加してくれる。

apply plugin: 'java'
apply plugin: 'eclipse'

しかし特殊なケース、例えば、compileやtestCompileではないconfigurationの依存ライブラリを入れるとか、JREコンテナの設定をいじるとかしたい場合はどうすれば良いのか?それを探求してみる。

参考情報

Gradleマニュアルより

以下は2018/2時点、Gradle4.5.1のEclipseClasspathマニュアルの一部の訳。

生成されるEclipseプロジェクトのためのビルドパスの設定。GenerateEclipseClasspathタスクによって使用され、Eclipseの.classpathファイルを生成する。

以下の例は様々なコンフィグオプションをデモする。心に留めておくべきとしては、すべてのプロパティが思慮深いデフォルト値を持つことであり、デフォルトが必要性にマッチしない場合にのみ明示的に設定すること。

apply plugin: 'java'
apply plugin: 'eclipse'

configurations {
  provided
  someBoringConfig
}

eclipse {
  // if you want parts of paths in resulting file to be replaced by variables (files):
  pathVariables 'GRADLE_HOME': file('/best/software/gradle'), 'TOMCAT_HOME': file('../tomcat')

  classpath {
    // Eclipseプロジェクトのクラスパスを拡張コンフィグを追加することにより変更することができる。
    plusConfigurations += [ configurations.provided ]

    // クラスパスからコンフィグを除去することもできる
    minusConfigurations += [ configurations.someBoringConfig ]

    // コンテナを追加したい場合
    containers 'someFriendlyContainer', 'andYetAnotherContainer'

    // クラス出力フォルダを変更したい場合
    defaultOutputDir = file('build-eclipse')

    // デフォルトでソースとjavaDocをダウンロードするか
    downloadSources = true
    downloadJavadoc = false
  }
}

特殊なケースでは、出力結果のXMLファイルに対する上級コンフィグを実行することができる。
また、Eclipseのプラグインが既存のコンフィグをマージするやり方に影響を与えることができる。これには、beforeMerged, whenMarkedクロージャを使う。

beforeMerged, whenMergedクロージャはクラスパスオブジェクトを受け取る。

以下は上級コンフィグの例:

apply plugin: 'java'
apply plugin: 'eclipse'

eclipse {
  classpath {
    file {
      // 結果のXMLにちょっかいを出す方法、どのようにでもできる。
      withXml {
        def node = it.asNode()
        node.appendNode('xml', 'is what I love')
      }

      // このクロージャは、.classpathコンテンツが既存のファイルからロードされた後
      // しかしGradleのビルド情報がマージされる以前に実行される。
      beforeMerged { classpath ->
        // ここでClasspathをいじることができる
      }

      // .classpathコンテンツが既存のファイルからロードされた後、Gradleビルド情報がマージされた後で実行される。
      whenMerged { classpath ->
        // ここでClasspathをいじることができる
      }
    }
  }
}

問題の解決方法

解決は以下の通り


sourceSets { main { java { // ビルド時には、すべての*Test.javaを排除するが、IDE上では排除しない。 srcDir 'src'; exclude '**/*Test.java'; } } } configurations { // builtinというコンフィギュレーションを使いたい builtin { transitive = false } } dependencies { // builtinは特定のフォルダの下全部のjarに依存する builtin fileTree(dir: 'lib-builtin', include: '*.jar') compile fileTree(dir: 'lib-ext', include: '*.jar') compile fileTree(dir: 'lib-ext/win32-win32-x86_64', include: '*.jar') testCompile group: 'junit', name: 'junit', version: '4.12' testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19' } eclipse { classpath { // builtinコンフィグを追加する plusConfigurations += [ configurations.builtin ] file { whenMerged { cp -> cp.entries.findAll { e -> if (e.kind == 'src') { // ※1.以下のexcludesの排除で説明 e.setExcludes(new ArrayList()) } if (e.kind == 'con' && e.path.startsWith('org.eclipse.jdt.launching.JRE_CONTAINER')) { // ※2.以下のaccessRulesの追加で説明 e.accessRules.add(new org.gradle.plugins.ide.eclipse.model.AccessRule('accessible', 'sun/misc/*')) } } } } } }

exlucdesの排除

ソースセットの定義に以下のように記述しているので、黙っているとEclipseのソースパス設定にも反映されてしまう。

sourceSets {
  main {
    java {
      // ビルド時には、すべての*Test.javaを排除するが、IDE上では排除しない。
      srcDir 'src'; exclude '**/*Test.java';
    }
  }
}

ここから生成されるEclipseの.projectファイル上では以下として現れる。

    <classpathentry output="bin/main" excluding="**/*Test.java" kind="src" path="src">
        <attributes>
            <attribute name="gradle_scope" value="main"/>
            <attribute name="gradle_used_by_scope" value="main,test"/>
        </attributes>
    </classpathentry>

※1.はこれをやめさせるためのもの、.projectのXMLではexcludingという名前だが、Gradleではexcludesという名前になっている。

Class SourceFolderを参考にする。

accessRuleの追加

※2.部分を普通にEclipse-IDEから設定する場合は以下になる。つまり、sun/misc/*というパッケージにエラー無くアクセスするという指定。

これを設定すると.classpathのJREコンテナは以下の状態になる。

    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/">
        <accessrules>
            <accessrule kind="accessible" pattern="sun/misc/*"/>
        </accessrules>
    </classpathentry>

これを参考にし、かつGradleのAPIを参照して、上記の設定になった。