Geeks With Blogs

News
Elton Stoneman (@EltonStoneman) IT Consultant, integration specialist, @Microsoft MVP and @Pluralsight author.

[Source: http://geekswithblogs.net/EltonStoneman]

Snappy title. We have a project which contains lots of WCF Service projects, and we want to generate MSIs so we can deploy them to IIS. The gaps between the projects are minimal as they all use the same structure, so instead of having separate Setup or Wix files in each of the solutions, generating the WXS files on the fly was an option.

The installer steps we wanted were reasonably simple:

  • install WCF artifacts to the chosen directory (.svc and web.config files)
  • install WCF binaries to the chosen directory\bin
  • install dependencies to the GAC
  • create a virtual directory in IIS pointing to the install directory

The only UI we particularly needed was the choice of install directory, so the Wix for this fits into a fairly straightforward T4 template:

<#@ template language="C#" #>

<#@ output extension=".xml" #>

<#@ assembly name="System.dll" #>

<#@ import namespace="System.IO" #>

<#@ import namespace="System.Security.Cryptography" #>

<#@ import namespace="System.Text" #>

<?xml version="1.0" encoding="UTF-8"?>

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension">

<Product Id="<#= this.GetProductId() #>"

Name="<#= ServiceName #>"

Language="1033"

Version="<#= VersionNumber #>"

Manufacturer="Company"

UpgradeCode="<#= this.GetUpgradeCode() #>">

<Package InstallerVersion="200" Compressed="yes" />

 

<Media Id="1" Cabinet="<#= ServiceName #>.Install.cab" EmbedCab="yes" />

 

<Directory Id="TARGETDIR" Name="SourceDir">

<Directory Id="ProgramFilesFolder">

<Directory Id="CompanyFolder" Name="Company">

<Directory Id="INSTALLLOCATION" Name="<#= ServiceName #>">

<Component Id="Website" Guid="<#= this.GetNewGuid() #>">

<# foreach (string filePath in this.WcfArtifacts.Split(';'))

{

                this.AddFile(filePath);    

}#>

            </Component>

<# int index = 0;

foreach (string assemblyPath in this.WcfDependencies.Split(';'))

{#>

                <Component Id="GACComponent_<#= index++ #>" Guid="<#= this.GetNewGuid() #>" SharedDllRefCount="yes">

<# this.AddAssemblyToGAC(assemblyPath); #>

    </Component>

<#}#>

<Directory Id="WebsiteBin" Name="bin">

            <Component Id="WebsiteBin" Guid="<#= this.GetNewGuid() #>">

                <# this.AddAssembly(this.WcfAssembly); #>

            </Component>

</Directory>

</Directory>

</Directory>

</Directory>

 

<Component Id="VirtualDir" Guid="<#= this.GetNewGuid() #>">

        <iis:WebVirtualDir Id="WcfVirtualDir" Alias="<#= ServiceName.TrimEnd(".Wcf".ToCharArray()) #>" Directory="INSTALLLOCATION" WebSite="DefaultWebSite">

            <iis:WebApplication Id="WcfApplication" Name="<#= ServiceName #>" />

        </iis:WebVirtualDir>

     </Component>

</Directory>

 

<iis:WebSite Id="DefaultWebSite" Description="Default Web Site">

        <iis:WebAddress Id="AllUnassigned" Port="80" />

    </iis:WebSite>

 

<Feature Id="ProductFeature" Title="<#= ServiceName #>" Level="1">

<ComponentRef Id="Website" />

<ComponentRef Id="WebsiteBin" />

<# int i = 0;

foreach (string assemblyPath in this.WcfDependencies.Split(';'))

{#>

<ComponentRef Id="GACComponent_<#= i++ #>" />

<# } #>

<ComponentRef Id="VirtualDir" />

</Feature>

 

<Property Id="WIXUI_INSTALLDIR" Value="INSTALLLOCATION"/>

<UIRef Id="WixUI_InstallDir" />

<WixVariable Id="WixUIDialogBmp" Value="$(WixPath)\SetupBackground.bmp" />

 

</Product>

</Wix>

<#+

private string ServiceName

{ get{ return "$(ProjectName).$(ProjectFilter)"; }}

 

private string VersionNumber

{ get{ return "$(Version)"; }}

 

private string WcfArtifacts

{ get{ return @"@(WcfArtifacts)"; }}

 

private string WcfAssembly

{ get{ return @"@(WcfAssembly)"; }}

 

private string WcfDependencies

{ get{ return @"@(WcfDependencies)"; }}

- the full template is here: WCF Service Wix template.

A couple of interesting things here – the Guids are generated from the name of the elements (see Generating Deterministic Guids), so multiple versions will have the same Guid and will operate as updates, provided the name of the service doesn't change; the tags for MSBuild properties in the template get resolved using the ExecuteT4Template MSBuild task; the GACd assemblies flag themselves as SharedDllRefCount so if you uninstall this package, the files will be left in the GAC if any other packages use them, but removed if this is the only package that uses them.

Another custom task is needed to resolve the dependencies of the WCF assembly (sample version here: MSBuild ResolveDependencies task), and the MSBuild snippet to run it together with the Wix commands looks like this:

<Target Name="ResolveWcfDependencies">

<!-- List the dependencies for the Wcf service:-->

<ResolveDependencies AssemblyList="@(WcfAssembly)"

Filter="$(CompanyName).$(ProductName)">

<Output TaskParameter="DependencyList" ItemName="WcfDependencies"/>

</ResolveDependencies>

</Target>

 

<Target Name="PublishWcf" DependsOnTargets=" ResolveWcfDependencies" Condition="'@(WcfAssembly)' != ''">

<!-- Create the wxs file: -->

<Message Text="Determined Wcf dependencies: @(WcfDependencies)"/>

<ExecuteT4Template ToolPath="$(MSBuildProjectDirectory)\$(BuildBinDir)"

TemplatePath="$(TemplateRoot)\Services.Wcf.wxs.tt"

OutputPath="$(ArtefactWixDir)\$(ProductName).$(ProjectName).Wcf.wxs">

<Output TaskParameter="TempFilePath" ItemName="T4TempFilePath"/>

</ExecuteT4Template>

<Message Text="T4 template resolved to: @(T4TempFilePath)"/>

<!-- Produce intermediate Wix obj:-->

<Exec Command='"$(WixPath)\candle" -out "$(ArtefactWixDir)\$(ProductName).$(ProjectName).Wcf.wixobj" "$(ArtefactWixDir)\$(ProductName).$(ProjectName).Wcf.wxs" -ext WixIIsExtension -ext WixUiExtension'/>

<!-- Produce MSI:-->

<Exec Command='"$(WixPath)\light" -out "$(PublishServiceDir)\$(ProductName).$(ProjectName).Wcf.msi" "$(ArtefactWixDir)\$(ProductName).$(ProjectName).Wcf.wixobj" -ext WixIIsExtension -ext WixUiExtension -cultures:en-us'/>

</Target>

- we store all the intermediate files (the T4 template with resolved property values, WXS and WIXOBJ files) to help with debugging.

Posted on Thursday, September 4, 2008 2:55 PM CodeGen , MSBuild , Wix , WCF | Back to top


Comments on this post: T4 template to generate Wix scripts to generate WCF MSIs in MSBuild

# re: T4 template to generate Wix scripts to generate WCF MSIs in MSBuild
Requesting Gravatar...
Nice work, But I dont uderstand what means $(something) and how to use it? Also there is not clear how to put it work with custom action, simple example solution for download where you are doing all of this would be helfull.
Left by Jack on Nov 27, 2009 2:05 PM

# re: T4 template to generate Wix scripts to generate WCF MSIs in MSBuild
Requesting Gravatar...
Hi Jack, the $(variable) notation means the script will pull the current value from MSBuild - e.g. $(Version) will be replaced in the ouput with the current build version, say 1.14.4.111.

The post should be read with this one which explains how the T4 template works with MSBuild: http://geekswithblogs.net/EltonStoneman/archive/2008/07/25/an-msbuild-task-to-execute-t4-templates.aspx.

The download from that post includes some simple T4 scripts and MSBuild projects.

Elton.
Left by Elton on Nov 28, 2009 12:28 AM

# re: T4 template to generate Wix scripts to generate WCF MSIs in MSBuild
Requesting Gravatar...
These are highly informative and helpful.Thanks for giving us such quality knowledge.Waiting for your next interesting posts.Keep blogging.
Left by electronic cigarettes on Apr 26, 2011 5:03 AM

# re: T4 template to generate Wix scripts to generate WCF MSIs in MSBuild
Requesting Gravatar...
Minimal gaps between projects must be secured with right formula so that generalization will not have bad effects to every specific projects
Left by online teaching degree on May 13, 2011 6:48 AM

# re: T4 template to generate Wix scripts to generate WCF MSIs in MSBuild
Requesting Gravatar...
Great! it'just what i am searching for, just need to adapt this to application :)
Left by Développeur .net on Oct 06, 2012 8:39 AM

Your comment:
 (will show your gravatar)


Copyright © Elton Stoneman | Powered by: GeeksWithBlogs.net | Join free