in

CodePrairie .NET

South Dakota .NET User Group

chrisortman

Another MSBuild gotcha

I came across an oddity last night when trying to setup a build….

 

I’m going to go into a little more detail than is maybe necessary incase someone sees it and can say whoa back up your going about this all wrong

 

I have the following root directory structure

 

DependentFiles

Source

Vendor

VendorLibs

Vendor.proj

Myproject.targets

 

I use several open source projects which I need to build myself because they rely on each other but the distributed binaries don’t all have strong names or necessarily reference the correct version of some other tool, so in my Vendor folder I have put the source for these projects.  I don’t want to have to ProjectReference to these guys from my own code since I typically don’t want to have them all loaded in my solution and I wanted to make minimal changes to the Vendor code so I can take updates with less hassle. So I created a vendor.proj build file that basically will build all the code in \Vendor and copy the resulting dll’s and pdb’s to VendorLibs, my projects then reference these dll’s. One thing I found with this approach is that doing master-child builds sucks when it comes to path resolution if the child projects don’t reference paths using $(MSBuildProjectDirectory)\<some relative path>. After some toiling I did get vendor.proj to build correctly when executing from the root directory alo (msbuild vendor.proj /t:Build

 

Here’s a typical target in my vendor.proj file:

  <ItemGroup>

    <DependentFiles Include="$(MSBuildProjectDirectory)\DependentFiles\**\*.dll" />

 

    <CastleDynProxyProjFiles Include="$(MSBuildProjectDirectory)\Vendor\Castle\Tools\DynamicProxy\Castle.DynamicProxy\Castle.DynamicProxy-2.0.csproj" />

   

    <CastleIOCProjFiles Include="$(MSBuildProjectDirectory)\Vendor\Castle\InversionOfControl\**\*-2-0.csproj" />

  </ItemGroup>

<Target Name="build-castle-ioc" DependsOnTargets="build-dynamic-proxy">      

    <MSBuild Projects="@(CastleIOCProjFiles)" Properties="Configuration=$(Configuration)"

             Targets="$(vstargets)">

      <Output TaskParameter="TargetOutputs" ItemName="CastleIOCOut"/>

    </MSBuild>

    <CreateItem Include="@(CastleIOCOut)" AdditionalMetadata="Project=CastleIOC">

      <Output TaskParameter="Include" ItemName="BuiltAssemblies"/>

    </CreateItem>

  </Target>

 

Now onto my code under Source I have:

Project1

Project2

Project3

…etc….

MyProject.sln

 

Now when I build my code I want to ensure that the VendorLibs are up-to-date (I haven’t done up to date yet, I just make sure the files are there) and if not I need to rebuild the vendor code. I accomplish this in my MyProject.targets file using something like this:

 

  <PropertyGroup>

    <ResolveReferencesDependsOn>

      EnsureVendorLibs;

      $(ResolveReferencesDependsOn);

    </ResolveReferencesDependsOn>

  </PropertyGroup>

 

<Target Name="EnsureVendorLibs">

       

    <MSBuild Projects="..\..\vendor.proj" Targets="Build"

             Properties="Configuration=$(Configuration) "

             Condition="!Exists('$(MSBuildProjectDirectory)\..\..\VendorLibs')"

             />

  </Target>

 

Now here’s what I ran into say from my Source folder I do

Msbuild MyProject.sln /t:Build

And it needs to build the VendorLibs ....

Some of the projects under Vendor use project to project references which when called via msbuild vendor.proj /t:Build and being passed using an ItemGroup of the project files to an msbuild task will happily resolve….however when my build using MyProject.sln and call to EnsureVendorLibs the project references fail to resolve. Here’s why, in the Microsoft.common.targets file we have this task

 

<Target

        Name="SplitProjectReferencesByType"

        Condition="'@(ProjectReference)'!=''">

 

        <!-- Assign a project configuration to each project reference if we're building a solution file. -->

        <AssignProjectConfiguration

            ProjectReferences="@(ProjectReference)"

            SolutionConfigurationContents="$(CurrentSolutionConfigurationContents)"

            Condition="'$(BuildingSolutionFile)'=='true'">

 

            <Output TaskParameter="AssignedProjects" ItemName="_ProjectReferenceWithConfiguration"/>

 

        </AssignProjectConfiguration>

 

<!—rest of target ommitted for brevity sake -- >

    </Target>

 

Well since I initially invoked MSBuild using msbuild MyProject.sln /t:Build the $(BuildingSolutionFile) property will evaluate to true which causes resolution to fail because the ProjectReferences are not defined in my solution (or any projects built by my solution)

The workaround I used was to change my EnsureVendorLibs target like so

<Target Name="EnsureVendorLibs">

       

    <MSBuild Projects="..\..\vendor.proj" Targets="Build"

             Properties="Configuration=$(Configuration);BuildingSolutionFile=false"

             Condition="!Exists('$(MSBuildProjectDirectory)\..\..\VendorLibs')"

             />

 

    <Message Text="$(BuildingSolutionFile)" />

  </Target>

 

Which now when I execute will build correctly and after the task completes the $(BuildingSolutionFile) will contain it’s original value.

 

 I posted this to the msdn forums also http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=464794&SiteID=1&mode=1

Published Jun 09 2006, 09:48 AM by chrisortman
Filed under:

Comments

No Comments

Leave a Comment

(required)  
(optional)
(required)  
Add
Powered by Community Server (Commercial Edition), by Telligent Systems