実用的なGroovy: GroovyによるAntスクリプト

AntとGroovyを組み合わせ、より表現力豊かな、そして制御可能なビルドを作る

AntとMavenは共にビルド・プロセスの世界を支配していますが、XMLは時に、構成フォーマットとして表現力に欠ける場合があります。Groovyの実用的なアプリケーションに関する新シリーズの第2回目として、今回はAndrew GloverがGroovyのビルダー・ユーティリティーを紹介します。このビルダー・ユーティリティーを使うと、GroovyをAntやMavenと組み合わせて、より表現力のある、制御しやすいビルドを作ることができるようになります。

Andrew Glover, CTO, Vanward Technologies

Andrew GloverAndrew Gloverは合衆国ワシントン特別区にある、Vanward TechnologiesのCTO(最高技術責任者)です。Vanward Technologiesは自動化テスト・フレームワークの構築を専門としており、ソフトウェアのバグ発生数や統合時間やテスト時間の減少、また全体的なコード安定性改善に貢献しています。



2004年 12月 14日

Antは広く使われていること、またその使いやすさから、Javaプロジェクト用のビルド・ツールとして他を圧倒しています。ビルドの領域における新参者であるMavenでさえ、その強力さはAntでの教訓に由来しています。ただし、この2つのツールに欠けているのは拡張性です。AntとMavenを開発の最前線に送り出す上でXMLの移植性が大きな役割を果たしているとはいえ、ビルドの構成フォーマットとしてのXMLは、結果としてのビルド・プロセスの表現力をいくらか制限してしまっています。

例えばAntでもMavenでも条件論理は可能なのですが、XMLを使用すると、やや面倒です。さらに、カスタムのタスクを定義することでAntのビルド・プロセスを拡張することは可能なのですが、そうしてしまうと、普通はアプリケーションの振る舞いを限定してしまうことになります。そこでこの記事では、より表現力を増すために、そして、よりきめ細かくビルド・プロセスの振る舞いを制御するために、Mavenの内部でGroovyとAntをどのように組み合わせるかを説明します。

すぐに分かると思いますが、GroovyによってAntもMavenも非常に機能強化されます。そして都合の良いことに、XMLが取り残してしまうところをGroovyが拾い上げることが多いのです。実際、Groovyスクリプト内部でのAnt利用をサポートする強力な新ツールであるAntBuilderが導入されたことから見ると、Groovyを作った人達は、XMLでAntやMavenのビルド・プロセスを実装することに苦痛を感じていたに違いありません。

実用的なGroovyシリーズの今回の記事では、ビルドの構成フォーマットとしてXMLの代わりにGroovyを使うことによって、いかに容易にビルド・プロセスを機能強化できるかを説明します。クロージャー(closure)はGroovyの重要機能であり、この言語の持つ拡張性の中心をなすものです。そこで先に進む前に、まずクロージャーの簡単な復習から始めることにしましょう。

クロージャーの簡単な復習

Groovyはその先輩達と同じように、名前のないファンクション(nameless functions)、あるいはクロージャーの概念をサポートしています。PythonやJythonでコーディングをしたことのある人であれば、クロージャーには慣れているでしょう(PythonやJythonでは、クロージャーはキーワードlambdaを使って導入されます)。Rubyでは、ブロックやクロージャーを利用しないとすると、本気でスクリプトを書く必要があります。Java言語でも、匿名内部クラスで、限定的な形ながら、名前のないファンクションをサポートしています。

Groovyにおいてクロージャーは、振る舞いをカプセル化する、名前のないファーストクラス・ファンクションです。Rubyの作成者であるYukihiro Matsumotoは、こうした強力なファーストクラス・オブジェクトを「別のファンクションに渡すと、そのファンクションが渡された[クロージャー]を呼び出すことができる」と述べて、クロージャーのユーティリティーについて触れています(完全なインタビューへのリンクは参考文献にあります)。当然のことですが、Groovyという、この興奮すべき言語にとってクロージャーがいかに強力な財産であるかを知るには、自分でGroovyを色々使ってみることが一番です。

このシリーズについて

