14.4.一発コンパイル
 14.4.1.プログラムを書く
 14.4.2.従来のコンパイル
 14.4.3.従来の方法の落とし穴
 14.4.4.build.xmlの作成
 14.4.5.いよいよAntでコンパイル
 


14.4.一発コンパイル

 14.4.1.プログラムを書く
   今回は前回のプログラムを拡張して、複数のパッケージを使ったサンプルプログラムを作成します。
 Javaのプログラムならどんなものでもかまいませんが、例として以下のプログラムを使用します。
 
 とりあえず、以下の4つのプログラムを入力して以下のディレクトリ構造のようにに保存しましょう。
 
  c:\java\hello2
      |-hello1.java
      |-sample2
      |  |-hello2.java
      |-sample3
      |  |-hello3.java
      |-sample4
      |  |-hello4.java
 

 
hello1.java
import sample2.hello2;

public class hello1{
  public void print(){
    System.out.println("Hello1");
  }
  
  public static void main(String args[]){
    hello1 h1=new hello1();
    hello2 h2=new hello2();
    
    h1.print();
    h2.print();
  }
}

 
hello2.java
package sample2;

import sample3.hello3;

public class hello2{

  public hello2(){}
  
  public void print(){
    System.out.println("Hello2");
    
    hello3 h3=new hello3();
    h3.print();
  }
}

 
hello3.java
package sample3;

public class hello3{
  
  public hello3(){}
  
  public void print(){
    System.out.println("Hello3");
  }
}

 
hello4.java
package sample4;

public class hello4{
  public hello4(){}
  
  public void print(){
    System.out.println("Hello4");
  }
}

 それでは、簡単に上のプログラムの説明をします。
 まず、「hello1.java」は「hello2」を内部で使います。そして、「hello2」というクラスは「sample2」というパッケージに入っています。そのため「hello1.java」では1行目でパッケージの中の「hello2」をimportしています。同じように、「hello2.java」の内部では「sample3」パッケージの中の「hello3」クラスを使用します。
 一方で「hello1.java」「hello2.java」「hello3.java」は名前こそ違うものの基本的には同じで、何もしないコンストラクタと「hello?」という文字列を出力する「print」メソッドをもっています。
 また、「sample4」パッケージ内の「hello4」は「hello1.java」では使用されませんし、他の二つのクラスからも使用されません。

 14.4.2.従来のコンパイル
    まずは、Antを使わずに上のプログラム群をコンパイルしてみましょう。普通の人であれば「hello1.java」を以下のようにコンパイルするでしょう。
C:\java\hello2>cd \java\hallo2

C:\java\hello2>javac hello1.java

 すると、以下のように「hello1.java」はコンパイルされて「hello1.class」が出来上がります。
C:\java\hello2>dir
C:\java\hello2 のディレクトリ

2003/04/24 19:38 213 build.xml
2003/04/30 12:31 564 hello1.class
2003/04/25 00:05 272 hello1.java
2003/04/30 12:10 <DIR> sample2
2003/04/30 12:10 <DIR> sample3
2003/04/30 12:09 <DIR> sample4

 そして、さらに「sample2」「sample3」ディレクトリの中の「hello2.java」と「hello3.java」もコンパイルされてそれぞれ「.class」ファイルが作成されていることがわかります。
C:\java\hello2>cd sample2
C:\java\hello2\sample2>dir
2003/04/30 12:31 396 hello2.class
2003/04/24 23:57 128 hello2.java

C:\java\hello2>cd ../sample3
C:\java\hello2\sample3>dir
2003/04/30 12:31 396 hello3.class
2003/04/24 23:57 128 hello3.java

 しかしながら、「sample4」ディレクトリを見てみると「hello4.java」はコンパイルされていないことがわかります。
C:\java\hello2\sample2>cd ../sample4
C:\java\hello2\sample4>dir
2003/04/24 23:56 126 hello4.java

 これは、「hello1.java」をコンパイルするときに「hello2.class」がコンパイルされ、「hello2.java」をコンパイルするときに「hello3.class」が必要なためコンパイラが必要に迫らせ自動的に「hello2.java」と「hello3.java」をコンパイルしたためです。よって使用されない「hello4.java」はコンパイルする必要がなかったため、コンパイルされなかったのです。

 それでは、以下のようにしてコンパイルした「hello1.class」を実行してみましょう。
C:\java\hello2\sample4>cd ../
C:\java\hello2>java hello1
Hello1
Hello2
Hello3

 実行結果は予想通りになったと思います。「hello1.java」はちゃんと使用できたのだから、これで問題ないじゃないかと思う人がいるかもしれません。しかしながら次のような場合にうまくいかないことがあるのです。

 14.4.3.従来の方法の落とし穴
   前節では、一見うまくいったように思えますが、実は落とし穴があります。この節ではその後し穴を実験してみることにします。
まず、先ほどの「hello3.java」を以下のように変更します。
hello3.java
package sample3;

public class hello3{
  
  public hello3(){}
  
  public void print(){
    System.out.println("Hello3-update");
  }
}

 次に、上と同じようにして「hello1.java」をコンパイルしなおします。通常であれば、先ほどと同じように「hello1.java」をコンパイルすれば必要に迫られて変更した「hello3.java」もコンパイルしてくれるはずです。
C:\java\hello2>cd \java\hallo2

C:\java\hello2>javac hello1.java

 それでは、早速実行してみましょう。
 
