Corredor

ウェブ、プログラミングの勉強メモ。

Ant から Eclipse ワークスペースのリフレッシュやフルビルドを行う

今まで Eclipse の「プロジェクト設定に従い WAR ファイルを作成」メニューから WAR ファイルを作っていた Tomcat プロジェクトがキモいので、Ant でビルドしようと思って苦戦した話。

javac タスクが制御できなかった

Ant の war タスクで class ファイルを格納する前に javac タスクでコンパイルしなきゃねー、java ファイル以外は src ディレクトリから classes ディレクトリへ copy タスクでコピーしなきゃねー、とか思ってたんだけど、javac タスクがどうしてもうまく行かなくて諦めました。

まず javac タスクで動かす JDK を「外部ツールの構成」で「ワークスペースと同じ JRE を使用」にしているのに、「インストール済みの JRE」で選ばれている JDK ではなく普通に環境変数 PATH に入ってる別の JRE が選ばれてしまい、コンパイルができなかった。

環境変数 JAVACMD というものを使うと、その環境変数を Ant が自動認識してコンパイラとして使用すると聞いたが、それもなんかうまく動かず。

使用する JDK はともかく、コンパイルがとりあえずできるようになってからも、ファイル数が多いと OutOfMemory で落ちてしまう。Ant は異常終了するときにエラーメッセージが何も出ないパターンが多く、どうして落ちたか分かりづらかった。

また、なんとか javac でコンパイルできたファイルを見てみると、Eclipse がビルドして生成する class ファイルとは色々と中身が違った。

  • Eclipse から起動する Ant の javac はワケが分からないなぁー

というのと、

  • Eclipse が「プロジェクト設定に従い WAR ファイルを作成」メニューで作った WAR に入る class ファイルはどうやってコンパイルされてんだろうー

というので途方に暮れていた。

考え方を変えた

そんなワケで考え方を変えてみた。

Ant の javac でコンパイルするのは諦めよう。んで、Eclipse が class ファイルを生成するのは「プロジェクトのビルド」の時だ。普段は「自動的にビルド」にチェックを入れているので基本的には最新の状態で class ファイルが生成されているはずだが、Ant から Eclipse のビルド処理を呼んであげられたら確実だよなぁ。

というところでググってたら、ドンピシャなモノを発見。

eclipse.incrementalBuild

eclipse.incrementalBuild という Eclipse 独自の Ant タスクがあり、このタスクを使えば特定のプロジェクト、もしくはワークスペース全体に対し、ビルドやクリーンの命令が出せる

Eclipse のフルビルドは、src ディレクトリにある .java 以外のファイルを自動的に classes ディレクトリにコピーしてくれるので、そこも優秀。

基本的な書式は以下のとおり。

<eclipse.incrementalBuild project="myProject" kind="full"/>
  • project 属性はプロジェクト名を指定する。書かなければワークスペース全体が対象になる。
  • kind 属性には incremental (デフォルト)、clean、full の3種類のいずれかを指定する。

eclipse.refreshLocal

また、eclipse.refreshLocal というタスクでプロジェクトのリフレッシュもできるので、ファイルの追加変更等を確実に認識させてからビルド、といったことができるようになる。

こちらの基本的な書式は以下。

<eclipse.refreshLocal resource="myProject" depth="infinite"/>
  • resource 属性はプロジェクト名を指定する。
  • depth 属性は zero、one、infinite が選べる様子。階層の掘り下げ方っぽいので、とりあえず infinite にしとく。

作った

というわけで、以下のようなタスクで WAR ファイルを生成すると、「プロジェクト設定に従い WAR ファイルを作成」で生成した WAR とほぼ同一の内容になる。

<project name="myAnt" default="createWar" basedir=".">
  <!-- ルートディレクトリの設定 -->
  <dirname property="base" file="${ant.file}"/>
  
  <!-- プロジェクトのビルド -->
  <target name="buildProject">
    <!-- 予めプロジェクトをリフレッシュしておく -->
    <eclipse.refreshLocal resource="myProject" depth="infinite"/>
    <!-- プロジェクトをフルビルドする -->
    <eclipse.incrementalBuild project="myProject" kind="full"/>
  </target>
  <!-- WAR ファイルを作る -->
  <target name="createWar" depends="buildProject">
    <!-- 前回の Ant 実行で作成されている WAR ファイルを消しておく -->
    <delete file="${base}/myProject.war"/>
    <!-- WAR ファイルを作る -->
    <war destfile="${base}/myProject.war" webxml="${base}/WEB-INF/web.xml">
      <classes dir="${base}WEB-INF/classes"/>
      <lib dir="${base}/WEB-INF"/>
      <fileset dir="${base"}>
        <exclude name="**/src/**/*"/>
        <exclude name="**/src"/>
        <exclude name="**/work/**/*"/>
        <exclude name="**/work"/>
      </fileset>
    </war>
  </target>
</project>

exclude 要素での除外は適宜行う。不要なものは入れないようにしておこう。

「プロジェクト設定に従い WAR ファイルを作成」で生成した WAR と差分が発生しうる場所は、exclude 要素で除外したファイルと、Ant の war タスクはマニフェストファイルを自動的に作成して格納するので、元のプロジェクトにマニフェストファイルがなかった場合は差分になる。

しかしこれらはアプリの動作には関係しないファイルたちで、class ファイルなんかは中身も同一なので、このまま Ant から作った WAR をデプロイして使うことができた。

参考