どのようなツールであれ、開発作業の中に採り入れるためには、どういう場合に使うべきか、または使うべきではないかをよく知る必要があります。スクリプト言語は非常に強力なツールですが、その強力さは、適切なシナリオで適切な使い方をした場合にのみ発揮されます。実用的なGroovyシリーズはそうした点を念頭に置き、Groovyの実用的な使い方に焦点を絞って、どういう場合に、どのように使うのかを解説して行きます。


クロージャーの実際

リスト1は、このシリーズ第1回で使ったコードの一部を、クロージャーに重点を置いて手直ししたものです。第1回(参考文献)を読んだ人であれば、Groovyでのユニット・テストを示すために使った、パッケージをフィルターするJavaベースのオブジェクトを覚えているでしょう。今回も同じ例を使いますが、今回はクロージャーを使って大幅に機能を拡張します。下記に再掲したものが、前回使ったJavaベースのFilterインターフェースです。

リスト1. Javaでの単純なFilterインターフェース(前回で使ったのを覚えていますか? )
public interface Filter {
  void setFilter(String fltr);  boolean applyFilter(String value);
}

前回はFilterタイプを定義した後、RegexPackageFilterSimplePackageFilterという2つの実装を定義するところに進みました。これらの実装はそれぞれ、正規表現と単純なString操作を適用するものです。

クロージャー無しで書かれたコードとしては、これで充分でした。リスト2を見ると、構文的な変更を少し加えるだけで、コードがどのように(良い方向に)変わってくるかが分かるでしょう。まず、下にあるような汎用のFilterタイプを定義するところから始めますが、今回はGroovyで行います。strategy属性が、今度はFilterクラスと関連付けられていることに注意してください。この属性は、applyFilterメソッドが実行された時に呼ばれるクロージャーのインスタンスです。

リスト2. (クロージャーを持つ)Groovyフィルター
class Filter{strategyboolean applyFilter(str){return strategy.call(str)}}

クロージャーを追加するということは、元々のFilterインターフェースで行ったようにインターフェース・タイプを定義したり、必要な振る舞いにするために特定な実装に依存したりする必要がないことを意味します。そうする代わりに今度は汎用のFilterタイプを定義し、applyFilterメソッドの実行中にクロージャーが適用されるようにします。

次のステップでは2つのクロージャーを定義します。最初のクロージャーは、与えられたパラメーターに対してString操作を適用することによって、(前回の記事からの)SimplePackageFilterをエミュレートします。新しいFilterタイプが作られると、それに対応する、simplefilterという名前のクロージャーがコンストラクターに渡されます。2番目のクロージャー(コード検証用に幾つかのassertが発行された後に登場します)は、与えられたStringに正規表現を適用します。ここで再び新しいFilterタイプを作り、rfilterという名前の正規表現クロージャーを渡し、幾つかのassertを実行して、どこにも問題がないようにします。この一部始終をリスト3に示します。

リスト3. Groovyクロージャーによる単純な魔術
simplefilter = { str | if(str.indexOf("java.") >= 0){
     return true
   }else{
     return false
   }
}
		
fltr = new Filter(strategy:simplefilter)
assert !fltr.apply("test")
assert fltr.apply("java.lang.String")
		
rfilter = { istr |
   if(istr =~ "com.vanward.*"){
     return true
   }else{
     return false
   }
}
		
rfltr = new Filter(strategy:rfilter)
assert !rfltr.apply("java.lang.String")
assert rfltr.apply("com.vanward.sedona.package")

非常に感動的だと思いませんか? クロージャーを使うことによって、必要とする振る舞いの定義を実行時まで遅らせることができたのです。ですから前回の設計とは異なり、新しいFilterタイプを定義してコンパイルする必要はありませんでした。匿名内部クラスを使えばJavaコードでも似たことができますが、Groovyクロージャーを使った方が、とにかく容易でスッキリするのです。

クロージャーは実際強力です。またクロージャーによって振る舞いの扱いも変わってきます(Groovyは、そしてその遠縁の親戚であるRubyも、クロージャーに大きく依存しています)。ですから、ビルドとビルダーを取り上げる次のセクションでは、クロージャーが主な話題になります。


