2/14/08

ClickOnce - how to update publish.htm with MSBuild

When publishing ClickOnce application using Visual Studio, the publish.htm page is created automatically. If you use MSBuild for publishing, then soon you will realize that MSBuild does not offer same functionality, so you either have to build your own page or use the prebuilt publish.htm, check it into the source control and edit it every time you have a build.

To automate the process a little bit, I used the prebuilt page that is created by VisualStudio and checked it in my source control. Then used my MSBuild .proj file to update the version and copy it to my publish location. Since HTML is XML, you can use task.

If you are about to use publish.htm created by Visual Studio, there were a couple of things that you should know. That publish.htm file probably does not comply with XML standards. So test it. A simple check would be to create a .NET app with these two lines of code:


XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(@"C:\Source\WinDTS_NET_3_5\BuildTools\Publish.htm");


If it loads, you are good to go. My page did not load. I had a problem with Visual Studio spacers ("&spns", about 8 of them). Originally I just removed them, but then to save the look of the page I replaced them with:

<SPACER TYPE="block" WIDTH="20" HEIGHT="1" />

When the .htm page is XLM ready, it's time to go back to MSBuild! You can use task provided by MSBuild.Community.Tasks found at http://msbuildtasks.tigris.org/ (the site shows you how to get started and reference the library).

Here's the snipet that updates my publish.htm file:


  <Target Name="UpdatePublishPage"
<XmlUpdate XmlFileName="$(YourPath)\Publish.htm"
XPath="HTML/BODY/TABLE/TR/TD/TABLE/TR/TD/TABLE/TR/APPINFO/TD"
Value="$(Major).$(Minor).$(Build).$(Revision)">
</XmlUpdate>
</Target>
Note that the XPath has "APPINFO" tag. In publish.htm I found the tags that specify the build version of my application and incapsulated them in "APPINFO". That way my XPath can find and manipulate just that part of html:

<APPINFO>
<TD>2.0.0.22103</TD>
</APPINFO>

Finally, when the publish.htm is updated, use MSBuild task to copy it to the your publish folder:

<Copy SourceFiles="$(MyPath)\Publish.htm" DestinationFolder="$(PublishFolder)"/>

That's it!


...to a comment 1: the Target is all there, the only thing that I ommitted was Dependency on another target. I took it out for simplicity sake:

<Target Name="UpdateVersionFile" DependsOnTargets="GetBuildVersion">
<AssemblyInfo
CodeLanguage="CS"
OutputFile="AssemblyVersion.cs"
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" />
</Target>

after you declare a target then you can call it.
For example, at some point some other target such as "DoMYBuild" maybe will call your UpdateVersionFile:

<Target Name="DoMYBuild">
<CallTarget Targets="Clean;GetNewFiles;Build;UpdateVersionFile">
</Target>

2 comments:

  1. Can you provide the <Target> ... </Target> from your .csproj? MSbuild complains that there's no target named UpdatePublishPage in the project.

    ReplyDelete
  2. the Target sample is all there, the only thing that I committed was Dependency on another target. I took it out for simplicity sake..
    here's the ommited part
    Target Name="UpdatePublishPage" DependsOnTargets="GetBuildVersion"

    You should be able to create whatever targets in your file and never call them. However you have to create them to call them. Are you calling the target before (above) the actual target declaration? that might be your problem.

    ReplyDelete