Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

conditional statements - Wix Installer : Setting component condition property when doing a MSIEXEC admin install at command line

We have three types/flavours of our product, but only one MSI written in WiX. When we build the installer we pass in the flavour via a defined constant:

Call MSBUILD.bat ..MSIsCoreProductOurProduct.sln /p:DefineConstants="FLAVOUR=%_Flavour%"

and the constant is setup in Visual Studio, under Build -> Define preprocessor variables as FLAVOUR=50. The build process, passes in values 50, 200 or LITE as the flavour.

In the WiX code we have loads of conditions on our components that tell it which file to install based on the flavour; eg

      <Component Id="cmp7F45920B1AA100729BAE37FC846B3FC5" Guid="*">
    <File Id="fil238A776D9294E14671E012472F9F7196"
          KeyPath="yes"
          Source="$(var.MenusPath)ClientListView 200.r5m"  
    <Condition>$(var.FLAVOUR)=200</Condition>
  </Component>

      <Component Id="cmp8BFF42B232724DC4BA5B8F87994DEF21" Guid="*">
    <File Id="fil808D6428D67248DDB8CA65DBC5978283" 
          KeyPath="yes" 
          Source="$(var.MenusPath)ClientListView Lite.r5m"
    <Condition>$(var.FLAVOUR)=LITE</Condition>
  </Component>

So the example above will install a file called "ClientListView Lite.r5m" if the FLAVOUR is LITE or it will install a file called "ClientListView 200.r5m" if the FLAVOUR is 200.

This all works as expected and has done for years !!

But now, we have a web version of our product and we need a zip file to contain the folder structure and files that would be installed for each flavour. I discovered that you can run a msi at the command line using MSIEXEC and the /a argument which will then redirect everything that would have been installed into a folder & thought this is exactly what I want ... but alas it's not working as I'd expected.

What it seems to be doing is running the MSI and extracting the files into the target folder, but it is ignoring the flavour and so you end up with both the "ClientListView Lite.r5m" and "ClientListView 200.r5m" files been extracted into the folder; which is obviously not what I want.

Upon reading the docs on MSIEXEC, it seems that you can pass in the value for a Public property eg msiexec.exe /a "C:Example.msi" MY_PROP="myValue" - so I thought this might help me; so in my WiX code I added the line:

    <Property Id='PRODTYPE' Value="$(var.FLAVOUR)"/>

and then altered my component conditions to be like:

  <Component Id="cmp7F45920B1AA100729BAE37FC846B3FC5" Guid="*">
    <File Id="fil238A776D9294E14671E012472F9F7196"
          KeyPath="yes"
          Source="$(var.MenusPath)ClientListView 200.r5m"  
    <Condition><![CDATA[PRODTYPE=200]]></Condition>
  </Component>

  <Component Id="cmp8BFF42B232724DC4BA5B8F87994DEF21" Guid="*">
    <File Id="fil808D6428D67248DDB8CA65DBC5978283" 
          KeyPath="yes" 
          Source="$(var.MenusPath)ClientListView Lite.r5m"
    <Condition><![CDATA[PRODTYPE=LITE]]></Condition>
  </Component>

but although that compiled ok, running it via:

msiexec /a OurProduct.msi /qb PRODTYPE=200 TARGETDIR="C:InstalledFiles200"

still extracts both files for the 200 & LITE flavours, where I just wanted the one for 200.

So, am I trying to do something that is not possible ... or am I doing something wrong - any help would be gratefully appreciated, because the alternative of mimicking the process in a batch file to create my zip; will be horrendous !!

Cheers,

Chris.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Your Pre-processor Use

I was half-way answering with a suggestion to use the pre-processor constructs (?if? et al) when I realized you only wanted a single MSI (at least at a time - it seems), so I skipped it. I normally use such constructs to compile flavors of MSI files from the same WiX source. I have dumped what I wrote below with some rehashing - without too much review. I will check in on it later.

I might be missing something in your case, but I do not see how an ?if? statement can work at installation time - it is a compile time construct. It pre-processes your WiX source file before compilation and linking. As such it sounds like you actually have compiled three different versions of your MSI file and then run admin install on each of them? If this is the case then the use of the admin image is irrelevant, because your whole MSI does not contain anything but the components that you have included with ?if? - there is no need to pass in any edition property specification.


Setup Flavors