ビルダーでビルドする

Groovy内でのAntのすばらしさの中心となるのは、ビルダーの概念です。要はビルダーを使うことによって、XML文書のようにネストしたツリー状のデータ構造を、Groovyで容易に表現できるということです。そして、紳士淑女の皆さん、これこそが鍵なのです! ビルダー、特にAntBuilderを使うことによって、Ant XMLビルド・ファイルを難なく構成でき、しかもXML自体を相手にすることなく、でき上がった振る舞いを簡単に実行できるのです。GroovyでAntを使う利点はそれだけではありません。XMLとは異なり、Groovyは非常に表現力豊かな開発環境であり、ループ構造や条件分岐を容易にコード化でき、そのうえ新しいbuild.xmlファイルを作る時につきものだったカット・アンド・ペーストの儀式の代わりに、再利用という強力なことさえできるのです。しかもそれがすべてJavaプラットフォームの中で可能なのです!

ビルダー、特にGroovyのAntBuilderの素晴らしいところは、その構文が、ビルダーが表現するXML文書の論理進行を密接に反映した論理進行に従っているということです。AntBuilderのインスタンスに付加されたメソッドは、対応するAntタスクに合致します。同様に、こうしたメソッドは、タスクの属性に対応する(mapの形式での)パラメーターを取ります。そうすると今度はincludefilesetなど、ネストしたタグは、クロージャーとして定義されます。

構成ブロック: 例1

echoと呼ばれるAntタスクという、非常に簡単な例を使って、ビルダーを紹介しましょう。リスト4では、Antのechoタグを、何ら変哲もないXML版として作っています(ここでは何ら驚くようなことはありません)。

リスト4. AntのEchoタスク
<echo message="This was set via the message attribute"/>
<echo>Hello World!</echo>

リスト5になると話が面白くなります。同じAntタグを取り、AntBuilderタイプを使ってGroovyで再定義しています。echoの属性であるmessageを使うか、あるいは単純に望みのStringを渡せばよいことに注意してください。

リスト5. Groovyでの、AntのEchoタスク
ant = new AntBuilder()
ant.echo(message:"mapping it via attribute!")		 
ant.echo("Hello World!")

ビルダーで特に感動的なのは、Groovyの持つ通常機能と自分のビルダー構文を組み合わせることによって、非常に表現力に富む振る舞いセットを生成できる、という点です。リスト6を見ると、無限の可能性があることが分かってくるでしょう。

リスト6. GroovyとAntによるフロー制御
ant = new AntBuilder()
ant.mkdir(dir:"/dev/projects/ighr/binaries/")
try{
    ant.javac(srcdir:"/dev/projects/ighr/src", destdir:"/dev/projects/ighr/binaries/" )
}catch(Throwable thr){
    ant.mail(mailhost:"mail.anywhere.com", subject:"build failure"){
       from(address:"buildmaster@anywhere.com", name:"buildmaster")
       to(address:"dev-team@anywhere.com", name:"Development Team")
       message("Unable to compile ighr's source.")
    }
}

この例では、ソースコードをコンパイルする時のエラー条件を捉えようとしています。catchブロックで定義されているmailオブジェクトが、fromto、それにmessage属性を定義しているクロージャーをどのように扱うかに注意してください。

なんとまあ、これほどたくさんのことがGroovyにはあるのです! しかし、こうした賢い機能をいつ適用すべきかを知るというのは難しい課題であり、私達は皆、苦労するわけです。幸い、多くの場合には「習うより慣れろ」であり、Groovyを(あるいはどんなスクリプト言語でも)使い始めれば、実際にそれを使う場面が数多くあることに気がつくでしょう。そうした中で、どういう場合にGroovyが最適なのかが分かるようになるのです。次のセクションでは、ここで紹介した機能を使うような、実際的かつ典型的な例を見て行きます。


Groovyの応用

