views:

325

answers:

1

I'm fairly new to MSBuild, and I've done some customization on a Wpf project file that I'm building both in VS2010 and TFS2010. I've customized the output path as follows:

<OutputPath Condition=" '$(TeamBuildOutDir)' == '' ">$(SolutionDir)build\binaries\$(ProjectName)\$(Configuration)\$(Platform)</OutputPath>
<OutputPath Condition=" '$(TeamBuildOutDir)' != '' ">$(TeamBuildOutDir)binaries\$(ProjectName)\$(Configuration)\$(Platform)</OutputPath>

This allows me to build to a centralized binaries directory when building on the desktop, and allows TFS to find the binaries when CI builds are running.

However, it seems that in both cases, the $(ProjectDir) property is evaluating to '' at buildtime, which creates strange results. Doing some debugging, it appears as if $(ProjectName) is set by the time BeforeBuild executes, but that my OutputPath property is evaluating it prior to that point.

<ProjectNameUsedTooEarly Condition=" '$(ProjectName)' == '' ">true</ProjectNameUsedTooEarly>

The preceeding property is in the same property group as my OutputPath property. In the BeforeBuild target, $(ProjectNameUsedTooEarly) evaluates to true, but $(ProjectName) evaluates to the project name as normal by that point.

What can I do to ensure that $(ProjectName) has got a value when I use it?

edit: I just used Attrice's MSBuild Sidekick to debug through my build file, and in the very first target available for breakpoint (_CheckForInvalidConfigurationAndPlatform) all the properties seem to be set already. ProjectName is already set correctly, but my OutputPath property has already been set using the blank value of ProjectName.

+3  A: 

Hmm - bit of confusion going on there which I'll try to sort out

  1. Don't use $(ProjectDir) - use $(MSBuildProjectDir) - that's the location of your csproj in the source tree and is set by MSBuild.exe as a reserved property. I don't think $(ProjectDir) is available until after Microsoft.Common.Targets has been imported (which is done by Microsoft.Csharp.targets). Property evaluation is always carried out "in-place" within the file, and not when all the Imports have completed. This may explain why you are seeing the property as valid in the SideKick tool

  2. Likewise use $(MSBuildProjectName) (which I think will address your problem)

  3. I'm unsure about VS2010 and TFS2010 (as that uses MSBuild 4.0 and no doubt a new TeamBuild), but in 2008, it's pretty hard within a .csproj to figure out if your build was called from a command line/IDE build or from within TeamBuild. What I'm trying to say is that I don't think $(TeamBuildOutDir) is available within your csproj. I normally test $(TeamBuildConstants) property, as that property is passed down when teambuild calls your proj file. YMMV as I haven't played with 2010 yet..

Peter McEvoy
Yeah, you're right--ProjectDir is set (directly from MSBuildProjectDir, in fact) in Microsoft.Common.Targets. That much I have found in investigating this. I guess my own confusion stems from the fact that I don't yet understand the sequence of common.targets, csharp.targets, the csproj file in question, etc.2. I got ProjectName from the pre-build macro page from the project's property pages--I'll need to investigate whether MSBuildProjectName is available to team build. Where does MSBuildProjectName come from? Any idea how it's supplied?
bwerks
3. Apparently the maligned and ineffectual IsDesktopBuild has been replaced with $(BuildingInsideVisualStudio), but I only assume this having seen the property in my build log. As for TeamBuildOutDir, you're right--it's not set in a normal VS build, so you react really just based on its existence. I actually got the idea from http://blogs.msdn.com/aaronhallberg/archive/2007/06/07/preserving-output-directory-structures-in-orcas-team-build.aspx.
bwerks
+1: I just spent all afternoon beating my head over `$(ProjectName)` not being set yet. That'll teach me not to check Stack Overflow first...
James McNellis