GradleのEclipseプラグインの処理結果を変更

2018年7月15日

GradleのEclipseプラグインは、build.gradleに記述した通りに、Eclipseの.projectファイル.classpathファイルを書き換えてくれるので非常に便利なのだが、しかし意図しない状態になってしまうことがある。これを修正する。

GradleのEclipseプラグインの問題

ソースフォルダを複数回記述してしまう

ビルドの都合で、以下のように同じソースフォルダを複数記述している。

sourceSets {
  main {
    java      { 
      srcDir 'src_common'; exclude '**/*Test.java'; 
      ...
    }
  }
  foobar {
    java      { 
      srcDir 'src_common'; exclude '**/*Test.java'; 
    }
  }
}

すると、このプラグインは、Eclipseのビルドパスに同じsrc_commonというソースフォルダを複数回記述してしまう。これはEclipse側としては気に入らないため、当然エラー状態になり、手で修正せざるをえない。

excludeを入れ込んでしまう

前述の例のようにソースフォルダに「exclude ‘**/*Test.java’」という記述をしておくと、これまたご丁寧なことに、Eclipseのビルドパスにもこの記述を再現してしまう。これらの*Test.javaというクラスは、ビルド時には見えなくなってほしいのだが、開発環境では見えていて欲しいのであるにも関わらず。

Allow output folders for source foldersがONになっている

ビルドパスの中のこの指定は複数のソースフォルダがある場合に、そのコンパイル結果を一つにせず、別々のフォルダに出力するという意味なのだが、これが勝手にONになる。OFFにさせる方法は無いようだ。

すべてのbuild.gradleに共通のリンクファイルを.projectに記述したい。

これについては後の方で説明する。

eclipseプラグイン動作の変更

他にやり方はあるかと思うが、Gradleが良くわからない私にとって最も簡単な方法をとった。とりあえずこれで行くことにする。

まず、build.gradleにeclipseプラグインに関する動作変更を記述する。.classpath、.projectの両者についてwhenMergedの時点で修正するクロージャを指定する。

eclipse {
  // .classpathについての変更
  classpath {
    // ソースもダウンロード
    downloadSources=true

    // 出力フォルダの指定
    defaultOutputDir = file('bin')

    // whenMergedで修正する    
    file whenMergedEclipseClassPathMod
  }   

  // .projectについての変更  
  project {
    // whenMergedで修正する
    file whenMergedEclipseProjectMod
  }   
}

.classpathの変更

.classpathに関する変更クロージャwhenMergedEclipseClassPathModは以下のようなものである。
なお、この処理は共通化するために関数にはしていない。クロージャとして実現している。

/** eclipseプラグインのprojectについてwhenMergedの時点で修正を行う */
whenMergedEclipseClassPathMod = {
  whenMerged {cp->
    removeDuplicatedSources(cp)
    removeOutputFoldersForSourceFolders(cp)
    removeAllExcludes(cp)
  }
}

/* sourceSetsで同じフォルダを複数回指定すると、ご丁寧にもその分だけ
 * classPathに入れてしまう。重複した分を削除する
 */
removeDuplicatedSources = { cp->
  def newList = new ArrayList()
  def duplicated = new HashSet()
  cp.entries.each { e-> 
    if (e.kind != 'src') {
      newList.add(e);
      return
    }
    if (duplicated.contains(e.path))
      return;
    newList.add(e);
    duplicated.add(e.path);          
  }
  cp.entries = newList;
}

// srcDir 'src'; exclude '**/*Test.java';のexcludeを削除する。
// IDE上ではテストユニットが見えていて欲しい
removeAllExcludes = { cp->
  cp.entries.findAll{ it.kind == 'src' }.each{
    it.setExcludes(new ArrayList())  
  }
}

/* 勝手に指定されたoutputを削除する。
 * これがあると、勝手にAllow output folders for source foldersがONになってしまう。
 */
removeOutputFoldersForSourceFolders = { cp->
  cp.entries.findAll{ it.kind == 'src' }.each{
    it.output = null
  }
}

.projectの変更

.projectに関する変更のためのクロージャは以下のようなものである。Eclipseの各プロジェクトに、共通ファイルへのリンクをつけるものだが、これについては後で説明する。

eclipseProjectMod = { proj-> 
  addCommonGradle(proj)
}
addCommonGradle = { proj ->
  proj.linkedResources.add(new org.gradle.plugins.ide.eclipse.model.Link(
  'common.gradle', '1', 'C:/Foo/Bar/common.gradle', null));
}

これは、単純にlinkedResourcesにエントリを追加するだけなので、以下のように記述してもよい。

eclipse {
  project {
    linkedResource name: 'common.gradle', type: '1', location: 'C:/Foo/Bar/common.gradle'   
  }
}

eclipseプラグインへの修正クロージャを共通ファイルにまとめ、Eclipseプロジェクトから閲覧する

これらのeclipseプラグインについての動作修正処理を、各プロジェクトのbuild.gradleに毎回記述するのも面倒なので、共通ファイル化する。これについては、build.gradleの共通設定に記述した。

つまり、common.gradleは、各プロジェクトから独立した存在であり、ユーザホーム以外の場所に設置することができる。

しかし、各Eclipseプロジェクトからこれが見えて欲しいのである。各々のEclipseプロジェクトに取り組んでいるあいだに、新たなものをcommon.gradleに入れたくなるかもしれない。

Eclipse IDEから行う方法とその結果

これをEclipseIDEで行うには次のようにする。

まずプロジェクトを右クリックし、Fileを選択する。

Advancedをクリックし、Link to file in the file systemをチェックする。

後はBrowseで、ファイルを指定すればよい。すると、Eclipseプロジェクト中にcommon.gradleが「リンクされた状態」で現れる。つまり、ファイルのコピーではなく、いわばプロジェクト外のファイルを直接見れるようになる。

.project再作成の際に消える問題

しかし、GradleのEclipseプラグインが機能すると、こんな事情は知るよしも無いので、手作業で作成したリンクを消しさってしまう。

そのために、先のeclipseProjectModクロージャを作成した。.projectファイルの再作成に際し、常にこのリンクを保持する。

再度だが、単に以下のように書いてもよい。

eclipse {
  project {
    linkedResource name: 'common.gradle', type: '1', location: 'C:/Foo/Bar/common.gradle'   
  }
}