例として、自分のコードに対して定常的にチェックサム・レポートを作る必要があるとしましょう。一旦これを実装すれば、必要な場合にはこのレポートを使ってファイルの整合性を検証することができます。チェックサム・レポートを高度に技術的な使い方をする例としては、次のようなものがあります。

  1. 全ソースコードをコンパイルする
  2. バイナリーのクラス・ファイルに対してmd5アルゴリズムを実行する
  3. 各クラス・ファイルと、それに対応するチェックサムをリストアップした、単純なレポートを生成する

この場合では、AntやMavenを完全に捨て去って全ビルド・プロセスにGroovyを使うのは行き過ぎでしょう。実際、先に説明した通り、Groovyはこうしたツールを大いに機能強化するものですが、置き換えではありません。従って、後の方の2項目のみをGroovyの持つ表現の豊かさを使って攻め、最初のステップはAntまたはMavenに任せる方が賢明です。

ただしここでは、最初のステップに対してもMavenを使ったと仮定しましょう。実は正直なところ、これが私の好きなビルド・プラットフォームなのです。java:compiletest:compileゴールを使えば、Mavenでソース・ファイルをコンパイルするのは簡単です。ですからこれらには触れずにおき、新しいゴール参照を、前提条件としての先行ゴールとします。それで終わりです。コンパイルしたソース・ファイルを使って、チェックサム・ユーティリティーの実行に進む準備ができました。しかしその前に、少しばかり簡単なセットアップをしておく必要があります。


Md5ReportBuilderを設定する

Antからこの素敵なチェックサム・ツールを実行するためには2つの情報、つまりチェックサムの対象となるファイルはどのディレクトリーにあるのか、そしてレポートを書き込むのはどのディレクトリーなのか、という情報が必要です。最初の属性としては、カンマで区切られたディレクトリー・リストを想定します。後の方の属性に対しては、希望するレポート・ディレクトリーがあると想定します。

このユーティリティー・クラスをMd5ReportBuilderと呼ぶことにします。そのmainメソッドはリスト7で定義されています。

リスト7. Md5ReportBuilderのmainメソッド
static void main(args) {	 	
		
  assert args[0] && args[1] != null
		
  dirs = args[0].split(",")
  todir = args[1]
		
  report = new Md5ReportBuilder()
  report.runCheckSum(dirs)  report.buildReport(todir)		 	
}

上記で最初のステップでは、2つの属性がこのユーティリティーに渡されているかどうかをチェックしています。次に、最初の属性をカンマで分割することによって、Antのchecksumタスク実行の対象となるディレクトリー配列を作ります。最後にMd5ReportBuilderクラスの新しいインスタンスを作り、必要な機能を処理するために2つのメソッドを呼びます。


チェックサムを追加する

Antにはchecksumタスクがあり、対象とするファイルの集合を含む必要なfilesetを渡すことによって、非常に簡単に呼び出すことのできます。この場合の対象ファイルは、コンパイルしたソース・ファイルとそれに対応したユニット・テスト・ファイルを含むディレクトリーです。こうしたファイルには、forループで繰り返すことによってたどり着くことができます。この場合ではディレクトリーの集合に対して繰り返します。どのディレクトリーに対してもchecksumタスクが呼ばれ、しかもchecksumタスクは .classファイルに対してのみ実行されます。これをリスト8に示します。

リスト8. runCheckSumメソッド
/**
 * runs checksum task for each dir in collection passed in
 */
runCheckSum(dirs){
  ant = new AntBuilder()	   for(idir in dirs){	   ant.checksum(fileext:".md5.txt" ){
      fileset(dir:idir) {
        include(name:"**/*.class")        }
   }
 }
}

レポートを作成する

この時点から、レポートの作成は単なるループの練習になります。新たに生成されたチェックサム・ファイルはそれぞれ読み取られ、それに対応した情報はPrintWriterに送られます。PrintWriterはXMLをファイルに書き出します(これ以上ないほど醜悪な形式ですが)。これをリスト9に示します。

