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