How should I build using Installshield 2012 and TFS2010 / MSBuild? - tfs

I've got a solution to build an installer with InstallShield. The solution worked local. Now I've checked it in to the version control of our TFS2010 and I want to build the InstallShield-solution after all other solutions have been built.
What is the best way to build this solution?
Integrate this solution into the normal build as one of the many solutions and build it as the last one? How can I set special folders (pathvariables) I defined in the InstallShield-solution (PATH_TO_APP1, PATH_TO_APP2) to look for all the assemblies to put together in the setup?
Or, is it better to extend the build definition by an command-line call of MSBuild.exe with the InstallShield-solution after all the other solution have been built? But how do I set the path variables calling MSBuild.exe? Is every path variable a MSBuild-property and I set it via /p:PATH_TO_APP1="xxx"?

Set up the InstallShieldPathVariableOverrides itemgroup, or if you're not using the standard .isproj support, pass your own itemgroup into the InstallShield task's PathVariables property.
Aidan Ryan shows how to do the former, which looks like this. I believe his comment about needing to override the whole tree is no longer necessary in IS2012.
<ItemGroup>
<InstallShieldPathVariableOverrides
Include="((location))">
<PathVariable>((PATH_VARIABLE_NAME))</PathVariable>
</InstallShieldPathVariableOverrides>
</ItemGroup>

Related

How to generate a file with version control information (TFS) during build

I'm tired of not knowing what builds web application is built from when an issue is presented to me by QA. My proposed solution is to generate some static content as part of the build process that has useful information in it. Somebody could reference that file manually on the server and I think it would be nice to offer the ability the HTTP GET the content as json perhaps as part of the API.
Seems simple right? I've done a lot of looking into doing this with Subversion in the past but I'm not familiar with the options for TFS. I realize that I'm really looking for two things, one to harvest the TFS information such as the repository, branch, changeset number, ... as well as a way to write content to a file all within a standard build process. Are there any existing MSBuild targets or tools I can pull in using something like Nuget to ensure that this is done for all builds whether they are local dev builds or ones done through a CI server which don't require any extra configuration or steps by the dev or the one setting up the CI server environment? What I'm hoping is that this is a common enough pattern that maybe somebody has already done this exact thing and packaged it for reuse or that the pieces are simple enough to piece together in a way that can be repeated as a general pattern for various types of .NET projects.
UPDATE: I realized that one issue with the proposed solution is that whatever static file is generated, it would have to be added as a reference to the project in order to be picked up by the build process as content so that it is copied/published properly along with the rest of the web application. Perhaps a broken reference can be added to the project ahead of time that would be fulfilled after the project is built at least once. This is reminiscent of the old way that Package Restore used to work with NuGet.
Related resources:
MSDN - Code Generation in a Build Process
StackOverFlow - MSBuild to copy dynamically generated files as part of project dependency
StackOverflow - How to programmatically get information about branches in TFS?
StackOverflow - It is possible to get TFS change set number from the local file system?
StackOverflow - tf.exe info /version:T does not get latest
The version control information is already generated in environment variables, you just need to write some simple command to read them from the environment variables. Check following links to see if these information meets your requirement:
For vNext build: Predefined variables.
For XAML build: TF_BUILD environment variables.
To include the generated file, you can refer to this question for details: How do you include additional files using VS2010 web deployment packages?
Here is what I've come up with which is working for now:
<Target Name="BeforeBuild">
<PropertyGroup>
<ProgramFiles32>$(MSBuildProgramFiles32)</ProgramFiles32>
<VS14Dir>$(ProgramFiles32)\Microsoft Visual Studio 14.0</VS14Dir>
<TF Condition="Exists('$(VS14Dir)')">$(VS14Dir)\Common7\IDE\TF.exe</TF>
<TfCommand>"$(TF)" vc info . > version.txt</TfCommand>
</PropertyGroup>
<Message Importance="High" Text="Creating version.txt: $(TfCommand)" />
<Exec Command="$(TfCommand)"></Exec>
</Target>
<ItemGroup>
<Content Include="$(SolutionDir)\version.txt">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
This invokes the tf.exe console application with the args vc info . which gets version control information for the current directory and then uses >> version.txt to pipe the output to a file. There are a probably a bunch of improvements that could be made on this but it's a start. Next, I'll have to find some way to include the generated version.txt file as content within the project.
UPDATE: It looks like tf info only gives info about the specified directory and not about the working copy. I'm looking around for alternatives again.

OverwriteReadOnlyFiles property ,error MSB3021

