Using TFS (TeamBuild) to build Setup projects in Visual Studio

If you took the shortcut of a Visual Studio Setup project (as opposed to using Wix) then you must have faced the same problem that I did, viz., not being able to create the MSI as part of your TFS nightly builds which hands over the bits to the test team. The problem here is that the vdproj files of the setup projects is in a format that MSBuild (and TeamBuild) does not understand. But we know that Visual Studio can build this project. So I tried to see if I can use devenv.exe to build the project. I tried devenv.exe /? on the command prompt and found that it indeed takes a build switch. So I tried this devenv.exe MySetup.vdproj /build

That did not succeed. The following error message was spit out:

Microsoft (R) Visual Studio Version 9.0.30729.1.
Copyright (C) Microsoft Corp. All rights reserved.
—— Starting pre-build validation for project ‘MySetup’ ——
ERROR: Cannot find outputs of project output group ‘(unable to determine name)’.  Either the group, its configuration, or its project may have been removed from the solution.
ERROR: Cannot find outputs of project output group ‘(unable to determine name)’.  Either the group, its configuration, or its project may have been removed from the solution.
—— Pre-build validation for project ‘MySetup’ completed ——
—— Build started: Project: MySetup, Configuration: Debug ——
========== Build: 0 succeeded or up-to-date, 1 failed, 0 skipped ==========

I then decided to run it against the solution file devenv.exe MySolution.sln /Build

That did the trick. The setup files were created in MySolution/MySetup/Debug/ folder.

So I needed to integrate this into the build script. Using devenv.exe means this build machine needs to have Visual Studio installed on it. We already had it, so I was in luck. In some teams they might not allowed the build machine to have Visual Studio. 

I added the path to devenv.exe to the system path. Screen shot below should show you how to do that.

image

As a first attempt I added the following code to the AfterDropBuild target.

<Target Name="AfterDropBuild">   
    <Exec Command="devenv.exe $(SolutionRoot)/MySolution.sln /Build"/>
    <ItemGroup>
      <SetupFiles Include="$(SolutionRoot)/MySetup/Release/MySetup.msi" />
      <SetupFiles Include="$(SolutionRoot)/MySetup/Release/Setup.exe" />
    </ItemGroup>
    <Copy SourceFiles="@(SetupFiles)" DestinationFolder="C:\Temp\MSI" />
  </Target>

This attempt failed. The error message was not helpful. So I logged into the build machine and tried the same command from command prompt and nothing happened ! So changed the current directory to the location of the sln file and then ran the command again and it worked. So it seems that devenv needs only the name of the sln file (not the full path to it) and the current working directory needs to be the directory when the sln file resides.

So my second attempt:

<Target Name="AfterDropBuild">   
    <Exec Command="devenv.exe MySolution.sln /Build" WorkingDirectory="$(SolutionRoot)"/>
    <ItemGroup>
      <SetupFiles Include="$(SolutionRoot)/MySetup/Release/MySetup.msi" />
      <SetupFiles Include="$(SolutionRoot)/MySetup/Release/Setup.exe" />
    </ItemGroup>
    <Copy SourceFiles="@(SetupFiles)" DestinationFolder="C:\Temp\MSI" />
  </Target>

That worked ! Well not fully, but the Exec task worked but the Copy task failed. I found the reason for that was the files were getting created in the Debug folder and not the Release folder. In order to force a Release build I had to pass additional info to devenv. So I changed the script as shown below and I could see the bits were getting copied correctly.

<Target Name="AfterDropBuild">
    <Exec Command="devenv.exe MySolution.sln /Build &quot;Release|Any CPU&quot;" WorkingDirectory="$(SolutionRoot)"/>
    <ItemGroup>
      <SetupFiles Include="$(SolutionRoot)/MySetup/Release/MySetup.msi" />
      <SetupFiles Include="$(SolutionRoot)/MySetup/Release/Setup.exe" />
    </ItemGroup>
    <Copy SourceFiles="@(SetupFiles)" DestinationFolder="C:\Temp\MSI" />
  </Target>

Here is my final version for copying it to a remote drop location.

<Target Name="AfterDropBuild">   
    <Exec Command="devenv.exe MySolution.sln /Build &quot;Release|Any CPU&quot;" WorkingDirectory="$(SolutionRoot)"/>
    <ItemGroup>
      <SetupFiles Include="$(SolutionRoot)/MySetup/Release/MySetup.msi" />
      <SetupFiles Include="$(SolutionRoot)/MySetup/Release/Setup.exe" />
    </ItemGroup>
    <Copy SourceFiles="@(SetupFiles)" DestinationFolder="\\Build-Machine\Build_Drop_Folders\MyProjectMSI\$(BuildNumber)" />
    <Copy SourceFiles="@(SetupFiles)" DestinationFolder="\\Build-Machine\Build_Drop_Folders\MyProjectMSI\Latest_MSI" />
  </Target>

Note: Here $(SolutionRoot) is the path you have given while creating the build definition. You can see it by editing the build definition.

image

3 thoughts on “Using TFS (TeamBuild) to build Setup projects in Visual Studio

  1. Hi,

    A great tip. However if I build the entire sln it will end up rebuilding the entire projects again (including vdproj) files. I have over 40 projects and it would take quite a while. Is there another way I can rebuild the solution and it only rebuilds the vdproj files.

    Thanks

  2. The setup project usually requires the output of the other projects (the exe and dlls) to create the MSI. So it probably will have to build the other projects first. But assuming they have already been built, you could try creating a new build configuration that builds only the setup project. I havent tried it out and dont know whether that will work, but it is worth a try.

  3. Pingback: Migrating to TFS 2010 from TFS 2008 « Robb Sadler's Weblog

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>