Ant による自動化メモ::build.properties 分割編

[Ant による自動化メモ::build.xml 分割編](http://qiita.com/t-suwa/items/09d75ef579d856cda06b)でもチラッと触れたとおり、ant を使い込んでゆくと自然と設定を外部ファイルに追い出すことになります。最初は小さかった設定ファイルも徐々に肥大化し、やがて分割したくなる時が訪れることでしょう。そんなときに役立つかもしれないメモです。

# 動機

build.properties が肥大化してきたので、ビルド・デプロイ環境ごとに分割して管理したいのです。

# 設定ファイルの読み込み（おさらい）

ant では **property** で変数を定義できます。

```xml
<property name="deploy.host" value="vwebdev01"/>
<property name="deploy.port" value="8884"/>
```

こうしたハードコーディングは最初は問題ないのですが、徐々に数が増えたりして見通しが悪くなってくると、環境ごとに分けて管理したくなるものです。

そこでまずはこれらを別ファイルとして外だしにします。形式は Java のプロパティファイルです。

```properties:build.properties
deploy.host=vwebdev01
deploy.port=8884
```

次にこれを読み込めばいいのですが、それにも先ほどの property を使います。

```xml:build.xml
<property file="build.properties"/>
```

これで \${deploy.host} や \${deploy.port} で定義した変数を参照することができるようになります。

このほかに、より詳細な制御をしたい場合には **loadproperties** というタスクも使えますし、コマンドラインから渡すこともできます。

# property の罠

さて、初心者が最初にはまるポイントの一つが、一度定義した変数を（原則として）再定義できないことではないかと思います。例えば：

```xml
<property name="deploy.host" value="vwebdev01"/>
<echo>${deploy.host}</echo>
<property name="deploy.host" value="vwebdev02"/>
<echo>${deploy.host}</echo>
```

のように試みても、これを実行すると

```
     [echo] vwebdev01
     [echo] vwebdev01
```

といった表示になり、頭を抱えることになります。

ただ、ちょっと手間をかければ変更することは可能です。その一つは **local** タスクを使うことです。local 定義された proeprty はそのスコープ内に限り、定義を上書きできるようになります。

```xml
<property name="deploy.host" value="vwebdev01"/>

<target name="scope-test">
  <local name="deploy.host"/>
  <property name="deploy.host" value="vwebdev02"/>
  <echo>${deploy.host}</echo>
</target>
```

こうすると、scope-test 内限定で deploy.host の定義を変更することができます。ただし、scope-test を抜けてしまえば効果も失われます。あくまでも一時的、ということですね。ちなみにトップレベルスコープで local を使うと、トップレベルに限定して再定義が有効となります。

```xml
<local name="deploy.host"/>
<property name="deploy.host" value="vwebdev01"/>
<echo>${deploy.host}</echo>

<target name="test">
  <echo>${deploy.host}</echo>
</target>
```

こうすると、その出力は以下の通りで、test ターゲットでは未定義となっていることに注意。

```
     [echo] vwebdev01

test:
     [echo] ${deploy.host}
```


もう一つの方法は、**antcall** を使うことです。

```xml
<property name="deploy.host" value="vwebdev01"/>

<target name="scope-test">
  <antcall target="main-target">
    <param name="deploy.host" value="vwebdev02"/>
  </antcall>
</target>

<target name="main-target">
  <echo>${deploy.host}</echo>
</target>
```

antcall は指定したターゲットを起動するタスクです。だた起動するだけなら target タスクの depends に依存関係を指定するのが普通ですが、antcall の偉いところは param で property を渡せることです。

param の構文は property と同じです。事前に定義された property を指定した場合には、必ず新しい値を受け渡すようになっています。param の数に制限はありません。

なお、param で指定しない property についてはオプションで禁止しない限り自動的に引き継がれるようになっています。要するに、差分で property を書き換えることができるわけです。

# antcall の罠

ただ、antcall もそうそう良い面ばかりではありません。まず、antcall のたびに build.xml が再パースされ、新しい実行環境が作られます。そして、antcall で起動されたターゲット内で property や resource の類を定義したとしても、それらが呼び元の実行環境にフィードバックされることはありません。これが depends との大きな違いです。

つまり、以下のように呼ばれた側で property を定義したとしても、

```xml
<target name="scope-test">
  <antcall name="main-target">
    <param name="deploy.host" value="vwebdev02"/>
  </antcall>
  <echo>${main-target-result}</echo>
</target>

<target name="main-target">
  <property name="main-target-result" value="success"/>
</target>
```

呼び元では未定義の変数として解釈され、

```
     [echo] ${main-target-result}
```

となるだけなのです。要するに、現環境のコピーを作ってその中でターゲットを実行した後それを捨てる、というイメージですね。

こういった、デメリットというか仕様を理解したうえで、プロパティを変更したい場合に local を使うのか、antcall を使うのかの判断を下していくことになるでしょう。

# 環境別設定ファイルの読み込み

ちょっと寄り道が長くなりました。ここまでわかったことを総合して環境別設定ファイルの読み込みを実現してみます。

まず、環境別の設定ファイルを、env/環境別ディレクトリ/build.properties に配置すると決めます。環境別ディレクトリは、DEV や IT や ST や UAT になります。

これを指定するのは、ひとまずコマンドライン引数ということにします。

```
ant -Ddeploy.env=DEV
```

といった具合です。出来上がった build.xml は以下の通り（project は割愛、デフォルトターゲットは main）。

```xml:build.xml
<property name="build.props" location="env/${deploy.env}/build.properties"/>

<target name="main" depends="validate-deploy-env">
  <antcall target="-main-body">
    <param file="${build.props}"/>
  </antcall>
</target>

<target name="validate-deploy-env">
  <fail message="deploy.env プロパティが定義されていません">
    <condition>
      <not>
        <isset property="deploy.env"/>
      </not>
    </condition>
  </fail>

  <fail message="${deploy.env} 用の設定ファイル ${build.props} が存在しません">
    <condition>
      <not>
        <available file="${build.props}"/>
      </not>
    </condition>
  </fail>
</target>

<target name="-main-body">
  <!-- 必要な処理など -->
</target>
```

肝は、antcall の param で 設定ファイルを指定しているところです。こうすると、設定ファイルの内容をまとめて渡すことができます。しかも、ここで渡した内容は呼び元を汚染することもないので、とてもクリーンな気持ちになれます。

**ただし！**

param で file を使った場合、事前定義された property は上書きされません。param に name を使う場合と挙動が異なるのです。理由は不明、バグなのか仕様なのかも今のところ不明です（というかそこまで詳しく調べてない）。

なので、どうしても確実に渡したいということであれば、前述の local を使うことになります。

なお、target の名前が "-main-body" のようにハイフンで始まっていると、コマンドラインからは指定できないターゲットとなります。この例のように、確実に環境設定が行われたことを保障した上で起動したいターゲットや、内部でしか起動したくないターゲットに使うことができます。

# まとめ

+ 環境別に設定ファイルを用意すると、いろいろ便利です
+ local や antcall はよく検証・理解した上で使いましょう
+ ant にも結構モヤっとしたところあります

# 参考文献

### Apache Ant User Manual

+ [Tasks Property](http://ant.apache.org/manual/Tasks/property.html)
+ [Tasks LoadProperties](http://ant.apache.org/manual/Tasks/loadproperties.html)
+ [Tasks Local](http://ant.apache.org/manual/Tasks/local.html)
+ [Tasks AntCall](http://ant.apache.org/manual/Tasks/antcall.html)
+ [Tasks Fail](http://ant.apache.org/manual/Tasks/fail.html)