this is the problem im having :
The error message
error MSB3021: Unable to copy file "<filename>" to "<output location>". Access to the path '<output location>' is denied
occurs when the MSBuild Copy task cannot overwrite an existing read-only file.
Typically in Team Build this error will occur because (a) there seems to be an issue in MSBuild where the same file can get included more than once in the list of files to get copied, and (b) these files will typically be read-only in a Team Build since they are retrieved from version control (and not checked out). The workarounds for the issue depend on the version of Team Build (and MSBuild) you are using.
i found this solution:
Team Build 2008 / MSBuild 3.5
In MSBuild 3.5, an OverwriteReadOnlyFiles property was added that can be set to true to allow Copy tasks involved in the build process to overwrite read-only files in cases like the one outlined here. As such, a third workaround is possible in Team Build 2008 / MSBuild 3.5. Note that this workaround will only work for projects that use the 3.5 version of Microsoft.Common.targets - because of the multi-targeting feature available in MSBuild, this will not necessarily be every project built by Team Build 2008.
To set the OverwriteReadOnlyFiles property to true globally, you can either:
Add the text "/p:OverwriteReadOnlyFiles=true" to TfsBuild.rsp for your build definition, or
Add the following property group to TfsBuild.proj for your build definition.
<PropertyGroup>
<CustomPropertiesForBuild>OverwriteReadOnlyFiles=true</CustomPropertiesForBuild>
</PropertyGroup>
Can someone Guide me on how to implement this solution on TFS 2010 on my build ?
i don't know where :
TfsBuild.rsp
or
TfsBuild.proj
are located and how i can add the mentioned suggestions
TFSBuild changed significantly in TFS 2010, it is no longer MSBuild based and is instead Workflow based. There is no longer a TfsBuild.proj file.
If you are trying to overwrite read-only files, you are possibly doing something that is not according to best practices (as MrHinsh mentioned in the comments). If you are sure that what you are doing is appropriate then you either need to turn-off the read-only flag, or use a copy method that is able to ignore the read-only flag.

How can I refer to the workspace path from msbuild in team build?

I've got a boatload of legacy project files that use a $(ProjectRoot) variable in include paths to refer to the root of the source tree. We define the variable in our visual studio environment so desktop builds can find their way to the tfs workspace in use.
I would like to define this variable in the Team Build environment via an MSBuild command line option in my tfs build definition, but I can't seem to find a way that works. I found an answer (List of msbuild properties TFS passes to a build script) with a list of variables and SolutionRoot looked promising.
When I put /p:ProjectRoot=$(SolutionRoot) in the MSBuild Arguments for my build definition, ProjectRoot ends up empty. I want it to expand to something like D:\Builds\18\TfsProject\BuildDef\src\
Is there a way to define my variable with the value of the root of the workspace sources in Team Build?
Note: I tried to figure this out in TFS2010 and finally gave up and modified the BuildProcessTemplate to define the variable using the SourcesDirectory variable that's available inside that context. I thought I'd take another run at it in the hopes of using the new template in TFS2013 without modification, but I'm still stumped.
UPDATE: Turns out the syntax I had in my MSBuild Arguments would work if the variable I'm dereferencing were actually being passed to msbuild. The list from the other post I reference seems to no longer be valid. Looking at the detailed Team Build logs, it looks like the only variables being passed in to msbuild in Team Build are OutDir, BuildId, BuildLabel, BuildTimestamp, BuildSourceVersion, and BuildDefinition. None of those gives me the info my current msbuild scripts need at build time so I'm stuck with either reworking all our project files or continuing with the BuildProcessTemplate modifications I've been using. Unless someone comes along with a better answer here.
You should be able to specify /p:ProjectRoot=$(TF_BUILD_SOURCESDIRECTORY) in the MSBuild Arguments of your build definition when using TFS2013.
See the Team Foundation Build environment variables documentation for more information.
Set the property using the other property. Use condition to only set it if its empty.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="xxxxxxxxxxxx">
<PropertyGroup>
<ProjectRoot Condition="$(ProjectRoot)==''"/>$(SolutionRoot)</ProjectRoot>
</PropertyGroup>

Building select projects only with Team Foundation Build

I’m trying to set up Team Foundation Build and so far it’s running ok with builds, tests and code analysis.
My problem is, that I can’t figure out how to build just a subset of the projects included in the solution. I know how to use the configuration manager to create custom solution configuration that will build/deploy a selection of my projects. But when I create a new build definition, I only get the default “Debug” and “Release” configurations.
alt text http://img686.imageshack.us/img686/7453/builddefinitionprojectf.jpg
How do I set up a Team Foundation Build that only includes a selection of the projects in the solution?
I know I may be on the completely wrong track here, but I've been digging around for a while and so far it's my best guess.
Your custom configurations won't appear in the build definition wizard because we don't parse the solution file format. You can, however, just type it in and that will work just fine without manually editing the TFSBuild.proj file.
You will need to edit the TFSBuild.proj file generated by Build Definition created.
I have never used it to specify other configuration, but try using ConfigurationToBuild:
<ItemGroup>
<ConfigurationToBuild Include="Release|Any CPU">
<FlavorToBuild>Release</FlavorToBuild>
<PlatformToBuild>Any CPU</PlatformToBuild>
</ConfigurationToBuild>
</ItemGroup>
I think, you already answer your question: put the subset of the projects into the new solution and create new build definition for this solution.
What other configuration would you expect then Release and Debug?