リスト9. レポートを作成する
buildReport(bsedir){
  ant = new AntBuilder()
  scanner = ant.fileScanner {
    fileset(dir:bsedir) {
      include(name:"**/*class.md5.txt")
    }
  }
  rdir = bsedir + File.separator + "xml" + File.separator
  file = new File(rdir) 	    if(!file.exists()){	 	    ant.mkdir(dir:rdir) }
  nfile = new File(rdir + File.separator + "checksum.xml")
  nfile.withPrintWriter{ pwriter |
     pwriter.println("<md5report>")
     for(f in scanner){
       f.eachLine{ line |
         pwriter.println("<md5 class='" + f.path + "' value='" + line + "'/>")
       }
     }
     pwriter.println("</md5report>")
  }		
}

では、このレポートでは何が行われているのでしょう? まず、FileScannerを使って、リスト8のchecksumメソッドで作られた全チェックサム・ファイルを見つけ出します。次に新しいディレクトリーを作り、その中に新しいファイルを作ります。(ディレクトリーが存在するかどうかのチェックが、単純なifステートメントでできるのは素晴らしいと思いませんか?) 次に対応するファイルを開き、そして、素晴らしいクロージャーを使って、それぞれに対応するFilescanner集合から読み取ります。そして、レポートの内容がXML要素として書き込まれて終了します。

ゴールはターゲットです

Mavenになじみのない読者にとっては、goalといっても何の話なのか分からないかも知れません。MavenでのgoalはAntでのtargetだと考えてください。goalは単に、振る舞いをグループ化する方法の一つなのです。Mavenでのgoalは、mavenコマンドで呼ぶことのできるものとして与えられた名前です。与えられたgoalにあるタスクはどれも、呼ばれると実行されます。

withPrintWriterメソッドがFileのこうしたインスタンスに対して持つ強力な効果について、皆さんもすぐに気がついたでしょう。全てが私のために処理されているので、例外や、ファイルを閉じる心配をする必要がありません。私は単に必要な振る舞いを、クロージャーを経由して渡しただけです。それで全てがすんでしまうのです。


ユーティリティーを実行する

このGroovy展示ツアーでは次に、Groovyをビルド・プロセスにつなぎ込みます。具体的には私のmaven.xmlファイルにつなぎ込みます。幸いこれは、比較的簡単な練習の中でも一番簡単な部分です。これをリスト10に示します。

リスト10. MavenでMd5ReportBuilderを実行する
<goal name="gmd5:run" prereqs="java:compile,test:compile">
  <path id="groovy.classpath">						
    <ant:pathelement path="${plugin.getDependencyClasspath()}"/>
    <ant:pathelement location="${plugin.dir}"/>
    <ant:pathelement location="${plugin.resources}"/>            </path>
  <java classname="groovy.lang.GroovyShell" fork="yes">
    <classpath refid="groovy.classpath"/>
    <arg value="${plugin.dir}/src/groovy/com/vanward/md5builder/Md5ReportBuilder.groovy"/>
    <arg value="${maven.test.dest},${maven.build.dest}"/>
    <arg value="${maven.build.dir}/md5-report"/>
  </java>
</goal>

先に説明した通り、チェックサム・ユーティリティーは完全なコンパイルの後に実行する必要がある、と規定しています。ですから私のゴールには、java:compiletest:compileという2つの前提条件があります。クラスパスは常に重要なので、Groovyが実行できる適切なクラスパスを作るように、特別な注意を払っています。最後に、リスト10に示すゴールはGroovyのシェルを呼び出し、実行すべきスクリプトと、対応する2つの引き数、つまり最初にチェックサムの対象となる(カンマで区切られた)ディレクトリー、次にレポートを書き込むべきディレクトリーを渡します。


終わりも完璧です!

maven.xmlファイルをコーディングしてしまえば、ほとんど完了です。しかしここで、最後のステップがあります。必要な依存関係を反映してproject.xmlファイルを更新する必要があるのです。MavenではGroovyに必要とされる依存関係が動作する必要がある、というのは驚くにはあたりません。こうした依存関係は、asmやバイトコード操作ライブラリー、(コマンドライン構文解析を処理する)commons-cli Ant、それに関連したant-launcherなどです。この例に関する依存関係をリスト11に示します。