C:\java\hello2\sample4>cd ../
C:\java\hello2>java hello1
Hello1
Hello2
Hello3

 さて、どうでしょう?「hello3.java」対して行った変更が反映されていません。
 これは、「hello1.java」をコンパイルしようと思った時に「hello2.class」がすでに存在し、かつその内容が変更されていなかったために「hello2.java」はコンパイルされませんでした。よって、変更された「hello3.java」はコンパイルが行われなかったのです。
 このように、階層的にコンパイルしていくJavaのコンパイラでは、変更したはずのファイルがコンパイルされないことがありえるのです。
 実際に、筆者もJavaを使い始めたころにこの性質のせいで丸一日を棒に振ったことがあります(悲)
 
 少し整理しておくと、Javaのコンパイラを単体で使った場合
 ・コンパイルされないファイルがある
 ・変更がコンパイルされないことがある

 14.4.4.build.xmlの作成
    Antを使用するためには、build.xmlを記述しなければなりません。
 以下のbuild.xmlは前節のものと同じですが、これを入力して「c:\java\hello2」ディレクトリに保存しましょう。
build.xml
<?xml version="1.0" encoding="Shift_JIS"?>
<project name="hallo" default="compile" basedir=".">
  <target name="compile">
    <mkdir dir="classes" />
    <javac srcdir="." destdir="classes"/>
  </target>
</project>

 それでは、この内容を簡単に説明します。まず、一行目はAntにはまったく関係ありませんが、XMLで文章を書く場合に必須であるXMLのバージョンと文字コードの記述です。バージョンの方は今のところ1.0しかありませんのでそのまま書いておけばよいでしょう。そのあとのエンコーディングの指定はお使いの環境に合わせて変える必要があります。Windowsを使っている方はこの例のとおり「Shift_JIS」で大丈夫です、「Linux」などUNIX系のOSを使っている方は「euc_jp」にしてください。
 Antに関して意味があるのは、2行目以降の部分です。まず一番大きなタグは「project」です、これはその名のとおりプロジェクトを表すためのものでありBuild.xmlの中にたった一つだけ定義することができます。また、その属性には「name」と「default」と「basedir」があり「name」属性にはプロジェクトの名前を書きます、この名前は任意でよいでしょう。次の「default」属性は後で出てくるターゲットの名前を書き、何もパラメータを指定せずにAntを実行したときに行われる動作を指定します、ちなみに「All」を指定するとすべてのターゲットを実行するようになります。さらに「basedir」属性はBuild.xmlが置いてあるディレクトリからの相対パスを指定しますが、通常はカレントディレクトリ「.」で問題ないでしょう。
 次の「target」タグはAntの動作を定義するものです。「target」タグの属性である「compile」も任意の名前でよいですが、Antの使用時に手入力することが多いので自分でわかりやすい名前にするのがよいでしょう。
 「target」タグの中にはAntが実際に行う動作とそのための情報を欠きます。「mkdir」タグはもしも「classes」というディレクトリが存在しなかった場合にはディレクトリを作成することを示しています。また、次のタグが重要で「javac」タグはJavaのコンパイラを使用することを表し、その属性でソースファイルが置いてある場所「srcdir」とコンパイルしたクラスファイルを出力するディレクトリ「destdir」を指定します。
 

 14.4.5.いよいよAntでコンパイル
    通常コンパイルの場合に起こる不具合を頭にいれておいて、Antを使ってみましょう。
 以下のように入力して、すべてのをコマンド一つでコンパイルしてみましょう。
C:\java>cd hello2
C:\java\hello2>ant
Buildfile: build.xml

compile:
[mkdir] Created dir: C:\java\hello2\classes
[javac] Compiling 4 source files to C:\java\hello2\classes

BUILD SUCCESSFUL
Total time: 1 second

 それでは、コンパイルされたファイルを確認してみましょう。「c:\java\hello2\classes」ディレクトリ以下にソースファイル(hello1.java・hello2.java・hello3.java・hello4.java)をコンパイルしてできたクラスファイル「*.class」がパッケージごとに作成されていることがわかります。
 通常のコンパイラだけでは、必要最低限のものしかコンパイルされなかったのに対しAntでは指定されたディレクトリを再帰的に見に行き、そこにあるすべてのソースファイル(*.java)をコンパイルしてくれるのです。

 念のためコンパイルしたプログラムを実行して動作を確認してみてください。
C:\java\hello2>cd classes
C:\java\hello2\classes>java hello1
Hello1
Hello2
Hello3-update



 次に、もう一つの問題点がAntを使った場合には解決されていることを確かめます。上で変更したhello3.javaをもう一度変更して元に戻します。
hello3.java
package sample3;

public class hello3{
  
  public hello3(){}
  
  public void print(){
    System.out.println("Hello3");
  }
}

 修正ができたら、またAntを実行してコンパイルします。
C:\java\hello2\classes>cd ../
C:\java\hello2>ant
Buildfile: build.xml

compile:
[mkdir] Created dir: C:\java\hello2\classes
[javac] Compiling 4 source files to C:\java\hello2\classes

BUILD SUCCESSFUL
Total time: 1 second

 それでは、実行してみましょう。
C:\java\hello2>cd classes
C:\java\hello2\classes>java hello1
Hello1
Hello2
Hello3

 今回はちゃんと修正が変更されたことがわかります。このようにAntではファイルのタイムスタンプを見て修正があったソースファイルをコンパイルします。大規模なソフトウェアなどで非常にたくさんのソースファイルが存在する場合はAntのこの修正されたファイルだけコンパイルという機能は非常に役に立つでしょう。
 

 
   >>> 14.5.jarファイルを作ってみよう <<<


このドキュメントの間違い・誤字・脱字を発見された方は筆者までご連絡をいただけるとうれしいです。

また、私のわかる範囲でご質問にも答えようと思います。ご質問や疑問点がある方は気楽にメールをください。