Friday, December 14, 2012

Setting up Visual Studio for MSBuild development

When working with MSBuild in Visual Studio the easiest thing to do is open a CMD or POSH console, make sure the Framework bin directoy is in your path and run MSBuild from the command line.  Back and forth you go between Console and XML editor in Visual Studio.  It works, done it plenty, but the better way is to create an External Tool to launch it within VS.


ItemPath and ItemDir are the easiest arguments to use whatever is in your edit session.

In the next step, we'll be configuring for Visual Studio Debug. 

http://blogs.msdn.com/b/visualstudio/archive/2010/07/06/debugging-msbuild-script-with-visual-studio.aspx

So make a copy of your MSBuild tool and call it MSBuild Debug.  Add the /debug argument.




Wednesday, December 05, 2012

MSBuild Relative Path using GetDirectoryNameOfFileAbove

I stumbled on this yesterday just before I needed it today! 

Problem:
We have an VisualStudio Project that is burried several directories below the Main Solution (.sln).   We have 'project level' solutions to facilitate development without loading the mega-solution.
However, it needs to reference a targets file at the root solution level.  You can make a relative reference by including as many ..\ parent directory references as necessary but it is brittle.  If, or should I say 'when', projects are re-organized the reference will break the the project won't even build.


I've faced this problem in various forms for years.  The GetDirectoryNameOfFileAbove function available in 4.0 and later is just what I needed.

http://msdn.microsoft.com/en-us/library/dd633440.aspx#BKMK_GetDirectoryNameOfFileAbove

Two things about it.  It doesn't match folders.  When it says "Get Directory Name" of "File Above", it means just that.  The example in the MS doc references some file called 'EnlistmentInfo.props'.  (You can see just how many people Blog by cutting and pasting MS docs by searching for this string!)  This file must be in a parent directory. 

My situation called for looking in a child folder for a file.  I started out by trying to use GetDirectoryNameOfFileAbove to find the folder in a parent but it didn't work.  However, turning it into a relative path solved the problem.

This is a contrived test to find a Web.Config file in a project called TestProject somewhere in my parent path.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Debug">
  <PropertyGroup>
    <TestPath>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), TestProject\Web.config))\TestProject\Web.config</TestPath>
  </PropertyGroup>
 
  <Target Name="Debug">
    <Message Text="TestPath: $(TestPath)"/>
  </Target>
</Project>

Note:  You don't need quotes for string literals inside the GetDirectoryNameOfFileAbove call.  This is a little strange but it makes your XML easier.  Also, don't use any 'anchor' folder references like '.\' or '\' at the beginning of your relative file reference.

$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),TestProject\Web.config))\TestProject\Web.config

However, GetDirectoryNameOfFileAbove doesn't return a ending '\' for the folder reference. Therefore you have to have the '\' when you build the path. 

I found creating a small sample project like this to be helpful in figuring out the behavior before translating it into the Import statement I really needed.