リスト11. Groovyに必要な依存関係
<dependencies>
  <dependency>
    <groupId>groovy</groupId>
    <id>groovy</id>
    <version>1.0-beta-6</version>
  </dependency>
  <dependency>
    <groupId>asm</groupId>
    <id>asm</id>
    <version>1.4.1</version>
  </dependency>
  <dependency>
    <id>commons-cli</id>
    <version>1.0</version>
  </dependency>
  <dependency>
    <groupId>ant</groupId>
    <artifactId>ant</artifactId>
    <version>1.6.1</version>
  </dependency>
  <dependency>
    <groupId>ant</groupId>
    <artifactId>ant-launcher</artifactId>
    <version>1.6.1</version>
  </dependency>
</dependencies>

レッスンの復習

実用的なGroovyシリーズ第2回目の今回は、Groovyの持つ豊かな表現力とアジャイル性を、AntとMavenの持つ無敵のユーティリティーと組み合わせた時に何が起こるかを見てきました。Groovyはいずれのツールに対しても、XMLに代わるビルド・フォーマットとして素晴らしいものを提供します。Groovyを使うことによって、ループ構成体や条件付き論理を使ってプログラムの流れを制御できるようになり、ビルド・プロセスの機能が大きく向上するのです。

このシリーズではGroovyの実用的な面を紹介しながら、Groovyの使い方として何が最もふさわしいかを探ろうとしています。どんな技術であれ、技術を使う上での鍵は、どういう状況に対してその技術を適用しようとしているのかを注意深く考慮することです。今回の記事では、既に強力なユーティリティーであるAntを置き換えるのではなく、Antを機能強化するためにGroovyをどのように使うのかを解説しました。

次回は、クロージャーに依存する、Groovyのもう一つの機能を紹介します。GroovySqlは非常に手軽なユーティリティーであり、データベースへのクエリーや更新、挿入、そしてそれらに関連した論理を驚くほど容易に操作できます。ではまた次回にお会いしましょう!

参考文献

  • Feeling Groovy」(developerWorks, 2004年8月)はalt.lang.jreシリーズの第1回として、Javaランタイム環境用の代替言語を幾つか紹介しています。
  • Andy Gloverによる実用的なGroovyの全記事のリストを読んでください。
  • Groovy open source project pageからGroovyをダウンロードしてください。このサイトでは、コンパイルやユニット・テスト、正規表現などについても学ぶことができます。
  • Malcolm Davisによる「AntとJUnitを用いた漸進的開発」(developerWorks, 2000年11月)で、Antについてさらに深く学んでください。
  • Bill VennersがRubyの作成者であるYukihiro Matsumotoに対して行ったインタビュー、「Blocks and Closures in Ruby」(Artima.com, 2003年12月刊)を読んでください。
  • Erik Hatcherが「Automating the build and test process」(developerWorks, 2001年8月)の中で、XP涅槃の境地に一歩近づくためにはAntとJUnitをどのように組み合わせるべきかを解説しています。
  • MavenはAntに代わるものとして、特にプロジェクト管理で効果を発揮します。Mavenについてさらに詳しく知るために、Charles Chanによる「プロジェクト管理: Mavenでもっと簡単に」(developerWorks, 2003年4月)を読んでください。
  • Groovy でのAntスクリプトに関して、Filippo Diotaleviが書いている「Build scripts with Groovy and Ant」(JavaWorld.com, 2004年10月)から学んでください。
  • Groovyの最も強力な機能の一つがアジャイル性です。アジャイル開発(またはXP)の下にある原理についてさらに詳しく知るために、Roy Millerによるエクストリーム・プログラミングの神秘を解くシリーズを読んでください(developerWorks, 2002年8月)。
  • Richard HightowerとNicholas LesieckiによるJava tools for extreme programmingは、Javaプラットフォームでのアジャイル開発に関する技術者用ガイドであり、「Building Java applications with Ant」に関する章もあります(2002年7月のdeveloperWorksに抜粋があります)。
  • Just GroovyはGroovyに特化したWebサイトです。
  • developerWorksのJava technologyゾーンには、Javaプログラミングのあらゆる面に関する記事が豊富に用意されています。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=218889
ArticleTitle=実用的なGroovy: GroovyによるAntスクリプト
publish-date=12142004