The normal technical approaches I use when I need to create different setup flavors or product editions are as follows:

  1. Preprocessor constructs: used to compile different flavors or editions (also different language editions) of the MSI at compile time. One such construct is the ?if? statement you are mentioning. There are several other ones, as seen in the link provided directly above. The relevant ones for you would be the conditional statements (if, ifdef, ifndef, else, elseif, endif) and possibly the include statement.

    • So the overall message is that preprocessor constructs allow you to build several different flavors of MSI files from a single WiX XML source.

    • In your case this would yield a single WiX XML source and three MSI files - each for a different version of the application. All similar, but with only the components they need.

    • Running installation via /i or administrative image (file extract) via /a would yield only the components you added to that setup flavor, but you do not have a single MSI as you say - but three different ones depending on how you compiled.

    • I prefer to use ?include? statements to include WiX fragment files rather than to just condition components directly with ?if? as you mention. There is a sample of the difference at the bottom in the "Preprocessor Constructs" section.

  2. MSI Features (as PhilDW answered): you can also rely on MSI features to deliver a single setup in different flavors. Features are used to segregate the MSI into various user-selectable installation items (some of which you can make mandatory).

    • Features allow a single MSI to be installed in different flavors - whilst containing all components needed by each flavor. Conditioning components can achieve similar effects.
    • Conditioning components resembles feature manipulation, but works on a more primitive and fundamental level. These are not user-selectable installation units, but fundamental and atomic bits and pieces of your product to install and hidden from the user's view - whilst features are user-selectable and generally visible for the user.
    • What features are to be installed can be adjusted by the user or manipulated programmatically via a custom action (to override user specifications and enforce a technical design auto-magically - for example).

      • User feature control

        • Users can control features to install interactively via the setup's FeatureTree dialog found in most setups (not all). Usually this involves selecting to do a "Custom" installation.

        • You can also specify what features to install in the setup via the command line kicking off the installation using the ADDLOCAL property and the REMOVE property (and potentially other, similar properties - see link for details).

        • Sometimes you can set a custom property during installation that would trigger a constellation of standard features to installed based on the "edition" specified, which brings us to the next point.

      • Programmatic feature control

        • Adding a link to an answer on programmatic feature control: How to install feature based on the property set in custom action?

        • As stated, you can manipulate what features to install via the command line by using the ADDLOCAL property and the REMOVE property inside the setup while it is running. To do this you can use a custom action.

        • There is also an INSTALLLEVEL property which is relevant for the installation state of features. For each feature there is an install level, and it can be affected by conditions set in the Condition table.

          • So the Condition table can be used to modify the selection state of any entry in the Feature table based on a conditional expression.

          • In other words you can use this to set features to either install or not install by default based on the condition.

        • In addition to the INSTALLLEVEL concept, you can also use custom actions to control the feature selection state.

          • This is strangely more reliable today than the feature condition, since many application packagers routinely max out the INSTALLLEVEL property to force all features to be installed. This is wrong since some features may need to not be installed if they are incompatible with the OS you are running on. I have tried to communicate this to many deaf ears.
        • Using these programmatic constructs your setup can, for example, change the feature selection state for what is to be installed based on a serial key entered by the user.

        • Your setup can also determine that a certain feature should not be installed based on the OS you are running (do not install Tablet OS features, for example).

        • There could be any number of technical and practical reasons for changing feature selection programmatically.

    • It is important to note that the overall result is that the feature selection can be set via the command line or by the user, and then your setup makes some "under the hood" modifications for technical reasons.

    • And just to point it out: the features that are manipulated for installation programmatically are typically hidden from view (but still possible to override via the command line - which can be a problem).

    • More on features, components and customization of setups for installation here: How to make better use of MSI files.

  3. Setup.exe launcher: a "modern" way to do complex deployment in different flavors can be to use WiX's Burn feature to compile smaller MSI setups that are installed in different "sets" to yield a different installation state.

    • I find this to be too complicated for general use, but it is certainly possible. I think some find it easier, since there is less feature manipulation. Maybe I just lack experience with it.
    • The benefit is smaller MSI files that install quicker, and you can update a single MSI file and create a new setup.exe wrapper and then do total QA on your whole solution without rebuilding all setups.
    • In my world a single, updated MSI requires a full QA anyway, so I am not always buying these "simplicity arguments" myself. Every release cycle has risks, and hence adds to total risk. It can be great, though, to be able to rebuild a tiny setup and keep your large one stable.

Practical Use

I prefer to make as few setup flavors as possible, but most of the time I end up using preprocessor constructs to create different setups for different language versions (English, German, Russian) and also for different product editions (Enterprise, Professional, Standard). I usually set them all related sharing an upgrade code and not capable of installing side-by-side.

I feel that a single MSI gets more QA resources and will hence be tested better. However, this single MSI approach typically breaks down if the setups are very large - in which case I like to split them. I also like to make separate setups for each language for business- and real-world practical reasons explained here. Practical and legal requirements (licensing) can also make it imperative to compile special versions of the same MSI setup. I have also been asked to build new MSI flavors for OEM vendors.

And now a surprising conclusion (that you never asked for :-) ): with all that said, in an ideal world I like to install all features without any technical fuss in the setup, and use the serial key to determine what application functionality and modules should be activated in the application. I also like to put the serial key validation inside the application, and not in the setup either. The reason? I want setups as primitive as possible to ensure the application is properly installed without a high deployment error percentage. Therefore I hunt for complexity to kill in deployment to ensure less deployment support issues. Reliable deployment is crucial to the success of the product. Once you are installed, your application can launch in a much more debuggable and predictable context (no conditioning, impersonation or sequencing issues) and report any errors interactively and meaningfully to the user - and they can call support with more hope of successfully resolving any issues than what would be the case if the setup just bombed out with a mysterious non-interactive error message (in the system's event log).

Once you got a good, simple deployment solution, your job as release manager and setup developer shou


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...