What's the best way to get TFS to output each project to its own directory?

I'm putting a large codebase into Team Foundation Server. I would like the build process to create a "ready to deploy" build of our projects.
The normal way we've been doing this is to have each project's output be in its own folder. So, for example, we wind up with something like
C:\project1\
assembly1.dll
assembly2.dll
project1.exe
project1.exe.config
C:\project2\
assembly2.dll
assembly3.dll
project2.exe
project2.exe.config
C:\project3\
assembly1.dll
assembly3.dll
project3.exe
project3.exe.config
Which is the way we like it.
TFS, though, seems to want to stick everything in the same directory.
C:\output\
assembly1.dll
assembly2.dll
assembly3.dll
project1.exe
project1.exe.config
project2.exe
project2.exe.config
project3.exe
project3.exe.config
which, although it saves some amount of disk space (the assemblies are only there one time each) is not how we want it.
What's the best way to specify where TFS/MSBuild should put the output files? Do I need to edit sln/csproj files individually to achieve this or can I do it in the TFSBuild.proj file? (i.e., in a MSBuild-specific file)
I just blogged another method here:
http://mikehadlow.blogspot.com/2009/06/tfs-build-publishedwebsites-for-exe-and.html
but if you can't be bothered to follow the link, here it is in full:
It’s generally good practice to collect all the code under your team’s control in a single uber-solution as described in this Patterns and Practices PDF, Team Development with TFS Guide. If you then configure the TFS build server to build this solution, it’s default behaviour is to place the build output into a single folder, ‘Release’.
Any web application projects in your solution will also be output to a folder called _PublishedWebsites\. This is very nice because it means that you can simply robocopy deploy the web application.
Unfortunately there’s no similar default behaviour for other project types such as WinForms, console or library. It would be very nice if we could have a _PublishedApplications\ sub folder with the output of any selected project(s). Fortunately it’s not that hard to do.
The way _PublishedWebsites works is pretty simple. If you look at the project file of your web application you’ll notice an import near the bottom:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\WebApplications\Microsoft.WebApplication.targets" />
On my machine the MSBuildExtensionsPath property evaluates to C:\Program Files\MSBuild, if we open the Microsoft.WebApplication.targets file we can see that it’s a pretty simple MSBuild file that recognises when the build is not a desktop build, i.e. it’s a TFS build, and copies the output to:
$(OutDir)_PublishedWebsites\$(MSBuildProjectName)
I simply copied the Micrsoft.WebApplication.targets file, put it under source control with a relative path from my project files and changed _PublishedWebsites to _PublishedApplications and renamed the file CI.exe.targets. For each project that I want to output to _PublishedApplications, I simply added this import at the bottom of the project file:
<Import Project="<your relative path>\CI.exe.targets" />
You can edit CI.exe.targets (or whatever you want to call it) to do your bidding. In my case, the only change so far is to add a couple of lines to copy the App.config file:
<Copy SourceFiles="$(OutDir)$(TargetFileName).config" DestinationFolder="$(WebProjectOutputDir)\bin" SkipUnchangedFiles="true" />
There’s a lot of stuff in Microsoft.WebApplication.targets that’s only relevant to web applications and can be stripped out for other project types, but I’ll leave that as an exercise for the reader.
TFS 2012+
I like this solution...
Edit your build definition. Under Process section, set MSBuild arguments to
/p:GenerateProjectSpecificOutputFolder=true
Like this:
By default each project file (*.csproj, *.vbproj, etc.) specifies a default output directory (which is usually bin\Debug, bin\Release, etc.). Team Build actually overrides this so that you're not at the whim of what properties the developer sets in the project file but also so that Team Build can make assumptions about where the outputs are located.
The easiest way to override this behaviour is to set CustomizableOutDir to true in the SolutionToBuild item group as shown here:
<ItemGroup>
<SolutionToBuild Include="$(BuildProjectFolderPath)\path\MySolution.sln" />
<Properties>CustomizableOutDir=true</Properties>
</SolutionToBuild>
</ItemGroup>
This will make the drop folder structure roughly match what you would get locally if you built the solution.
This method is definitely preferable to overriding the Core* targets which can cause upgrade issues.
For each SolutionToBuild node, set the property OutDir to $(OutDir)\SubFolder
For example:
<ItemGroup>
<SolutionToBuild Include="Project1.sln" >
<Properties>OutDir=$(OutDir)\Project1\</Properties>
</SolutionToBuild>
<SolutionToBuild Include="Project2.sln" >
<Properties>OutDir=$(OutDir)\Project2\</Properties>
</SolutionToBuild>
<SolutionToBuild Include="Project3.sln" >
<Properties>OutDir=$(OutDir)\Project3\</Properties>
</SolutionToBuild>
<ItemGroup>
(This works in TF2008, but not TF2005.)
I'm a bit late to the party answering this question but there is a very simple way to implement Mike Hadlows answer. Someone has written a nuget package that does exactly what Mike talks about. You can find it here: http://www.nuget.org/packages/PublishedApplications
Update for TFS 2010 (and upcoming TFS 2012). Jason Stangroome has written a nice blog post outlining how to do this.
https://blog.stangroome.com/2012/02/03/override-the-tfs-team-build-outdir-property/
Override the TFS Team Build OutDir property
Update: with .NET 4.5 there is an easier way.
A very common complaint from users of Team Foundation Server’s build system is that it changes the folder structure of the project outputs. By default Visual Studio puts all the files in each project’s respective /bin/ or /bin// folder but Team Build just uses a flat folder structure putting all the files in the drop folder root or, again, a // subfolder in the drop folder, with all project outputs mixed together.
Additionally because Team Build achieves this by setting the OutDir property via the MSBuild.exe command-line combined with MSBuild’s property precedence this value cannot easily be changed from within MSBuild itself and the popular solution is to edit the Build Process Template *.xaml file to use a different property name. But I prefer not to touch the Workflow unless absolutely necessary.
Instead, I use both the Solution Before Target and the Inline Task features of MSBuild v4 to override the default implementation of the MSBuild Task used to build the individual projects in the solution. In my alternative implementation, I prevent the OutDir property from being passed through and I pass through a property called PreferredOutDir instead which individual projects can use if desired.
The first part, substituting the OutDir property for the PreferredOutDir property at the solution level is achieved simply by adding a new file to the directory your solution file resides in. This new file should be named following the pattern “before..sln.targets”, eg for a solution file called “Foo.sln” then new file would be “before.Foo.sln.targets”. The contents of this new file should look like this. Make sure this new file gets checked-in to source control.
The second part, letting each project control its output folder structure, is simply a matter of adding a line to the project’s *.csproj or *.vbproj file (depending on the language). Locate the first element inside the project file that doesn’t have a Condition attribute specified, and the locate the corresponding closing tag for this element. Immediately above the closing tag add a line something like this:
<OutDir Condition=" '$(PreferredOutDir)' != '' ">$(PreferredOutDir)$(MSBuildProjectName)\</OutDir>
In this example the project will output to the Team Build drop folder under a subfolder named the same as the project file (without the .csproj extension). You might choose a different pattern. Also, Web projects usually create their own output folder under a _PublishedWebSites subfolder of the Team Build drop folder, to maintain this behaviour just set the OutDir property to equal the PreferredOutDir property exactly.
You can verify if your changes have worked on your local machine before checking in simply by running MSBuild from the command-line and specifying the OutDir property just like Team Build does, eg:
msbuild Foo.sln /p:OutDir=c:\TestDropFolder\
You could have one buildscript per project, that would do exactly what you want.
Just create a new TFSBuild file, add the projects you want to have built to the itemgroup(in the order you want them built), set where you want the output to be. This is done by overriding the - property in your TFSBuild file.
But I also agree with the previous poster - why don't you just run with a single build script, and add a zip-task at the end? Maintaining a buildscript per project does add maintenance overhead...
Sling this in a propertygroup:
<CustomizableOutDir>true</CustomizableOutDir>
It'll override the global 'CustomizableOutDir' property which, by default, is set to False. Setting this in the SolutionToBuild's properties will not work.
You achieve this by overriding the default CoreDropBuild target implementation.
In your TFSBuild.proj file (by default stored under TeamBuildTypes/<Build Type>) add the following target:
<!-- Override default implementation -->
<Target
Name="CoreDropBuild"
Condition=" '$(SkipDropBuild)'!='true' and '$(IsDesktopBuild)'!='true' "
DependsOnTargets="$(CoreDropBuildDependsOn)">
...
</Target>
Within this target you can manipulate the output as you want it. The default is to just copy everything from $(BinariesRoot)\$(BuildType) to $(DropLocation)\$(BuildNumber).
I normally use the Microsoft.Sdc.Tasks project for file copying capabilities.
Simple Solution:
Replace all <SolutionToBuild> nodes with <SolutionToPublish>. This will of course only work for publishable projects (e.g. Web projects and applications), not for library projects.
As simple as that :)

Resources