Inside the Microsoft Build Engine

Sayed Ibrahim Hashimi, William Bartholomew

Mentioned 69

Presents a guide to the software build and deployment process using MSBuild.

More on Amazon.com

Mentioned in questions and answers.

I am aware there are other NAnt and MSBuild related questions on Stack Overflow, but I could not find a direct comparison between the two and so here is the question.

When should one choose NAnt over MSBuild? Which one is better for what? Is NAnt more suitable for home/open source projects and MSBuild for work projects? What is the experience with any of the two?

NAnt has more features out of the box, but MSBuild has a much better fundamental structure (item metadata rocks) which makes it much easier to build reusable MSBuild scripts.

MSBuild takes a while to understand, but once you do it's very nice.

Learning materials:

I'm working on a web application project, and I need to create a build script; a build script that I can trigger from my cruisecontrol server. Since nant has not been maintained for ages, I figure that MSBuild is the way to go.

I need the build script to be able to

  • Compile all assemblies
  • Execute unit tests
  • Run NCover analysis on the unit tests
  • Deploy the database (depending on parameters). This is really executing a tool since I will end up writing my own tool to deploy the database. But the execution of the tool should be conditional based on some command line parameter.
  • Deliver unit tests and coverage results in a format that cruisecontrol can read and understand.

I assume that MSBuild will allow me to do all these things. But I don't know where to start. Does anybody know of a good tutorial to get me started with my build script?

I wasted a lot of time before buying the Hashimi MSBuild book, fantastic -- I concur with this review by Steven St Jean.

In addition to an excellent all-round introduction to MSBuild scripting, it has a cookbook section on doing typical build automation thigns like build numbers etc. Obviously the last TeamBuild section will not be directly relevant for you, but a lot of the topics are generic across all CI tools.

UPDATE: The 2nd ed keeps up the trend, adding a must-have section re WebDeploy

We have a solution with around 100+ projects, most of them C#. Naturally, it takes a long time to both open and build, so I am looking for best practices for such beasts. Along the lines of questions I am hoping to get answers to, are:

  • how do you best handle references between projects
    • should "copy local" be on or off?
  • should every project build to its own folder, or should they all build to the same output folder(they are all part of the same application)

  • Are solutions' folders a good way of organizing stuff?

I know that splitting the solution up into multiple smaller solutions is an option, but that comes with its own set of refactoring and building headaches, so perhaps we can save that for a separate thread :-)

You might be interested in these two MSBuild articles that I have written.

MSBuild: Best Practices For Creating Reliable Builds, Part 1

MSBuild: Best Practices For Creating Reliable Builds, Part 2

Specificially in Part 2 there is a section Building large source trees that you might want to take a look at.

To briefly answer your questions here though:

  • CopyLocal? For sure turn this off
  • Build to one or many output folders? Build to one output folder
  • Solution folders? This is a matter of taste.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I want to build projects from the command line. Is it possible to deploy a C# compiler without installing Visual Studio?

Like some others have mentioned MSBuild is deployed with the .NET Framework (versions 2.0 and 3.5) so if you have either of those installed you can build your applications without needing Visual Studio installed on the machine.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

So I have an ASP.NET MVC app that references a number of javascript files in various places (in the site master and additional references in several views as well).

I'd like to know if there is an automated way for compressing and minimizing such references into a single .js file where possible. Such that this ...

<script src="<%= ResolveUrl("~") %>Content/ExtJS/Ext.ux.grid.GridSummary/Ext.ux.grid.GridSummary.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext.ux.rating/ext.ux.ratingplugin.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext-starslider/ext-starslider.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext.ux.dollarfield.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext.ux.combobox.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/ext.ux.datepickerplus/ext.ux.datepickerplus-min.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/SessionProvider.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ExtJS/TabCloseMenu.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ActivityViewer/ActivityForm.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ActivityViewer/UserForm.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ActivityViewer/SwappedGrid.js" type="text/javascript"></script>
<script src="<%= ResolveUrl("~") %>Content/ActivityViewer/Tree.js" type="text/javascript"></script>

... could be reduced to something like this ...

<script src="<%= ResolveUrl("~") %>Content/MyViewPage-min.js" type="text/javascript"></script>

Thanks

Actually there is a much easier way using Web Deployment Projects (WDP). The WDP will manage the complexities of the aspnet__compiler and aspnet__merge tool. You can customize the process by a UI inside of Visual Studio.

As for the compressing the js files you can leave all of your js files in place and just compress these files during the build process. So in the WDP you would declare something like this:

<Project>
   REMOVE CONTENT HERE FOR WEB

<Import 
  Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<!-- Extend the build process -->
<PropertyGroup>
  <BuildDependsOn>
    $(BuildDependsOn);
    CompressJavascript
  </BuildDependsOn>
</PropertyGroup>

<Target Name="CompressJavascript">
  <ItemGroup>
    <_JSFilesToCompress Include="$(OutputPath)Scripts\**\*.js" />
  </ItemGroup>
  <Message Text="Compresing Javascript files" Importance="high" />
  <JSCompress Files="@(_JSFilesToCompress)" />
</Target>
</Project>

This uses the JSCompress MSBuild task from the MSBuild Community Tasks which I think is based off of JSMin.

The idea is, leave all of your js files as they are (i.e. debuggable/human-readable). When you build your WDP it will first copy the js files to the OutputPath and then the CompressJavascript target is called to minimize the js files. This doesn't modify your original source files, just the ones in the output folder of the WDP project. Then you deploy the files in the WDPs output path, which includes the pre-compilied site. I covered this exact scenario in my book (link below my name).

You can also let the WDP handle creating the Virtual Directory as well, just check a checkbox and fill in the name of the virtual directory.

For some links on MSBuild:

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I currently use nant, ccnet (cruise control), svn, mbunit. I use msbuild to do my sln build just because it was simpler to shell out.

Are there any merits to switching my whole build script to MSBuild? I need to be able to run tests, watir style tests, xcopy deploy. Is this easier?

Update: Any compelling features that would cause me to shift from nant to msbuild?

Nant has more features out of the box, but MSBuild has a much better fundamental structure (item metadata rocks) which makes it much easier to build reusable MSBuild scripts.

MSBuild takes a while to understand, but once you do it's very nice.

Learning materials:

I'm developing a custom MSBuild task that builds an ORM layer, and using it in a project. I'm being hampered by Visual Studio's behaviour of holding onto MSBuild task DLLs and not letting go.

I'd like to organize my solution like this;

My Solution
 |
 +- (1) ORM Layer Custom Task Project
 |  |
 |  +- BuildOrmLayerTask.cs     // here's my task
 |  
 +- (2) Business Logic Project  // and here's the project that uses it.
    |
    +- <UsingTask TaskName="BuildOrmLayerTask" AssemblyFile="$(TaskAssembly)" />

However, when project (2) builds, it locks onto the assembly from project (1). So now I can't build project (1) again without closing the solution and re-opening it.

Is there any way I can organize things so that the custom build task is not kept locked by Visual Studio?

(Edit: Sayed Ibrahim Hashimi, who literally wrote the book on msbuild, suggests the AppDomainIsolatedTask class for a better approach)

I've managed to solve this one myself...

Found this forum post from Dan Moseley, one of the MSBuild developers from Microsoft:

Hi there,

Unfortunately this is because MSBuild loads task assemblies in the primary appdomain. The CLR does not allow assemblies to unload from an appdomain as this allows important optimizations on their part.

The only workarounds I suggest is to call out tomsbuild.exe to build the projects that use the task. To do this, create MSBuild.exe <> as an external tool in VS.

Dan
developer on msbuild
DanMoseley - MSFT

So, it seems that to stop the locks, you must spawn out a new MSBuild.exe process. It can't be the one that runs inside Visual Studio, because when MSBuild runs, it loads the tasks into Visual Studio's primary app domain, and that can never be unloaded.

  • create a new MSBuild project (a .csproj or similar) which overrides the 'Build' Target and performs your custom actions, eg;

    <!-- fragment of Prebuild.csproj -->   
    <Target Name="Build">   
         <BuildOrmLayerTask Repository="$(Repository)" />   
    </Target>
    
  • Add it to visual studio if you want, but use Configuration Manager to make sure it is notbuilt in any configuration. Just let VS take care of source control and suchlike, not building.

  • Edit the .csproj file of the project that depends on Prebuild.csproj. Add a BeforeBuild target which invokes MSBuild using the Exec task. This will start a new process, and when that process ends, the file locks are released. Example;

    <PropertyGroup>   
         <PrebuildProject>$(SolutionDir)Prebuild\Prebuild.csproj</PrebuildProject>   
    </PropertyGroup>   
    <Target Name="BeforeBuild">   
         <Exec Command="msbuild.exe &quot;$(PrebuildProject)&quot;" />   
    </Target>
    

Now, when you build the dependent project, it executes MSBuild in a new process before running the compile.

Unless I've grossly misunderstood MSBuild, tasks are executed in the document order in which they appear within a 'Target' node.

I'd like to be able to specify that two tasks (such as xcopy tasks) could run in parallel. I was expecting there to be a 'Parallel' task or something...?

As was stated, you cannot parallelise at the task level or even at the target level. MSBuild only will build projects (i.e. MSBuild project files) in parallel. So you have to use the MSBuild task with multiple projects specified and the BuildInParallel attribute should be set to true. Also make sure that when the build is invoked on the command line that the /m switch is sent it.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I simply wondered whether people thought it was worth learning to use the MSBuild syntax in order to customise the build process for a .net project, or whether it is really not worth it given the ease with which one can build a project using visual studio.

I am thinking in terms of nightly builds, etc., but then couldn't I use a scheduled event which uses the command-line build option built into VS? Are there superior tools out there?

MSBuild is definitely worth learning for anyone and everyone writing .NET software. The reason a build server for .NET apps no longer requires Visual Studio to be installed (as Andrew Burns mentioned) is because MSBuild is part of the .NET Framework now.

