プリプロセッサー

イントロダクション

しばしば、ビルドされている SKU といった多くの要因に従って ビルド時にセットアップの異なる部品を追加する必要があります。 これは、それが WiX コンパイラー (candle) に送られる前に xml をフィルターする条件文を使う事によって なされます。 もし文が true になるならば、xml のブロックは candle に送られます。 もし文が false になるならば、candle は決してそのセクションの xml を見ません。

条件文は、環境変数、xml 中で定義された変数、リテラル値、その他いろいろに基づいたブール演算子表現です。

例で始めましょう。 “Enterprise SKU” をビルドしていて ファイルをインクルードしたいと考えて下さい。 あなたのビルドは、この sku を指定するのに環境変数 %MySku%=Enterprise を使用します。

Enterprise sku をビルドする時、このファイルは candle に渡される xml 中にインクルードされます。 違う sku をビルドする時、EnterpriseFeature.wxs からの xml は無視されます。

<?if $(env.MySku) = Enterprise ?>
  <?include EnterpriseFeature.wxs ?>
<?endif ?>

ファイルをインクルード <?include?>

上の例に示されるように、ファイルは include タグを使う事によってインクルードされ得ます。 タグ中で参照されるファイル名は、まるでそれがこのファイルの一部であるかのように処理されます。

include file のルートエレメントは、<Include> でなくてはなりません。 期待される wix スキーマの他には必要条件はありません。例えば、

<Include>
   <Feature Id='MyFeature' Title='My 1st Feature' Level='1'>
      <ComponentRef Id='MyComponent' />
   </Feature>
</Include>

変数

あらゆる変数が、その値や単にその存在についてテストされ得ます。 カスタム変数も xml 中で定義できます。

次の3つのタイプの変数がサポートされています:
$(env._NtPostBld)
環境変数 %_NtPostBld% を得る
$(sys.CURRENTDIR)
カレントディレクトリについてのシステム変数を得る
$(var.A)
この xml 中で定義された変数 A を得る

<?if?> 式と属性値の中を含めて、プリプロセッサーは文書全体にわたって変数を評価します。

環境変数

あらゆる環境変数が、$(env.VarName) シンタックスで参照され得ます。 例えば、もし環境変数 %_BuildArch% を取得したいならば $(env._BuildArch) を使います。 環境変数は 大文字小文字を区別しません。

システム変数

WiX は、いくつかのビルトイン変数を持っています。 それらは $(sys.VARNAME) シンタックスで参照され、常に大文字です。

注: すべてのビルトイン ディレクトリ変数が “\” で終わっています。

カスタム変数 <? define ?>

もしカスタム変数を定義したいなら、<?define?> 文を使う事ができます。 コマンドラインで candle.exe を使って -d スイッチを使っても 変数を定義できます。 後で変数は <?if?> 文中で $(var.VarName) シンタックスで参照されます。 変数名は大文字小文字を 区別します。

変数の存在の定義の仕方:
<?define MyVariable ?>

変数の値の定義の仕方(注:もし値や値の中の他の変数の展開がスペースを含んでいるなら 引用符が必要です):
<?define MyVariable = “Hello World” ?>
<?define MyVariable = “$(var.otherVariableContainingSpaces)” ?>

定義の右辺が 他の変数を参照する事もできます:
<?define MyVariable = $(var.BuildPath)\x86\bin\ ?>

変数の無効化( undefine )の仕方:
<?undef MyVariable ?>

コマンドラインで変数を定義するには、以下の様なコマンドを打てます:

candle.exe -dMyVariable="Hello World" ...

コマンドラインだけで定義された変数をソース中で参照できますが、 もしその変数をコマンドラインで定義しないと candle.exe はソースコードをプリプロセスしている時に エラーになります。

条件文

いくつかの条件文があり、以下を含みます:

条件文の目的は、ビルド時に部分的な xml をインクルードや除外する事を 可能にする事です。 もし式が True になるなら、インクルードされます。 もしそれが False になるなら、無視されます。

条件文は常に、<?if ?>, <?ifdef ?>, <?ifndef ?> タグのどれかで始まります。 つぎに xml ブロック、オプションで <?else?> か <?elseif ?> タグが続き、<?endif?> タグで終わらなければなりません。

( <?if ?> と <?elseif ?> 中で使用される)式

例えば: <?if [式]?>

<?if ?> と <?elseif ?> タグの内側に見られる式は、ブール演算子式です。 それはこれらのルールに従う簡単な文法に従います:

( <ifdef ?> と <ifndef ?> 中で使用される)変数

例えば: <?ifdef [変数] ?>

<ifdef ?> について、もし変数が定義されてしまっていれば この式は True になります。 <ifndef ?> はちょうど逆の方に働きます。

さらなる例

if と endif タグの間にいかなるタグもないので、これらの例は実際にはどれも何もしない事に注意して下さい。
   <?define myValue  = "3"?>
   <?define system32=$(env.windir)\system32  ?>
   <?define B = "good var" ?>
   <?define C =3 ?>
   <?define IExist ?>
 
   <?if $(var.Iexist)       ?><?endif?> <!-- true -->
   <?if $(var.myValue) = 6  ?><?endif?> <!-- false -->
   <?if $(var.myValue)!=3   ?><?endif?> <!-- false -->
   <?if not "x"= "y"?>              <?endif?> <!-- true -->
   <?if $(env.systemdrive)=a?><?endif?> <!-- false -->
   <?if 3 < $(var.myValue)?>   <?endif?> <!-- false -->
   <?if $(var.B) = "good VAR"?> <?endif?> <!-- false -->
   <?if $(var.A) and not $(env.MyEnvVariable)      ?> <?endif?> <!-- false -->
   <?if $(var.A) Or ($(var.B) And $(var.myValue) >=3)?><?endif?> <!-- true -->
   <?ifdef IExist ?> <!-- true -->
     <?else?> <!-- false -->
   <?endif?>

エラーと警告

<?error エラーメッセージ ?> と <?warning 警告メッセージ ?> を使って 有意義なエラーと警告のメッセージを表示させる為に プリプロセッサを使う事ができます。 これらのプリプロセッサ インストラクションの内の一つに出くわした時、プリプロセッサは エラーを表示してコンパイルを止めるか 警告を表示して続行するかのどちらかです。

例:

<?ifndef RequiredVariable ?>
        <?error RequiredVariable が定義されなければなりません ?>
<?endif?>

反復文

ただ一つの繰り返し文 <?foreach 変数名 in セミコロンで区切られたリスト ?> <?endforeach?> があります。 これが現れた時、プリプロセッサは以下を行います: この処理の効果は、フラグメントがプリプロセッサによって一連のフラグメントを生成する為にテンプレートとして使用される事です。 ?foreach 文中の変数名は、"var." が前に付く事のできるものです。 フラグメントのテキストの内側で変数が使用される時、"var." が前に付かなければなりません。

例を少し:

<?foreach LCID in "1033;1041;1055"?>
        <Fragment Id='Fragment.$(var.LCID)'>
                <DirectoryRef Id='TARGETDIR'>
                        <Component Id='MyComponent.$(var.LCID)' />
                </DirectoryRef>
        </Fragment>
<?endforeach?>
または
<?define LcidList=1033;1041;1055?>
<?foreach LCID in $(var.LcidList)?>
        <Fragment Id='Fragment.$(var.LCID)'>
                <DirectoryRef Id='TARGETDIR'>
                        <Component Id='MyComponent.$(var.LCID)' />
                </DirectoryRef>
        </Fragment>
<?endforeach?>
または
filename: ExtentOfLocalization.wxi
<Include>
        <?define LcidList=1033;1041;1055?>
</Include>

and

<?include ExtentOfLocalization.wxi ?>
<?foreach LCID in $(var.LcidList)?>
        <Fragment Id='Fragment.$(var.LCID)'>
                <DirectoryRef Id='TARGETDIR'>
                        <Component Id='MyComponent.$(var.LCID)' />
                </DirectoryRef>
        </Fragment>
<?endforeach?>

foreach 処理の代案は、テンプレート WiX フラグメントを別個のファイルに書き、WiX に渡されるオーサリングを生成する別の処理を行う事です。 この代案の最大の長所は、デバッグするのがより容易である事です。

エスケープ

プリプロセッサは、もし $ か ( が続くと $ 文字を特別に扱います。 もしリテラル $$ を使いたいなら 代わりに $$$$ を使って下さい。 二つの $ 文字ごとに一つに置き換えられます。 例えば $$$$$ は $$$ に置き換えられます。

エクステンション

WiX は PreprocessorExtension クラスを経由して プリプロセッサーエクステンションのサポートを持っています。 PreprocessorExtension は、foreach 初期化でのコンテクストを持つコールバック、変数評価、関数定義、(フルカスタム プリプロセスの為に)コンパイラが実行される前の最後のコールを 提供できます。