Often you will need to add different pieces of your setup during build time depending on many factors such as the SKU being built. This is done by using conditional statements that will filter the xml before it is sent to the WiX compiler (candle). If the statement evaluates to true, the block of xml will be sent to candle. If the statement evaluates to false, candle will never see that section of xml.
The conditional statements are Boolean expressions based on environment variables, variables defined in the xml, literal values, and more.
Let’s start with an example. Say you want to include a file if you’re building the “Enterprise SKU.” Your build uses an environment variable %MySku%=Enterprise to specify this sku.
When you build the enterprise sku, this file will be included in the xml passed on to candle. When you build a different sku, the xml from EnterpriseFeature.wxs will be ignored.
<?if $(env.MySku) = Enterprise ?> <?include EnterpriseFeature.wxs ?> <?endif ?>
As shown in the example above, files can be included by using the include tag. The filename referenced in the tag will be processed as if it were part of this file.
The root element of the include file must be <Include>. There are no other requirements beyond the expected wix schema. For example,
<Include> <Feature Id='MyFeature' Title='My 1st Feature' Level='1'> <ComponentRef Id='MyComponent' /> </Feature> </Include>
Any variable can be tested for its value or simply its existence. Custom variables can also be defined in your xml.
Three types of variables are supported:The preprocessor evaluates variables throughout the entire document, including in <?if?> expressions and attribute values.
Any environment variable can be referenced with the syntax $(env.VarName). For example, if you want to retrieve the environment variable %_BuildArch%, you would use $(env._BuildArch). Environment variable names are case-insensitive.
WiX has some built-in variables. They are referenced with the syntax $(sys.VARNAME) and are always in upper case.
If you want to define custom variables, you need to use the <?define?> statement. Later, the variables are referred to in the <?if?> statements with the syntax $(var.VarName). Variable names are case-sensitive.
How to define the existence of a variable:
<?define MyVariable ?>
How to define the value of a variable (Quotes are only required if it contains spaces):
<?define MyVariable = “Hello World” ?>
The right side of the definition can also refer to another variable:
<?define MyVariable = $(var.BuildPath)\x86\bin\ ?>
How to undefine a variable:
<?undef MyVariable ?>
The purpose of the conditional statement is to allow you to include or exclude a segment of xml at build time. If the expression evaluates to true, it will be included. If it evaluates to false, it will be ignored.
The conditional statements always begin with either the <?if ?>, <?ifdef ?>, or <?ifndef ?> tags. They are followed by an xml block, an optional <?else?> or <?elseif ?> tag, and must end with an <?endif?> tag.
For example: <?if [expression]?>
The expression found inside the <?if ?> and <?elseif ?> tags is a Boolean expression. It adheres to a simple grammar that follows these rules:
For example: <?ifdef [variable] ?>
For <ifdef ?>, if the variable has been defined, this statement will be true. <ifndef ?> works in the exact opposite way.
<?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?>
An few examples:
<?foreach LCID in "1033;1041;1055"?> <Fragment Id='Fragment.$(var.LCID)'> <DirectoryRef Id='TARGETDIR'> <Component Id='MyComponent.$(var.LCID)' /> </DirectoryRef> </Fragment> <?endforeach?>or
<?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?>or
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?>
WiX has support for preprocessor extensions via the PreprocessorExtension class. The PreprocessorExtension can provide callbacks with context at foreach initialization, variable evaluation, and the last call before invoking the compiler (for full custom preprocessing). See the preprocessor.cs source file and the preprocessorextension.cs source file for more information.