Knowing MSBuild will give you significant flexibility in choosing what technologies you use to implement continuous integration. Because I took the time to learn MSBuild, I was able to change the CI system one of our teams was using from CruiseControl.NET to TeamCity without much difficulty. Those CI servers, or something like FinalBuilder (which I'm not familiar with), are better options for performing nightly builds than a scheduled task. Learning how to implement custom MSBuild tasks will give you even more flexibility in implementing custom builds. Jivko Petiov listed a number of tasks that MSBuild makes easier. In the case of database deployment and configuration, I've written scripts that do this in MSBuild, and it makes the development and testing process much easier.

If Visual Studio Team System is in your future, applications built using MSBuild will be much easier to move into that environment than those built via alternative means.

There are a lot of resources available to help you get started with MSBuild. I'd start with Inside the Microsoft Build Engine. One of the co-authors also has a ton of stuff on the web, including this site, and a project on CodePlex.

This is very weird. We've been trying to figure it out for a while, but it really doesn't make any sense.

Our web project imports a targets file that has a target similar to this:

<Target Name="CSSCheckInternal">
    <ItemGroup>
        <CSSFiles Include="$(MSBuildProjectDirectory)\**\*.css" />
    </ItemGroup>
    <CSSChecker Files="@(CSSFiles)" />
</Target>

At the moment, one branch is building perfectly, executing the task as desired; but the other branch is failing on the above target.

The failure is because the @(CSSFiles) item, when received by the task, appears not to be expanding into an ITaskItem array.

The task is written as follows (up to the point where I get the FullPath metadata):

public class CSSChecker : Task
{
    [Required]
    public ITaskItem[] Files
    {
        get;
        set;
    }

    public override bool Execute()
    {
        string fullFilePath = null;
        if (Files != null)
        {
            foreach (var item in Files)
            {
                fullFilePath = item.GetMetadata("FullPath");
                if(!File.Exists(fullFilePath))
                  throw new InvalidOperationException(
                   string.Format("{0} does not exist", fullFilePath));


        //rest of the code elided

The build that's failing is throwing the InvalidOperationException on the last line there, like this:

File does not exist: C:\Code\Project\**\*.css

So it would seem that MSBuild, instead of expanding the wildcard in the Include attribute, is merely passing the string over, thus creating only one ITaskItem on the task.

The target folder does exist on the disk, and the only difference between the broken project file and the working one is a single file include much earlier in the project file.

Update

I asked Sayed Hashimi on twitter (wrote the MSBuild book) and through that, tried taking out the ** folder wildcard and it's now started working. This isn't really suitable as the task is meant to be re-usable between projects. But it would appear to be something to do with this.

End update

Please if anyone knows under what situation MSBuild would not correctly expand a wildcard, it would be a great help!

Is there a way to compile a .vbproj or .csproj project file directly, just like Visual Studio does?

When you compile in Visual Studio, the "output" window shows the actual call to the compiler, which normally looks like:

vbc.exe [bunch of options] [looooong list of .vb files]

I would like to programatically call "something" that would take the .vbproj file and do whatever Visual Studio does to generate this long command line. I know i could parse the .vbproj myself and generate that command line, but I'd rather save myself all the reverse engineering and trial-and-error...

Is there a tool to do this? I'd rather be able to do it in a machine without having Visual Studio installed. However, if there's a way to call Visual Studio with some parameters to do it, then that'll be fine too.

I looked briefly at MSBuild, and it looks like it works from a .proj project file that i'd have to make especially, and that I'd need to update every time I add a file to the .vbproj file. (I did look briefly at it, so it's very likely I missed something important)

Any help will be greatly appreciated

Just so you know .vbproj and .csproj files are MSBuild. So everything that you've read, you can apply to those files directly.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have set up multiple targets in a single xml file. I expect all targets to run but only the frist target gets executed.

Here is a simplified version of what iam trying to do:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="T1">
    <Copy SourceFiles="c:\temp\a.txt" DestinationFolder="C:\temp2\" />    
  </Target>
  <Target Name="T2">
    <Copy SourceFiles="c:\temp\b.txt" DestinationFolder="C:\temp2\" />    
  </Target>
</Project>

I'am running the build from the TeamCity CI Server and the logs reports Process exit code: 0.

Anyone got any ideas why it does not run T2?

MSBuild uses this order to determine what target(s) should be executed. Once a value is found it stops there and begins execution.

  • The target(s) you specify using the /t switch on msbuild.exe
  • The targets(s) contained in the DefaultTargets attribute on the Project element
  • The first target which is found in the build script (slightly morecomplicated actually)

As the previous commenter stated you can use the DependsOnTargets list to have other targets execute first before that target.

About your solution, AfterTargets is only available in MSBuild 4.0 so that will not work with previous versions.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

Is it possible to obtain a list all all the output files from a MSBuild project?

In a simple project, I could do something like

<CreateItem Include="$(OutputDir)**\*">
      <Output ItemName="AllOutputs" TaskParameter="Include"/>
</CreateItem>

but my projects are part of a larger build, and all outputs go to a common location, I want to be able to exculde dlls and content that don't belong.

Any ideas?

If you are using the MSBuild task then you can get the files that were built by using the TargetOutputs output. Here is an example

<MSBuild Projects="YourProject.csproj">
      <Output ItemName="YourProjectOutputs" TaskParameter="TargetOutputs"/>
</MSBuild>

So in this case when YourProject.csproj is built the files that were created would be placed inside the YourProjectOutputs item.

I've created a blog entry a while ago that talks in more detail about this, MSBuild: How to Get All Generated Outputs.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

After looking at your comment again I realized that I mis-intrepreted what you really needed. This is an interesting problem you have on your hands.

If you don't mind editing the project file itself you may be able to get pretty close to what you want. There is an item FileWrites that keeps track of all the files that were written out during the build process. To start playing around with this edit the project file to have this AfterBuild target

<Target Name="AfterBuild">
  <Message Text="FileWrites: @(FileWrites)" Importance="high"/>
</Target>

There are some problems with this approach as well

  • You have to edit the project file itself
  • This will contain files written to the intermediate output dir (i.e. obj) and the output dir (i.e. bin)
  • If there are build customizations, they are not required to write to this item

You might think that you could solve the first problem with the MSBuild: Find Many Project References technique and output the FileWrites item after doing a build. This will only work if the wrapper proj file was placed in the same folder as the original project itself because all the items inside of a .csproj file are declared with a relative path. So there goes that for the most part.

You can get over the second limitation by using the FindUnderPath task to only get the files placed in the OutputPath folder.

What you could do but is not really reliable either is to examine the OutputPath at the begining of the build and then once again at the end of the build nad see what was added. Let's say that you put the original files into an item StartFiles and at the end of the build put all the files into an item named EndFiles you could the do:

<Target Name="SomeTargetHere">

<ItemGroup>
    <FilesWritten Include="@(EndFiles)" />
    <FilesWritten Remove="@(StartFiles)"/>
</ItemGroup>

<!-- Now FilesWritten contains the difference between EndFiles & StartFiles -->

</Target>

In short I'm not sure if there is a good solution that doesn't involve either a custom task or a custom logger :(.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I've noticed that our cruise control server does not fail the build when content files are missing.

I'd like to fail the build when javascript / graphics files etc are missing. How can I go about validating the project file with the files pulled from svn?

You can create a target to check to make sure that all Content files are physically located on disk and raise an error if this is not the case. Here is such a target

<Target Name="ValidateContentFiles">
  <Error Condition="!Exists(%(Content.FullPath))" 
         Text="Missing Content file [%(Content.FullPath)]"/>
</Target>

You can make sure that this target is executed everytime by adding it to the InitialTargets attribute on the Project element. For example

<Project InitialTargets="ValidateContentFiles"
         ToolsVersion="3.5" DefaultTargets="Build" 
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I'm currently integrating my Wix projects in MSBuild. It is necessary for me to pass multiple values to the Wix project. One value will work (ProductVersion in the sample below).

<Target Name="BuildWixSetups">
    <MSBuild Condition="'%(WixSetups.Identity)'!=''"
                Projects="%(WixSetups.Identity)"
                Targets="Rebuild" Properties="Configuration=Release;OutputPath=$(OutDir);DefineConstants=ProductVersion=%(WixSetups.ISVersion)" ContinueOnError="true"/>
</Target>

However, how do I pass multiple values to the DefineConstants key? I've tried all the 'logical' separators (space, comma, semi-colon, pipe-symbol), but this doesn't work.

Has someone else come across this problem?

Solutions that don't work:

  1. Trying to add a DefineConstants element does not work because DefineConstants needs to be expressed within the Properties attribute.

Why are you specifying DefineContstants=ProductVersion=XXXXXX?

For DefineConstants you are not assigning values, either a constant (like DEBUG or TRACE) is defined or it is not. This property relates to the /define C# compiler switch. What are you really trying to do?

Also I'm not sure what you mean when you say tha my blog post is a "hack" the fact that it was built twice is the whole point.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

As part of one of my projects, there are "BeforeBuild" tasks that ultimately generate some files. In particular, it compiles a small static class (included as "do not compile" in the project) into it's own executable and then executes it, passing in an external input file outputting a new generated class to be included in the project.

I programmed it to put the intermediate files in the projects $(OutDir), but found that on "Rebuild" (and ultimately "Clean"), these files aren't picked up. After some thought, I realized that the final, generated class which is placed right in $(ProjectDir) should probably be deleted on "Clean" too.

Some investigation into Microsoft.Common.targets revealed that there was some "master list" from the intermediate path (obj\build\assembly.FileListAbsolute.txt) that was queried for the files to delete.

Is there some standard method of adding my new files to this list in MSBuild to have them cleaned up, or would this sort of thing fit better in a "BeforeClean" (or "AfterClean") target override?

See Extending the Clean Process for details of the FileWrites mechanism (which is the system writing the FileListAbsolute.txt you're seeing) in this MSBuild article by Hashimi. And get the book right now if you're going to spend more than 2 hours writing build scripts in the next year.

I'd like to create a MSBuild project that reflects the project dependencies in a solution and wraps the VS projects inside reusable targets.

The problem I like solve doing this is to svn-export, build and deploy a specific assembly (and its dependencies) in an BizTalk application.

My question is: How can I make the targets for svn-exporting, building and deploying reusable and also reuse the wrapped projects when they are built for different dependencies?

I know it would be simpler to just build the solution and deploy only the assemblies needed but I'd like to reuse the targets as much as possible.

The parts

The project I like to deploy

<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <ExportRoot Condition="'$(Export)'==''">Export</ExportRoot>
    </PropertyGroup>

    <Target Name="Clean_Export">
        <RemoveDir Directories="$(ExportRoot)\My.Project.Dir" />
    </Target>

    <Target Name="Export_MyProject">
        <Exec Command="svn export svn://xxx/trunk/Biztalk2009/MyProject.btproj --force" WorkingDirectory="$(ExportRoot)" />
    </Target>

    <Target Name="Build_MyProject" DependsOnTargets="Export_MyProject">
        <MSBuild Projects="$(ExportRoot)\My.Project.Dir\MyProject.btproj" Targets="Build" Properties="Configuration=Release"></MSBuild>
    </Target>

    <Target Name="Deploy_MyProject" DependsOnTargets="Build_MyProject">
        <Exec Command="BTSTask AddResource -ApplicationName:CORE -Source:MyProject.dll" />
    </Target>
</Project>

The projects it depends upon look almost exactly like this (other .btproj and .csproj).

Wow, this is a loaded question for a forum post. I wrote about 20 pages on creating reusable .targets files in my book, but I'll get you started here with the basics here. I believe that the key to creating reusable build scripts (i.e. .targets files) is three elements:

  • Place behavior (i.e. targets) into separate files
  • Place data (i.e. properties and items, these are called .proj files) into their own files
  • Extensibility
  • .targets files should validate assumptions

The idea is that you want to place all of your targets into separate files and then these files will be imported by the files which will be driving the build process. These are the files which contain the data. Since you import the .targets files you get all the targets as if they had been defined inline. There will be a silent contract between the .proj and .targets files. This contract is defined in properties and items which both use. This is what needs to be validated.

The idea here is not new. This pattern is followed by .csproj (and other projects generated by Visual Studio). If you take a look your .csproj file you will not find a single target, just properties and items. Then towards the bottom of the file it imports Microsoft.csharp.targets (may differ depending on project type). This project file (along with others that it imports) contains all the targets which actually perform the build.

So it's layed out like this:

  • SharedBuild.targets
  • MyProduct.proj

Where MyProdcut.proj might look like:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This uses a .targets file to off load performing the build -->
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
    <OutputPath Condition=" '$(OutputPath)'=='' ">$(MSBuildProjectDirectory)\BuildArtifacts\bin\</OutputPath>
  </PropertyGroup>

  <ItemGroup>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary1\ClassLibrary1.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary2\ClassLibrary2.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary3\ClassLibrary3.csproj"/>
    <Projects Include="$(MSBuildProjectDirectory)\..\WindowsFormsApplication1\WindowsFormsApplication1.csproj"/>
  </ItemGroup>

  <Import Project="SharedBuild.targets"/>
</Project>

And SharedBuild.targets might look like:

<Project  DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- This represents a re-usable build file -->
  <Target Name="SharedBuild_Validate">
    <!-- See http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx for more info
         about this validation pattern
    -->
    <ItemGroup>
      <_RequiredProperties Include ="Configuration">
          <Value>$(Configuration)</Value>
      </_RequiredProperties>    
      <_RequiredProperties Include ="OutputPath">
          <Value>$(OutputPath)</Value>
      </_RequiredProperties>

      <_RequiredItems Include="Projects">
        <RequiredValue>%(Projects.Identity)</RequiredValue>
        <RequiredFilePath>%(Projects.Identity)</RequiredFilePath>
      </_RequiredItems>
    </ItemGroup>

    <!-- Raise an error if any value in _RequiredProperties is missing -->
    <Error Condition="'%(_RequiredProperties.Value)'==''"
           Text="Missing required property [%(_RequiredProperties.Identity)]"/>

    <!-- Raise an error if any value in _RequiredItems is empty -->
    <Error Condition="'%(_RequiredItems.RequiredValue)'==''"
           Text="Missing required item value [%(_RequiredItems.Identity)]" />

    <!-- Validate any file/directory that should exist -->
    <Error Condition="'%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)')"
           Text="Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)]" />
  </Target>

  <PropertyGroup>
    <BuildDependsOn>
      SharedBuild_Validate;
      BeforeBuild;
      CoreBuild;
      AfterBuild;
    </BuildDependsOn>
  </PropertyGroup>
  <Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
  <Target Name="BeforeBuild"/>
  <Target Name="AfterBuild"/>
  <Target Name="CoreBuild">
    <!-- Make sure output folder exists -->
    <PropertyGroup>
      <_FullOutputPath>$(OutputPath)$(Configuration)\</_FullOutputPath>
    </PropertyGroup>
    <MakeDir Directories="$(_FullOutputPath)"/>
    <MSBuild Projects="@(Projects)"
             BuildInParallel="true"
             Properties="OutputPath=$(_FullOutputPath)"/>
  </Target>
</Project>

Don't look too much at the SharedBuild_Validate target yet. I put that there for completeness but don't focus on it. You can find more info on that at my blog at http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx.

The important parts to notice are the extensibility points. Even though this is a very basic file, it has all the components of a reusable .targets file. You can customize it's behavior by passing in different properties and items to build. You can extend it's behavior by overriding a target (BeforeBuild, AfterBuild or even CoreBuild) and you can inject your own targets into the build with:

<Project ...>
   ...
  <Import Project="SharedBuild.targets"/>
  <PropertyGroup>
    <BuildDependsOn>
      $(BuildDependsOn);
      CustomAfterBuild
    </BuildDependsOn>
  </PropertyGroup>
  <Target Name="CustomAfterBuild">
    <!-- Insert stuff here -->
  </Target>
</Project>

In your case I would create an SvnExport.targets file which uses the required properties:

  • SvnExportRoot
  • SvnUrl
  • SvnWorkingDirectory You will use these properties to do the Export.

Then create another one for Biztalk build and deploy. You could split this up into 2 if necessary.

Then inside of your .proj file you just import both and setup the targets to build in the right order, and your off.

This is only really the beginning of creating reusable build elements, but this should get the wheels turning in your head. I am going to post all of this to my blog as well as download links for all files.

UPDATE:

Posted to blog at http://sedodream.com/2010/03/19/ReplacingSolutionFilesWithMSBuildFiles.aspx

In MSBuild you can override a <Target /> from another file in your own. For example the AfterBuild target included in Microsoft.Common.targets file simply by defining your own Target with the same name:

<Target Name="AfterBuild">
    <!-- Do something different -->
</TargetName>

You'll see a note like this:

Overriding target "AfterBuild" in project "C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.Common.targets" with target "AfterBuild" from project "XXXXX".

Is there any way to call the original AfterBuild target?

I'd like to do this to instrument certain complex default Targets and then execute the original behavior. Many targets like Build expose a BuildDependsOn property that can be used for this. Many others do not - and I'd like to override them without completely duplicating their content.

When an MSBuild script is processed it will also process the imported files. The result will be a single in memory canonical representation of the entire script. When a target is encountered that already exists the previous definition is discarded, therefore it is not possible to call the original target.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I am working on a test application based on ASP.NET MVC. I am new to CSS stuff so I am learning that as well.

I have created a master page called "new.master" and created a new css "new.css". I am calling the view from controller as:

return View ("About", "new");

What I am doing is that I am frequently updating the css and master page. So I run the application in debug mode frequently. After sometime I stop seeing the effect of changes in css. When I "view source" in chrome and check the css, it shows an old version.

When I change the name of both master page and css, I start to receive updated views but that works for sometime and then again I stop getting the CSS updates. Have you faced this problem? How can I fix it? (Its terribly annoying!)

I think this may be a browser cache issue. In in IE and FF I usually do Ctrl + F5 or Ctrl + Refresh button. Also you can manually clear the cache. For IE you can use the IE Dev Toolbar and for Firefox there is Firebug with both you can clear the cache easily. Not sure about chrome, sorry.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I would like to simulate "Publish..." menu item from WCF Service project (or ASP.NET Web Application or...) context menu in Visual Studio 2008. I do not have a need to publish it to a running IIS instance, just to put the web content together with bin folder on some local dir. Is there any way to add it in post build events, or as a MSBuild task?

HI, You should take a look at Web Deployment Projects. These are actually MSBuild files with Visual Studio GUI support. They will pre-compile your site. You can extend the behavior of these to copy the generated files to your web server.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have an interest in msbuild this week. I'm cleaning up a lot of extremely complex build scripts. Digging in surprises me with how much it can do - msbuild is sort of a hidden feature of .NET programming in itself.

In the SO convention that questions must have answers, in a few days or a week, I'll mark the most useful or coolest hidden feature(s) as accepted.

   let bestAnswer suprise slick useful = (surprise + slick + 2*useful)

Definition of useful: I'm updating existing msbuild scripts that: package (zip files) websites and utilities, CC.NET integration, launch tests (UT + selenium), build databases. I'm adding (new targets, even more useful): deploy to VMWare virtual servers, chained builds (fast build immediately, queue slow tests). If you refer to an external library (like MSBuild community tasks), it would be nice to know how to get it.

Some msbuild surprises I've already found.

  • Hello world using the Message task and Properties.
  • Using msbuild as an installer for an extremely complex server product. MSB community tasks managed IIS server setup. The WriteLinesToFile and XmlUpdate tasks wrote server specific configuration files. If you've work with MSI, you'll know that anything is better than MSI for installation.
  • For newbies: CSProj and Vbproj files are the same as msbuild "proj" files. To edit directly: Unload your csproj or vbproj, then right click project and select edit. This is nicer and more powerful than working with clunky pre-build / post-build events.
  • MSBuild comes with the generic .NET installation. Unlike other fancy tools, you can use it on a totally clean server / desktop.

Here is msbuild Hello World After I wrote it, I found the MSDN hello world.

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build;Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
       <Who>World</Who>
  </PropertyGroup>
  <Target Name="Hello">
    <Message Text="Hello, $(Who)" Importance="high" ></Message>
  </Target>
  <Target Name="Build" DependsOnTargets="Hello"/>
  <Target Name="Test"/>
</Project>

This is not really a hidden feature but I think that batching is very powerful when understood.

For more information you can read my related blog entries at:

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have a solution I'm trying to get to build on TFS. I want to update the versions of all appropriate files, and I've been stuck trying to get this done. There are plenty of links on how to do it, but none of them work for me, due to one little issue... Scope.

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="DesktopBuild" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
    <Target Name="DesktopBuild">
        <CallTarget Targets="GetFiles"  />

        <Message Text="CSFiles: '@(CSFiles)'" />
    </Target>

    <Target Name="GetFiles">
        <ItemGroup>
            <CSFiles Include="**\AssemblyInfo.cs" />
        </ItemGroup>
        <Message Text="CSFiles: '@(CSFiles)'" />
    </Target>
</Project>

My tree looks like this:

  • test.proj
  • application.sln
  • application (Folder)
    • main.cs
    • Properties (Folder)
      • AssemblyInfo.cs

When I run "c:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe test.proj" from the solution folder... I get the following output:

Microsoft (R) Build Engine Version 3.5.30729.1
[Microsoft .NET Framework, Version 2.0.50727.3074]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 7/6/2009 3:54:10 PM.
Project "D:\src\test.proj" on node 0 (default targets).
  CSFiles: 'application\Properties\AssemblyInfo.cs'
DesktopBuild:
  CSFiles: ''
Done Building Project "D:\src\test.proj" (default targets).


Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.04

So, how can I make my ItemGroup have global scope? All the Targets files used by the compiler and TeamBuild do this same thing, and theirs all seem to be global... I don't understand why this isn't working for me.

Any help?

The previous commenter was correct, you should change this to use DependsOnTargets instead of using the CallTarget task. What you are seeing is a bug not a scoping inssue. The way to avoid this bug is to use DependsOnTargets (which is a much better approach anywayz).

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have seen the prior questions and answers. In that one question, the original poster asked a followup question:

what are the compelling reasons to use msbuild? are there cons?

I didn't see the answer to that. I'd like to know the converse, too. What are the compelling features of Nant?

I think, for nant, cross-platform is big. For msbuild, it is the currency and integration with Visual Studio. Does this sound right? Anything else?

EDIT/Added: anyone have a feature list comparison? Someone said "nant has more features out of the box." Which ones?

Would it make sense to combine these projects, combine efforts so as to benefit mutually? Has anyone asked MS if they'd be willing to contribute msbuild to the community, like WiX? What are the chances?

EDIT2: I just found this prior discussion, not sure why I couldn't find it before.

Nant has more features out of the box, but MSBuild has a much better fundamental structure (item metadata rocks) which makes it much easier to build reusable MSBuild scripts.

MSBuild takes a while to understand, but once you do it's very nice.

Learning materials:

I got a directory I want to copy to a number of locations.

Say I have

  • home.aspx

I want to copy it to

  • abc/home.aspx
  • def/home.aspx
  • ghi/home.aspx

so two questions for me:

  • How do I define the list abc, def, ghi?
  • How do I execute my Copy task with each element of this list?

The concept that you should be interested in is known as Batching.

I've covered this exact scenario on my blog at http://www.sedodream.com/PermaLink,guid,5f1e0445-ce3d-4052-ba80-42fd19512d42.aspx

Here is the text of that blog entry, you can download the mentioned files at the link above.


Today someone was telling me about a co-worker who was having issues with MSBuild. He told me that he was trying to copy a set of files to a set of different servers. But the issue was that he didn’t know how to achieve this without performing multiple Copy task invocations. I told him that he could achieve this using MSBuild Batching. Batching is a process of performing a task (or target) on a set of items (batches) at a time. A batch can also include a single item. So in this scenario we need to perform the copy one time for each server that he wanted to deploy to. I’ve created a simple msbuild file which demonstrates this in two different ways. The first way uses task batching, which can bee seen in the Test target. And the other uses Target batching which can be seen in the DoItCore target. I've also created a clean target, which has nothing to do with batching.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Test">

      <ItemGroup>
            <SourceFiles Include="*.txt"/>
            <Dest Include="One;Two;Three;Four;Five"/>
      </ItemGroup>

      <Target Name="Test">
            <Copy SourceFiles ="@(SourceFiles)" DestinationFolder="%(Dest.FullPath)"/>
            <Message Text="Fullpath: %(Dest.FullPath)"/>
      </Target>


      <!-- These targets demonstrate target batching -->
      <Target Name="DoIt" DependsOnTargets="DoItCore"/>
      <Target Name="DoItCore" Inputs="@(SourceFiles)" Outputs="%(Dest.FullPath)">
            <Copy SourceFiles="@(SourceFiles)" DestinationFolder="%(Dest.FullPath)"/>
      </Target>


      <!-- This will clean up the files -->
      <Target Name="Clean">
            <CreateItem Include="%(Dest.FullPath)\**\*">
                  <Output ItemName="FilesToDelete" TaskParameter="Include"/>
            </CreateItem>
            <Delete Files="@(FilesToDelete)"/>
      </Target>
</Project>

Batching is an advanced topic of MSBuild, and is defintely neglected. I have to admit I’m guilty of not writing about it enough myself. There are some good batching resources, they are listed below.


Here are some other batching related blog entries that I've posted.

Thanks, Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have a batch file that is using the exit command to return an exit code.

This batch file may, in some cases, be invoked interactively from a commandline, or in other cases, may be run as part of an MSBuild project, using the Exec task.

  • If I use exit %errorlevel% within my batch file this works well and MSBuild sees the error code, however an interactive user who is running the batch file from a command window will get a rude exit of cmd.exe in this case.
  • If I use exit /b %errorlevel% the interactive scenario does not get a rude exit, but this also means that the cmd launched by my Exec task also does not exit, and therefore MSBuild does not see the return value.

As a solution to both problems, I am trying to use exit /b but launch the batch file from my build script like this:

<Exec Command="Batch.cmd params &amp; exit %errorlevel%" />

The idea being that I explicitly take the 'non-terminal' return from exit /b and manually call exit to propogate this value outside of cmd.exe where the Exec Build Task can see it.

This seems like the perfect solution, however it isn't working. Exec still doesn't get the correct error value.

One way to handle this could be to have MSBuild pass a parameter to the batch file so that it knows that MSBuild is calling it instead of from a command prompt. For example I have created the sample file test.bat shown below

ECHO OFF

IF (%1)==() goto Start
SET fromMSBuild=1

:Start

ECHO fromMSBuild:%fromMSBuild%


REM ***** Perform your actions here *****

set theExitCode=101
GOTO End



:End
IF %fromMSBuild%==1 exit %theExitCode%


REM **** Not from MSBuild ****

ECHO Exiting with exit code %theExitCode%
exit /b %theExitCode%

And I've created the MSBuild file wrapper.proj which is:

<Project DefaultTargets="Demo" ToolsVersion="3.5"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <BatchFile>test.bat</BatchFile>
    <FromMSBuild>FromMSBuild</FromMSBuild>
  </PropertyGroup>

  <Target Name="Demo">

    <Message Text="Executing batch file $(BatchFile)" Importance="high"/>

    <PropertyGroup>
      <_Command>$(BatchFile) $(FromMSBuild)</_Command>
    </PropertyGroup>

    <Exec Command="$(_Command)">
      <Output PropertyName="CommandExitCode" TaskParameter="ExitCode"/>
    </Exec>

    <Message Text="CommandExitCode: $(CommandExitCode)"/>

  </Target>
</Project>

If you execute the file test.bat from the command prompt the result is

C:\Data\Development\My Code\Community\MSBuild\BatchFile>test.bat

C:\Data\Development\My Code\Community\MSBuild\BatchFile>ECHO OFF
fromMSBuild:0
Exiting with exit code 101

And from MSBuild the result is:

C:\Data\Development\My Code\Community\MSBuild\BatchFile>msbuild Wrapper.proj /t:Demo /fl /nologo
Build started 5/18/2009 10:54:52 PM.
Project "C:\Data\Development\My Code\Community\MSBuild\BatchFile\Wrapper.proj" on node 0 (Demo target(s)).
  Executing batch file test.bat
  fromMSBuild:1
C:\Data\Development\My Code\Community\MSBuild\BatchFile\Wrapper.proj(17,5): error MSB3073: The command "test.bat FromMSBuild" exi
ted with code 101.
Done Building Project "C:\Data\Development\My Code\Community\MSBuild\BatchFile\Wrapper.proj" (Demo target(s)) -- FAILED.


Build FAILED.

"C:\Data\Development\My Code\Community\MSBuild\BatchFile\Wrapper.proj" (Demo target) (1) ->
(Demo target) ->
  C:\Data\Development\My Code\Community\MSBuild\BatchFile\Wrapper.proj(17,5): error MSB3073: The command "test.bat FromMSBuild" e
xited with code 101.

    0 Warning(s)
    1 Error(s)

Time Elapsed 00:00:00.06

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have a post-build target in MSBuild to copy some build outputs.

This is linked in as a dependency to the AfterBuild target (exposed by Microsoft.CSharp.targets):

<Target Name="AfterBuild" DependsOnTargets="InstallUtil;CopyPostBuildFiles" />

Is there any way to avoid the files being copied if the build didn't actually re-build?

For example, when the MSBuild dependency analysis asserts that the project doesn't need to be built because none of its source files have been updated, it doesn't build, but still executes my copy target. Is there any way to prevent this?

Since you are overriding the AfterBuild target it will always execute after the build occurs. This is the case for a rebuild or a normal build. If you want to perform some actions after the rebuild (and not build) then you should extend the dependency property for the Rebuild target. So in your case to inject the targets after a rebuild occurs your project file should look something like:

<Project ...>
   <!-- some content here -->

   <Import Project="... Microsoft.Csharp.targets" />


    <PropertyGroup>
        <RebuildDependsOn>
            $(RebuildDependsOn);
            InstallUtil;
            CopyPostBuildFiles
        </RebuildDependsOn>
    </PropertyGroup>
</Project>

This method extends the property which the Rebuild targets uses to declare what targets it depends on. I've detailed this in the article Inside MSBuild, see section Extending the build process.

Also what you are trying to accomplish may be acheived by the AfterBuild target if you can specify what what files would "trigger" an update to occur. In other words you can specify a set of "inputs" into the target and a set of "outputs" which are both files. If all outputs were created after all inputs then the target would be considerd up to date and skipped. this concept is known as Incremental Building and I've coverd it in the article MSBuild Best Practices Part 2. Also I have detailed this in my book, Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build.

EDIT: Adding BuildDependsOn example

If you want the target to only execute when the files are actually built and not just when the Rebuild target is executed. Then you should create your project to be like the following:

<Project ...>
   <!-- some content here -->

   <Import Project="... Microsoft.Csharp.targets" />


    <PropertyGroup>
        <BuildDependsOn>
            $(BuildDependsOn);
            CustomAfterBuild;
        </BuildDependsOn>
    </PropertyGroup>
   <Target Name="CustomAfterBuild" Inputs="$(MSBuildAllProjects);
            @(Compile);                               
            @(_CoreCompileResourceInputs);
            $(ApplicationIcon);
            $(AssemblyOriginatorKeyFile);
            @(ReferencePath);
            @(CompiledLicenseFile);
            @(EmbeddedDocumentation); 
            $(Win32Resource);
            $(Win32Manifest);
            @(CustomAdditionalCompileInputs)"
    Outputs="@(DocFileItem);
             @(IntermediateAssembly);
             @(_DebugSymbolsIntermediatePath);                 
             $(NonExistentFile);
             @(CustomAdditionalCompileOutputs)">

   <!-- Content here -->

    </Target>

</Project>

I just copied the inputs and outputs from the CoreCompile target inside of Microsoft.CSharp.targets (I'm assuming your are using C# here) and pasted it here. What this means is that the target will be skipped whenever the CoreCompile target is executed. Also since I extended the BuildDependsOn we know that MSBuild will try and execute it whenever the project is built.

I am working on a solution consisting of 8 .NET projects. Since I am practicing TDD, I have to re-compile my solution very often. Lately I have been getting the following error about every second time when trying to compile:

Error 2 Unable to copy file "obj\Debug\Zeiterfassung.Tests.dll" to "bin\Debug\Zeiterfassung.Tests.dll". The process cannot access the file 'bin\Debug\Zeiterfassung.Tests.dll' because it is being used by another process.

Zeiterfassung.Tests.dll is the dll generated by one of my projects (it's the unit testing project). It's always this dll that cannot be copied and causes the error. Everything else works fine 100% of the time.

In about 9/10 times I can "solve" the problem by recompiling my solution again. But when the problem is getting really bad, the project just won't compile successfully no matter how often I try and I have to restart the IDE.

I used microsoft's handle.exe to ascertain which process is locking the DLL and it is devenv.exe. I also tried deleting the DLL by hand and it really can't be deleted until I restart the IDE.

Last but not least, I tried adding <GenerateResourceNeverLockTypeAssemblies>true</GenerateResourceNeverLockTypeAssemblies> to my project as suggested in another forum, but this did not help.

Please help! This problem is really starting to drive me nuts.

Edit: I might also add that I made sure my unit tests are finished when this problem occurrs. Still, the dll remains locked. I am running my tests via the Resharper unit test explorer.

By "I tried adding true to my project as suggested in another forum" do you mean that you created a property named GenerateResourceNeverLockTypeAssemblies and set it to true as suggested at http://social.msdn.microsoft.com/Forums/en-US/msbuild/thread/6f76db9a-ea37-42b3-a016-571912c28032? If not try that out.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have a couple of projects which reference SQL Server assemblies. With SQL Server 2005 and SQL Server 2008 I am currently maintaining 2 project files which point to the same source files and the only difference is the references to the SQL Server assemblies.

Is there some way that I can only maintain one project and dynamically specify the references in my build script?

Every MSBuild element (ok almost every) can have a Condition associated with it. What I would suggest is that you edit the project file (which is an MSBuild file itself) and place all the SQL server references in an ItemGroup which has a condition on it for instance:

  <ItemGroup Condition="'$(SqlServerTargetEdition)'=='2005'">
    <!-- SQL Server 2005 References here -->
    <Reference Include="..."/>
  </ItemGroup>

And another ItemGroup for Sql server 2008:

  <ItemGroup Condition="'$(SqlServerTargetEdition)'=='2008'">
    <!-- SQL Server 2008 References here -->
    <Reference Include="..."/>
  </ItemGroup>

You should provide a default value for the property SqlServerTargetEdition before those items are declared. Then at the command line you can override that value using the /p switch when invoking msbuild.exe.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I'm trying to create copy/zip a pre-compiled site which is contains empty folders(after pre-compilation). Looks like empty folders is not included to the list of items after CreateItem task. How I can care of empty folders?

thanks

MSBuild will not pick up empty folders when creating items. You will have to use a task (like the FindUnder task from the MSBuild Extension Pack) if you want to be able to place empty folders into an item.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

In MSBuild I can use the Copy task to copy files from one location to another.

I can also use the SkipUnchangedFiles property to specify that files should not be copied if they have not changed.

Is there a standard pattern for predicating a follow-up action on the condition that one or more files were copied?

For example:

  1. Copy any updated deployment scripts
  2. Execute the batch file which runs all the deployment scripts, in the correct order, if and only if one or more of the scripts have changed

One further complication is that I am using the CreateItem task to dynamically generate the list of input files:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="CopyAndRun">
  <Target Name="CopyAndRun">
    <CreateItem Include="In\**\*Test*.txt">
      <Output TaskParameter="Include" ItemName="SourceFiles"/>
    </CreateItem>
    <Copy SourceFiles="%(SourceFiles.Identity)" DestinationFolder="Out\%(RecursiveDir)" SkipUnchangedFiles="true" />
    <!-- Only want to execute this if updated files were copied -->
    <Message Text="Running..." />
  </Target>
</Project>

You can achieve this with incremental building that is provided out of the box with MSBuild.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I know that the latest book out on MSBuild says this is not possible, but I am sure I have seen a property that points to the current file.

Does anyone know a way to find the current file? (ie When the main MSBuild file imports a secondary file. What is the path to the secondary file from inside that file.)

I am trying to register my file in HKEY_LOCALMACHINE\Software\Microsoft\VisualStudio\8.0\MSBuild\SafeImports, but the path to the file is not constant on each of my coworkers computers and it is imported from several different projects so doing a hard coded relative path is not a good idea either.

If there is a way to get the current working directory that will work too (the working directory switches with each import and I think I could save it off)

There is not a reliable means to identify the "current file". When MSBuild processes a file it can import several other files, the end result is a single in memory complete representation of the file. When the targets are executing they do not know which file they were declared in.

This is why reusable build scripts must be "parameterized" to accept the location to known relative locations. For example if you have a dependency on the location where your folder for 3rd party references are located, the build script which is driving the process must declare that property for you.

MSBuild 4.0 Only

If you are using MSBuild 4.0, i.e. Visual Studio 2010/.NET 4.0, (which can target .NET 2.0/3.0/3.5) as well. Then you now have these properties which can be used for this specific purpose:

  • MSBuildThisFile
  • MSBuildThisFileDirectory
  • MSBuildThisFileDirectoryNoRoot

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I'm looking for resources for learning MS Build (not just books). At this point I'm mostly concerned with merging assemblies and automating build tasks. (nightly builds etc.)

I use the docs previously mentioned, but they weren't really helpful to learn MSBuild. For that, I turned to "Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build". After reading it and learning all the MSBuild concepts, I now use the MSBuild doc's as a reference. They finally make sense.

I am trying to use TFS team build, but can't find any decent startup documents/guides. Can somebody give me some pointers on do's/dont's and maybe a link to a good guide?

Thanks!

I'm building an ASP .NET 2.0 (C#) based web application, which is primarily intended for intra-net use, i.e. to be used inside an organization's Local Area Network.

With respect to the User Interface, there are 2 approaches which I need to choose from.

  1. Build a 1 page web app, with lots of ASP .NET AJAX 1.0 controls (modal popups) to show categorized content which would otherwise have gone into a separate .aspx page.

  2. Use the traditional approach and build multiple pages.

The 1 page UI looks and feels very cool. However, I have doubts with respect to its scalability.

Agreed that the application is intended for use over a LAN, but since it is a web app, it could potentially be used from over the internet if the client wanted to.

Owing to the 1 page UI, there's already around 2600 lines of code in the single .aspx page and another 1600 lines of code in the code-behind (.aspx.cs)

This is going to grow - to at most - 10,000 lines of code (10,000 in .aspx and 10,000 in .aspx.cs). So I need to know - how much is too much for an ASP .NET based page - is 2600 + 1600 lines of code okay for Intranet AND Internet access? How about 10,000 lines of code? What is the bottle-neck? Is this single-page approach okay or do I need to fall back to the traditional multiple-page approach?

Before I say what I intend to say I would like to state that I do not think this is a good idea. Any class (ASP.NET) or not that is 5k or 10k lines long needs to be refactored. There are a lot of comments here that keep stating that your download times will be too long. Just because you have an .aspx file that has 5k lines of code embedded or 5k in a code behind file (or both) this doesn't mean that your download times will be significant just because of this. Those lines of code are compiled and executed on the server, they are not passed to the client. So there is not a direct relationship between # of lines of code to download size.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I'm in the process of writing a custom task for NAnt. I've been basing how I write the code based on some examples I've found on the Internet and by looking at the source of some other tasks for both NAnt and Ant.

With that being said, are there any guidelines or best practices for writing good tasks for build managers like NAnt, Ant, or MSBuild? I'm looking for such in terms of exception handling and modularity (i.e. if my task does something with the file structure should I delete and recreate the output file/directory of my task or back it up somehow, etc.).

EDIT: I'm looking for guidelines on custom task creation. If I wanted to create my own task for NUnit or Selenium or to compile F# (I know they already exists but just saying that these are tasks not in the original core of most build managers), what are some best practices on writing my own task. I have no issues with build files. Just looking at guidance for writing new compiled tasks.

Thanks

Is it possible to specify the target platform (x64, x86) when building a project?

I have a build task that looks as follows:

<MSBuild Projects="%(AgentProjectFiles.FullPath)" Properties="Architecture=x86;Configuration=$(Configuration);Optimize=$(Optimize);Platform=$(Platform);OutputPath=$(OutputDirectory)\Agent\;ReferencePath=$(ReferencePath);DebugSymbols=$(DebugSymbols);DebugType=none;" />

As you can probably tell, I've thrown everything possible I have seen online into the Properties attribute in the hope that it will work. You will notice that for the Architecture property I've set it to be x86 explicitly. the $(Platform) is also set to x86. I've tried a number of permutations, without success.

Unfortunately, it seems that no matter what gets put into these properties, my class libraries are x86, but my executables are x64.

I thought perhaps the problem could be that the build properties specified in the project file itself were causing MSBuild to ignore the ones I pass through from MSBuild, but after changing these to x86, I still have the same problem.

Any ideas?

In the declaration of the AgentProjectFiles item are you defining the Properties metadata. So does it look like:

<ItemGroup>
    <AgentProjectFiles Include="something.proj">
        <Properties>SOME VALUES HERE</Properties>
    </AgentProjectFiles>
</ItemGroup>

If you have defined that then the properties passed into the Properties attribute of the MSBuild task are ignored. I've bloged about this MSBuild: Properties and AdditionalProperties Known Metadata.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I usually use web applications in Visual Studio and the MSBUILD taks in Nant builds them without any problems:

<msbuild project="MySolution.sln">
  arg value="/property:Configuration=release" />
</msbuild> 

I now have a website (not a web application) in a VS Solution and it won't build - I get a lot of namespace errors.

Should I be able to build the solution with MSBUILD even though it contains a website? Or do I need to use CSC?

You should try using the devenv.exe command if msbuild.exe is failing for you. Also you may be interested in Web Deployment Projects.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I want to make an import dependent on which target I call MSBuild with from the commandline but I'm having a hard time figuring out how to programatically access the target name(s) the .proj file was called with

i.e. <Import Project="some.targets" Condition="$(TargetName) == myTarget"/>

In general is there a way to dump all defined environment variables that exist at proj file runtime?

Unfortunately I don't think that you can access the names of the targets specified on the command line from within the build script itself. What are you trying to do, maybe there is another approach you can take.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

Similar to how you can have multiple Web.config's in ASP.NET, is it possible to have multiple App.config's depending on the build configuration?

I want to change my connection string, so if I am building configuration test, the connection string should be:

"Server=test;Initial Catalog=test..."

If I am building against test2, the connection string would be:

"Server=anotherserver; Initial Catalog=test2..."

Currently, I am doing it manually, so I would like to know how can I automate this?

Although they are not natively support, you can have XML transformations also for Windows Forms projects (App.config) and not just for Web Application projects.

There is a VS addin that enables that support and although I never tried it, it would be what I would recommend for environments where you have VS installed since is authored by at least one Microsoft employee that I'm sure has deep knowledge on MSBuild, since he already authored a book on the subject.

SlowCheetah - XML Transforms

I don't know if SlowCheetah provides support for CI servers without VS installed so I'll also throw another solution based on inclusion of custom target files.

Visual Studio App.config XML Transformation

(shameless plug notice, I was the original author of the one above)

After MSbuild has built my solution (with an asp.net website), and the webdeployment project has built and put the website in the directory _PublishedWebsites:

c:\mybuilds\buildName\Daily_20090519.3\Release_PublishedWebsites\MyWebsite.

How do I copy this to the fixed directory where IIS points to for the test website?

I have found loads of code snippets, but I cannot seem to find one that will take into account the fact that this directory name changes.

This is pretty easy. You can edit the project and insert something similar to the following.

<PropertyGroup>
  <OutputDest>$(MSBuildProjectDirectory)\..\OutputCopy\</OutputDest>
</PropertyGroup>
<Target Name="AfterBuild">
  <!-- Create an item with all the output files -->
  <ItemGroup>
    <_OutputFiles Include="$(OutputPath)**\*" Exclude="$(OutputPath)obj\**\*" />
  </ItemGroup>
  <!-- You probably don't want to include the files in the obj folder so exclude them. -->

  <Message Text="OutputDest : $(OutputDest)" />
  <Copy SourceFiles="@(_OutputFiles)"
        DestinationFiles="@(_OutputFiles->'$(OutputDest)%(RecursiveDir)%(Filename)%(Extension)')"/>
</Target>

Is this what you are looking for???

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have an MSBuild file and I am building C# projects like this:

<ItemGroup>
    <ProjectsToBuild Include="./source/ProjectA/ProjectA.csproj"/>
    <ProjectsToBuild Include="./source/ProjectB/ProjectB.csproj"/>
</ItemGroup>

<Target Name="Build">
    <MSBuild Projects="@(ProjectsToBuild)" Targets="Build">
        <Output ItemName="ProjectOutputs" TaskParameter="TargetOutputs"/>
    </MSBuild>
    <Message Text="@ProjectOutputs"/>
</Target>

I successfully get an Item containing all of the .dll files that were built:

Build:
    c:\code\bin\ProjectA.dll;c:\code\bin\ProjectB.dll

I would also like to get the Content item from each project without modifying the .csproj files. After digging around in the Microsoft .targets files, I was almost able to get it working with this:

<MSBuild Projects="@(ProjectsToBuild)" Targets="ContentFilesProjectOutputGroup">
    <Output ItemName="ContentFiles" TaskParameter="TargetOutputs"/>
</MSBuild>
<Message Text="@(ContentFiles->'%(RelativeDir)')"/>

The problem with this approach is the RelativeDir is not being set correctly. I am getting the full path instead of relative:

Build:
    c:\ProjectA\MyFolder\MyControl.ascx;c:\ProjectB\MyOtherFolder\MyCSS.css;

instead of:

Build:
    MyFolder\MyControl.ascx;MyOtherFolder\MyCSS.css;

Is there a property I can pass to the MSBuild task that will make RelativeDir behave correctly?

Or, even better, is there an easier way to get the Content item?

You can do this but it is not very intutive. I've discussed this type of technique a few times on my blog ( which is currently down :( ).

So create a new file, I named it GetContentFiles.proj which is shown here.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup>
    <Projects Include="WindowsFormsApplication1\WindowsFormsApplication1.csproj"/>
  </ItemGroup>

  <!-- This target will be executed once for each file declared in the Project target -->
  <Target Name="PrintFiles" Outputs="%(Projects.Identity)">

    <Message Text="PrintFiles" Importance="high"/>

    <MSBuild Projects="$(MSBuildProjectFile)"
             Targets="GetContentFiles"
             Properties="ProjectToGetFiles=%(Projects.Identity)">
      <Output ItemName="projContent" TaskParameter="TargetOutputs"/>
    </MSBuild>

    <Message Text="ProjContent: @(projContent)" Importance="high"/>

    <!-- Transform the projContent to have correct path -->

    <!-- 
    Get the relative path to the project itself, this serves as the base for
    the Content files path
    -->
    <PropertyGroup>
      <_ProjRelativeDir>%(Projects.RelativeDir)</_ProjRelativeDir>
    </PropertyGroup>

    <!-- This item will contain the item with the corrected path values -->
    <ItemGroup>
      <ProjContentFixed Include="@(projContent->'$(_ProjRelativeDir)%(RelativeDir)%(Filename)%(Extension)')"/>
    </ItemGroup>

    <!-- Create a new item with the correct relative dirs-->
    <Error Condition="!Exists('%(ProjContentFixed.FullPath)')"
           Text="File not found at [%(ProjContentFixed.FullPath)]"/>
  </Target>

  <Import Project="$(ProjectToGetFiles)" Condition="'$(ProjectToGetFiles)'!=''"/>

  <Target Name="GetContentFiles" Condition="'$(ProjectToGetFiles)'!=''" Outputs="@(Content)">
    <Message Text="Content : @(Content)" Importance="high"/>
    <Message Text="Inside GetContentFiles" Importance="high"/>    
  </Target>

</Project>

I will try and explain this, but it may be tough to follow. Let me know if you need me to expand on it. This file has two targets PrintFiles and GetContentFiles. The entry point into this file is the PrintFiles target, in the sense that this is the target that you are going to call. So you call the PrintFiles target which it then uses the MSBuild task to call the GetContentFiles target on itself, also it passes a value for the ProjectToGetFiles property. Because of that the Import elemnent will be executed. So what you are really doing is taking the project defined in the ProjectToGetFiles property and extending it to include the target GetContentFiles (and whatever other content is inside the GetContentFiles.proj file). So we are effectively extending that file. I'm calling this technique "MSBuild Inheritance" because. So inside the GetContentFiles target we can access all properties and items that are declared inthe ProjectToGetFiles property. So I take advantage of that by simply putting the content of the Content item into the outputs for the target, which can be accessed by the original file using the TargetOutputs from the MSBuild task.

You mentioned in your post that you wanted to correct the path values to be the right ones. The problem here is that in the .csproj file all items are declared relative to the original project file. So if you "extend" the project file in this way from a file in a different directory you must correct the file path values manually. I've done this inside the PrintFiles target, check it out.

If you execute the command msbuild GetContentFile.proj /fl /t:PrintFiles the result would be:

Build started 7/3/2009 12:56:35 AM.
Project "C:\Data\Development\My Code\Community\MSBuild\FileWrites\GetContentFile.proj" on node 0 (PrintFiles target(s)).
  PrintFiles
Project "C:\Data\Development\My Code\Community\MSBuild\FileWrites\GetContentFile.proj" (1) is building "C:\Data\Development\My Co
de\Community\MSBuild\FileWrites\GetContentFile.proj" (1:2) on node 0 (GetContentFiles target(s)).
  Content : Configs\Config1.xml;Configs\Config2.xml
  Inside GetContentFiles
Done Building Project "C:\Data\Development\My Code\Community\MSBuild\FileWrites\GetContentFile.proj" (GetContentFiles target(s)).

PrintFiles:
  ProjContent: Configs\Config1.xml;Configs\Config2.xml
Done Building Project "C:\Data\Development\My Code\Community\MSBuild\FileWrites\GetContentFile.proj" (PrintFiles target(s)).


Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.03

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I'm developing a mobile phone application that targets a lot of mobile devices based on the capabilities they offer. There would be a base feature set which all phones are expected to support and then there would be additional features that would depend on specific set of phones.

How do I manage such a code base in terms of a version control system?

I have experience with CVS and VSS but both don't quite fit into my needs for this kind of an application. The last thing I would want to do is branch the code for each of these device sets.

Let me make this a bit more clear with the help of an example. Lets say I'm developing a J2ME application using MIDP 2.0. This is the base feature set that I would expect all phones supporting MIDP 2.0 to have. On top of this I would extend this application for specific sets of phones using their SDK's. For eg. Nokia S40, Nokia S60, Sony Ericsson, Blackberry etc. All these provide additional functionalities which lets you build more on top of your base application and most of the times these would affect your whole code base from UI to core logic.

One way to achieve this is to use a combination of a build system with preprocessor flags and trying to separate the differences enough to not have too many dependencies. This can get quite complicated at times. I am wondering if there is an easier way to handle this using a smart source control system....

I like Subversion for projects which don't have a lot of developers on it. From your problem statement, to me it sounds like you should be able to acheive what you want with a good build system. So I don't think the source control itself would make much a difference. But I may be misunderstanding your problem.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I need to shell out (to call svn commit on my .application file) after a ClickOnce publish. However I've not been able to find a way to hook it into my MSBuild .csproj file.

  • The PostBuild Event is too early
  • And calling 'start Some.exe' in PostBuild does not run in the background
  • And using the AfterBuild Target from MSBuild is done before the files are deployed.

Dang!

I used the pretty cool MSBuild SideKick to inspect my .csproj file and when I run from inside SideKick the AfterBuild target does in fact happen after build. Hoever; my shell out fails and I'd prefer for things to work from inside the IDE anyway.

Does anyone have any pearls of wisdom in this area?

Have you tried creating an AfterPublish target? You should create this target in your .csproj file after the statement.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

Is it possible to use the latest MSBuild (.NET4/VS2010) with the VS2005 toolchain?

I have a C++ project which compiles against VS2005. I'm not ready to upgrade to VS2010 while the compiler is still in beta. But I'd like to use the new version of MSBuild because it builds C++ natively and provides extension points and flexibility which the old VCBuild tool doesn't.

Does anyone know if you can use MSBuild this way? Or is MSBuild in .NET 4 coupled to the VS2010 toolchain?

Thanks in advance,

Ben

If you are not ready to upgrade to VS2010 because it is in Beta, are you not concerned that .NET 4.0 is beta? You can use MSBuild 4.0 to target other version of the .NET runtime (2.0 and above), but those tools are in beta as well.

You would have to convert your projects files to MSBuild 4.0 project files and then use those. The easiest way to do this is to open the solution file in Visual Studio 2010.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

Recently I've changed our team build project file from:

<SolutionToBuild Include="$(SolutionRoot)/OurSolution.sln">

to

<SolutionToBuild Include="$(SolutionRoot)/**/*.csproj">

This was necessary because we have many projects which are not contained in the solution file, and for our purposes it is not feasible to just add the projects. We would like to be able to build them all in one go. So we found a way to recursively build all the projects.

This works fine and the build can be done with no errors. The only problem is there are no binaries copied to the drop folder! In fact, we can't find them anywhere! Building from the solution, the binaries are copied to a Binaries folder on the build agent. But nothing is there when building from the individual projects.

So my question is, where are they? Why does team build report that everything built fine, but then nothing is there to be copied. They aren't even in the normal bin/Release folder under the project directory.

Can someone help me understand? I feel it probably has something to do with information contained in the solution file which is not present in the project files, but I can't figure it out.

I think that the SolutionToBuild item is empty and nothing is getting built. You can verify by

<Message Text="SolutionToBuild: $(SolutionToBuild)" Importance="high"/>

Then look through your log for that statement.

You may want to revise your approach in any case. Better would be to create an MSBuild file yourself, i.e. BuildAll.proj. In that file just use the MSBuild task to build all the projects. Then set that file to be the SolutionToBuild. This will give you some more flexibilty when the projects are getting built. You can add steps before/after the build for those projects or all of them.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

Today I've started adding Web Deployment projects for our web application, and noticed that the deployment project picks up everything that could be a content file from the web application, including the csproj files etc.

I've seen online how you can manually exclude files using the MSBuild task ExcludeFromBuild, and that is currently the way I'm looking to approach this.

My questions: Is there a way to restrict the files sent to the Web Deployment Project, to only those that are included in the Web Application Project?

You probably could do this using an approach similar to MSBuild: How to get all generated outputs but with this you will be adding a bunch of un-needed coplexity to your build script. For this I would just say maintain that list. I would also suggest that after you exclude the files initially there should not be too many files that need to be excluded after that. If you find that at some point later there are a bunch you should find out why that is.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

We seem to be having an issue when running our deployment project in that, when it compiles, it seems to miss our master pages from the output.

Is there any way to 'force' the project to include .master files, either through editing the .wdproj file, or via another method?

Also, I've been reading up on the MSBuildTasks community project, and have followed some of the sample documentation but this doesn't appear to work. The project won't exclude files that I select, and doesn't seem to do compression either. Has anyone else tried this extension that can provide feedback/guidance?

Many thanks in advance

Update:

I have fixed this by creating an Itemgroup and doing a copy.

<ItemGroup>
  <MasterFiles Include="$(SolutionDir)\MVC\Views\Shared\Templates\**\*.master" />
</ItemGroup>

<Target Name="AfterBuild">
  <Copy SourceFiles="@(MasterFiles)" DestinationFiles="$(OutputPath)\Views\Shared\Templates\%(RecursiveDir)%(Filename)%(Extension)" />
</Target>

One problem that I've noticed with Web Deployment Projects is that it assumes that your web application has already been built. So you must build it before invoking the .wdproj itself. I'm not sure if this is your problem though.

About excluding files, you'll have to crack open the .wdproj file, which is just an MSBuild file. To exclude files add them to the ExcludeFromBuild item. For instance to make sure that your project file is not included inthe deployment you would add a statement like:

<ItemGroup>
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)*.csproj"/>
    <!-- Below excludes svn folders -->
    <ExcludeFromBuild Include="$(SourceWebPhysicalPath)**\.svn\**\*"/>
</ItemGroup>

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

What reasons could there be for the following strange behaviour, and how might I track down the issues?

We use a combination of make files and msbuild.

I have a project which needs to be strongly named. I was previously setting the snk to use in the project file like this:

<AssemblyOriginatorKeyFile>$(EnvironmentVariable)TheKeyName.snk</AssemblyOriginatorKeyFile> 

where EnvironmentVariable was defined in the batch file that launched the shell for the build like this:

set EnvironmentVariable='SomePath'

and this worked ok. Now I need the string name key to be able to be changed, so it can be different on the dev machine and the release build server. There is a variable which exists to hold the full path to the strong name key file, called StrongNameKeyFile. This is defined in the msbuild environment, and if I put some text output in the targets or properties files that are included as part of the msbuild task which build the project then I can see that this StrongNameKeyFile points to the correct location. So I changed the csproj to have this instead:

<AssemblyOriginatorKeyFile>$(StrongNameKeyFile)</AssemblyOriginatorKeyFile>

but when I try and compile this is evaluating to empty and no /keyfile is specified during the build.

We also have variable defined in the make files and these can be accessed in the csproj as well. These are used to point to the locations of referenced dlls, so that they can be different on dev and build machines. I know that these are set as the references come out correctly and everything compiles, but if I try and use one of these variables in the AssemblyOriginatorKeyFile element then it evaluates to empty in that element, but works in the reference element.

Why might this be? Is AssemblyOriginatorKeyFile treated specially somehow? How can I go about tracking the cause of this down?

There's no good reason why this should happen - as you know it normally Just Works; it's likely to be something in the chain dropping it on the floor.

One thing to try is explicitly passing it via /p:StrongNameKeyFile=XX - that would eliminate environment variables and the correct propagation thereof from your inquiries.

Another potential thing is that something is clobbering the variable as the name is used vy something else?

Run with /v:diag and you'll get dumps of all the inputs and/or variables as they change.

Or if on V4, use the MSBuild Debugger

And buy the Hashimi et al MSBuild book

Can anyone please help me in converting the below nant script to MsBuild Script?

http://localhost//Tupe path="${triad_web_src_code.dir}\T.csproj" />

If it is not a web solution then I need not map the solution & the msbuild could be like this :

But this is not working for Web project. So please help...

An alternative the the approaches listed above is to create use Web Deployment Projects (2005 2008) for this. They support creating virtual directories as well a pre-compiliation out of the box.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

if I launch the RAD Studio command prompt and run

msbuild /t:Rebuild

in the project directory, msbuild will show the full command line to invoke dcc32, including all path settings. (see Is there a Delphi library which returns all effective source paths for a project?)

If I only want to capture this msbuild console output in a file, and do not need the compiler execution, is there a way to run msbuild only to display which actions it will perform? I have checked the msbuild options but there seems to be no 'dry run' switch.

One possible (but amateurish) solution could be to modify the PATH so that msbuild will not find the compiler.

I don't think this is possible. Also I don't think that you will have success by modifying the PATH variable. As far as I knwo the CSC task will not use that to locate where the csc.exe is located.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have a process where I need to automate the process of generating the satellite assemblies. Specifically this is for WPF and combining Resx and BAML resources.

I've got a build script that works, but it requires manual adding of the .resources files I want to combine with the BAML resources. IOW, I have to add to the build script each time I add a .Resx resource. Not cool!

Currently I'm running the assembly linker manually and the script looks like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Adds the build action 'LocBamlCsv' -->
  <ItemGroup>
    <AvailableItemName Include="LocBamlCsv" />
  </ItemGroup>


  <Target Name="CreateSatelliteAssemblies"
          DependsOnTargets="$(CreateSatelliteAssembliesDependsOn)">

    <!-- Locbaml needs the runtime assemblies in the intermediate dir -->
    <Copy SourceFiles="$(ProjectDir)..\Tools\LocBaml.exe"
          DestinationFolder="$(OutputPath)" />


    <!-- generate a .resources file for .csv merged output -->
    <Exec Command="LocBaml /generate ..\..\$(IntermediateOutputPath)$(TargetName).g.$(UICulture).resources /trans:%(LocBamlCsv.FullPath) /out:../../$(IntermediateOutputPath) /cul:%(LocBamlCsv.Culture)"
          WorkingDirectory="$(OutputPath)"
          Outputs="$(OutputPath)%(LocBamlCsv.Culture)\$(TargetName).$(UICulture).dll" />

    <!-- Generate the resource assembly by merging all .resources files -->
    <!-- NOTE: Explicitly add any resource files here -->
    <Exec Command="al /template:$(TargetName).exe /culture:%(LocBamlCsv.Culture) /out:%(LocBamlCsv.Culture)\$(TargetName).resources.dll /embed:$(TargetName).g.%(LocBamlCsv.Culture).resources /embed:$(TargetName).Properties.Resources.%(LocBamlCsv.Culture).resources"
          WorkingDirectory="$(InterMediateOutputPath)"
          />


  </Target>
</Project>

As mentioned it works. but the last command that calls al would be much easier to work with if there was some way to use wild cards (ie. $(TargetName).*s.%(LocBamlCsv.Culture).resources.

I've tried a number of things. Using the build process apparently fires at the wrong time and it ends up failing to find files.

I'm not sure exactly what your problem is but you did say something that makes me wonder. "Using the build process apparently fires at the wrong time and it ends up failing to find files." From this I get the impression that you are trying to create an item which contains files that are generated during the build process. If this is the case then you should declare those as dynamic items, which are items declared inside of a target. Items declared outside of targets (static items) are evaluated before any target begins to execute. See my blog post MSBuild: Property and Item Evaluation.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have a vcproj file that includes a simple pre-build event along the lines of:

Helpertask.exe $(ProjectDir)

This works fine on developer PCs, but when the solution is built on our TFS 2008 build server under MSBuild, $(ProjectDir) is either blank or points to an unrelated folder on the server!

So far the best workaround I have managed is to hard code the developer and server paths instead:

if exist C:\DeveloperCode\MyProject   HelperTask.exe C:\DeveloperCode\MyProject
if exist D:\BuildServerCode\MyProject HelperTask.exe D:\BuildServerCode\MyProject

This hack works in post-build steps but it doesn't work for a pre-build step (the Pre-build task now does nothing at all under MSBuild!)

Do you have any ideas for a fix or workaround? I have very little hair left!

I think your problem may be related to how items are initalized. An items include attribute is evaluated at the begining of a build. So if you depend on files that are created in the build process you must declare these as dynamic items. Dynamic items are those defined inside of a target, or by using the CreateItem task. I've detailed this on my blog MSBuild: Item and Property Evaluation.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have a very complex set of MSBuild projects. Often, one .proj will invoke the <MSBuild> task to build another project file.

When reading the console log, it's hard to see which invocation of MSBuild I'm looking at.

Is there a way to make the log either:

  • Indent the output from sub-invocations of MSBuild, or
  • Put each MSBuild log in t oa new file

Preferably without spending a lot of time writing a new logger.

I cannot think of a way that you could do this. You might be able to attach an XML logger (which are availabe on the internet) and then create your own XSLT to create the formatted output that you want. If you did want the logs to be in different files then you could replace your usage of the MSBuild task with the Exec task and on the Command you can specify a different log file.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have msbuild proj file which should build solution created on Visual Studio 2005 and containing both C++ and C# project If I need to build the solution with msbuild which comes with .NET 3.5, what should be added into proj file ?

Thanks in advance

If you are trying to build a solution file which contains C++ projects then you should use devenv.exe to build it. To do this you can use the Exec task.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

Is there a workaround for conditional imports in MSBuild?

I've found evidence here and here detailing a bug in the MSBuild IDE interface. In particular, Import statements do not reload when building:

This is a known limitation. VS will only ever process the tags once, when the project is loaded. So whatever tag is active at the time the project is first loaded (based on whatever values your properties have at that time)... that's the tag that you will get for the lifetime of that project in the IDE

For example, I might want to import the bar or baz project based on the value of foo:

<Import Project="bar.targets" Condition="'$(foo)' == 'bar'" />
<Import Project="baz.targets" Condition="'$(foo)' == 'baz'" />

Is there a workaround or different approach I can use for accomplishing the desired functionality?

I don't think that you can overcome this using the conditional import mechaism. What are you really trying to accomplish?

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

Just wondering if people are using any particular tools to improve the experience of deploying web applications ( and applications in general) on the .Net stack I use a bunch of scripts and FluentMigrator for my db schema support. But I need to change my web.config, ftp stuff up and down, seems rather manual I m sure there are better ways, please enlighten me I forgot to add. I m already using msbuild or nant to do some scripting.

In our shop we are using Team Foundation Build (we use TFS for source control) and MSBuild to deploy our applications. You can write task in .NET which gives you unlimited flexibility. Using MSBuild and Team Foundation Build was a good starting point. The introduction of config transformations in Visual Studio 2010 will make this even more powerful. We use this to deploy several different web applications and click-once applications across testing, acceptance, and production environments.

Regardless of the DebugSymbols setting in my various vbproj files I want to generate .pdb files.

I have a msbuild project named FX.proj that looks like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <ProjectReferences Include="C:\product\forms.vbproj" />
    <ProjectReferences Include="C:\product\core.vbproj" />
    <ProjectReferences Include="C:\product\fx.vbproj" />
  </ItemGroup>
  <Target Name="Build">
    <MSBuild Projects="@(ProjectReferences)" Targets="Build" />
  </Target>
</Project>

I call it from the command line like this:

msbuild /t:Build /v:Minimal /nologo "/p:OutputPath=%~dp0bin;Configuration=Release" /fl /flp:LogFile=FX.log;Verbosity=Normal FX.proj

I want to override the DebugSymbols property in each vbproj.

I have attempted to add it to the command line like this:

msbuild /t:Build /v:Minimal /nologo "/p:OutputPath=%~dp0bin;Configuration=Release;DebugSymbols=true" /fl /flp:LogFile=FX.log;Verbosity=Normal FX.proj

AND to the MSBuild target Properties like this:

<Target Name="Build">
  <MSBuild Projects="@(ProjectReferences)" Targets="Build" Properties="DebugSymbols=true" />
</Target>

but neither seems to work. Whatever is set in the vbproj for the specified Configuration is what happens.

Any ideas?

I just did this exact thing, and inserted the target

  <Target Name="AfterBuild">
    <Message Text="DebugSymbols: $(DebugSymbols)" Importance="high" />
  </Target>

Into every .vbproj file (after the import statement). Here is my entire FX.proj file.

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup>
    <ProjectReferences Include="WindowsApplication1\WindowsApplication1.vbproj"/>
    <ProjectReferences Include="ClassLibrary1\ClassLibrary1.vbproj"/>
    <ProjectReferences Include="ClassLibrary2\ClassLibrary2.vbproj"/>
  </ItemGroup>

  <Target Name="Build">

    <Message Text="Building for DebugSymbols=false" Importance="high"/>
    <MSBuild Projects="@(ProjectReferences)"
             Properties="DebugSymbols=false"/>

    <Message Text="Building for DebugSymbols=true" Importance="high"/>
    <MSBuild Projects="@(ProjectReferences)"
             Properties="DebugSymbols=true"/>
  </Target>

</Project>

You can download my files at http://www.sedodream.com/Content/binary/DebSymbols.zip. BTW you may want to consider renaming the item from ProjectReference to something else, maybe Projects. ProjectReference has a specific meaning so it may be confusing.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

We have a large codebase in VS 2008. We have developers that need to rely on the VS IDE for day-to-day development. But we also have complicated dependencies and deployment steps and require an automated nightly build.

I am familiar with NAnt. It is perfect for our out-of-IDE build and deployment steps. Unfortunately, I haven't seen a nice way to integrate its build steps into the IDE. For instance, developers will want to be able to CTRL-SHIFT-B to build. I've seen steps to add NAnt as an external build tool but that doesn't allow the developer to double-click errors to jump to the source.

Is MSBuild good enough these days? Is there anything else? I can't believe we're the first to deal with complicated builds and picky developers.

EDIT: I see from this question and answers that MSBuild is probably going to be the way to go if I want full IDE integration. Any arguments against that?

Ok I'm a bit biased but MSBuild is definetly the way to go. Currently MSBuild is used to build Visual Studio itself so it is capable. There will also be a new version of MSBuild with Visual Studio 2010 (actually .NET 4.0) that will have a bunch of enhancements including building C++ projects.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

How do you know if a value was passed in on a property that does not have the [Required] flag.

What will be the value of an string that is not required and was not passed in? If it is an empty string then how do you know the difference from a empty string sent by the caller?

If you need to know if a value was set or not then you can make a flag in your property for example

public MyTask : Task
{
    private string mName;
    private bool mNameSet;

    public string Name
    {
        get{return mName;}
        set
        {
            mName = value;
            mNameSet = true;
        }
    }

... MORE HERE

}

So you can just check the mNameSet flag to see if the property was set or not. Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have a build process that does the following:

  1. Runs TLBIMP on a number (say 10) of COM DLLs (a named group of TaskItems). This is my "Import" target that uses the Exec task.
  2. Runs ILDASM on the 10 interop assemblies. This is my "Disassemble" target that uses the Exec task.
  3. Runs a custom task to take all of the 10 IL files at the same time and do some work on them (details not important, but it's crucial that ALL 10 IL files are processed together by this task). This is my "Work" target.
  4. Reassembles the 10 IL files using ILASM, back into 10 DLLs. This is my "Assemble" target, uses the Exec task.
  5. Runs ILMerge to merge the 10 assemblies into one. This is my "Merge" target, uses the Exec task.

Everything's fine and dandy when I do a clean rebuild. When I do an incremental build, and just a few of the original COM DLLs have changed, MSBuild quite correctly only does partial builds of the appropriate targets, for the Outputs that are actually out-of-date with respect to their Inputs.

Is there a way that I can "force" MSBuild, for just one target (step 3 above), to run a Task passing ALL inputs instead of just the out-of-date ones? I don't want to change the code of my custom Task to hard-code the file names, I'd like them to be provided as TaskItems from the MSBuild script, but my custom task must be given ALL of the inputs derived from the original TaskItem group of COM DLLs, not just the ones that were rebuilt with this particular build.

You seem to be on the right track with touching the files before your target is executed. Since the input files have been modified (by your touch) more recently then all the outputs the target is completly rebuilt. Another approach you can do is to create a target, i.e. CleanIntFiles, which would delete those files. Then you can place that target onto the BeforeBuildDependsOn property.

For more info about how to extend that property take a look at my article Inside MSBuild.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have added custom build messages so the Visual Studio GUI shows status messages during the course of a msbuild. I would like to now add something dynamic so I can inject random cute thoughts for the build-watcher.

How could this be done?

Thanks.

You can always write you own task, if you need guidance on how to do so grab a copy of my book. That is if it is worth the effort for these "random cute thoughts" :)

I have a single solution with multiple database projects and an SSIS projects. The entire build and deploy portion takes a few minutes especially because we deploy unit test data along with the build.

Does Visual Studio 2008 allow me to display timestamps the output window of the build so I can keep track of things?

I know I can execute SQL Scripts during Post-Deployment and Pre-Deployment, but unless I really have to, I do not want to go around adding SQL scripts to all projects in my solution for a generic thing.

I don't think that any default loggers have support for this but other loggers available to. Just search for something like "MSBuild XML Logger".

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I have msbuild building my solutions. msbuild is being called inside a batch file and the file is being called in cruisecontrol.net. When msbuild throws an error, these errors appear in the logs but cruisecontrol still builds successfully since the batch file was executed successfully. Is there any way I can have cruisecontrol change it's status to Failure in this situation????

Have you tried using the CC.NET MSBuild Task instead of calling out to a batch file?

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

I recently switched to TFS 2010 and I want to start using Team Foundation Build to automate the build process for my application. I need some good resources (blogs, articles, books) on this topic.

If you are after Team Foundation Build for TFS 2010, then take a look at the following chapter that I wrote for the book Professional Application Lifecycle Management with Visual Studio 2010.

Chapter 21: Team Foundation Build.

Otherwise the blogs that Richard points to are an excellent resource. Also the following book is very good for Team Foundation Build prior to TFS 2010

Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build

Motivation: I have fxcop integrated in the build process, which makes fxcopcmd.exe run each time the target has changed or as long as there are warnings from the previous run of fxcop.

But when one works in the studio, devenv.exe often decides to compile the project in background for whatever reasons. For example, when I add a new project to the solution I notice that fxcopcmd.exe runs, meaning a background build has started. There are also other times as well.

So, I wish to suppress fxcop when built with devenv. Our CI server builds using msbuild, so no problem there.

BTW, if there is a way to disable the background builds, that could be great.

There is a property BuildingInsideVisualStudio which will tell you this.

For example compare the result when using msbuild.exe and devenv.exe with a .csproj with the following AfterBuild target defined

<Target Name="AfterBuild">
  <Message Text="BuildingInsideVisualStudio: $(BuildingInsideVisualStudio)" Importance="high"/>
</Target>

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

Sometimes after some refactoring occurs, and we produce a successful build, some legacy code using scriptlets fails to compile on runtime. I would like it if there was a way that I can verify that scriptlets will compile using msbuild.

Is that what aspnet_compiler is for?

Thanks

I think that even the aspnet_compiler.exe tool will not complain about that. I'm not sure the best approach to avoid that.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

This might be a duplicate question as I have found a few that are similar, but not exactly the same. The title pretty much says it all, but here are the details:

The basic setup I have in VS2008 is two web site projects (not web application projects!) living in the same solution; call them A and B. A depends on B, since B contains a user control which I build into a custom server control.

Ideally, what I would like to do is have a one-click way to build everything: generate the precompiled DLL for B (without having to separately click "Publish") and then build/publish A to my IIS virtual directory. So, how do I accomplish this?

The reason for doing everything in a single click (which "Build Solution" doesn't seem to do right now) is twofold: first, fewer clicks means fewer opportunities to forget a step, or do something out-of-order, and generally less chance of making a mistake (K.I.S.S., right?). Second, I can't figure out how to configure my user control web site project (B) so that it generates the DLL in a location that I specify - or if this is even possible to do. Ideally I could just set it up so that the DLL ends up in "[B's project directory]/bin/debug/" or something like that. I want to avoid using the command line (e.g. msbuild) if possible, since I understand that even less than the VS GUI.

EDIT: Another, related question is - instead of building my ASP.NET user control (which gets built into a custom server control) inside of a Web Site Project (B), is it possible to just build it in a C# class library project, which should solve all of these issues?

You can use Web Deplooyment Projects which are integrated into VS and have some customizations through its UI but if you want to really dig into and customize the process you will have to know MSBuild.

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

When building a solution on a machine without VS installed, and getting workflow compilation errors I would like to track down the source of the errors, but the msbuild output is not very helpful:

C:\Program Files\MSBuild\Microsoft\Windows Workflow Foundation\v3.0\Workflow.Targets(80,3): error : The type or namespace name 'Foo' could not be found (are you missing a using directive or an assembly reference?)

An nothing about the file and line even with diagnostic level of verbosity. However VS manages to find the file and line number. I don't want to install VS on the machine we're doing builds on. Is there a way to tell the workflow compiler to report source file names and line numbers for compilation errors?

Thanks!

Can you build it on the command line using MSBuild on a machine in which it builds successfully using Visual Studio, or does it fail there as well?

Sayed Ibrahim Hashimi

My Book: Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build