As part of a solution containing many projects, I have a project that references (via a <ProjectReference>
three other projects in the solution, plus some others). In the AfterBuild
, I need to copy the outputs of 3 specific dependent projects to another location.
Via various SO answers, etc. the way I settled on to accomplish that was:
<MSBuild
Projects="@(ProjectReference)"
Targets="Build"
BuildInParallel="true"
Condition="'%(Name)'=='ProjectA' OR '%(Name)'=='ProjectB' OR '%(Name)'=='ProjectC'">
<Output TaskParameter="TargetOutputs" ItemName="DependentAssemblies" />
</MSBuild>
<Copy SourceFiles="@(DependentAssemblies)" DestinationFolder="XX" SkipUnchangedFiles="true" />
However, I ran into problems with this. The <MSBuild
step's IncrementalClean
task ends up deleting a number of the outputs of ProjectC
. When running this under VS2008, a build.force
file being deposited in the obj/Debug
folder of ProjectC which then triggers ProjectC getting rebuilt if I do a Build on the entire solution if the project containing this AfterBuild
target, whereas if one excludes this project from the build, it [correctly] doesn't trigger a rebuild of ProjectC (and critically a rebuild of all dependents of ProjectC). This may be VS-specific trickery in this case which would not occur in the context of a TeamBuild or other commandline MSBuild invocation (but the most common usage will be via VS so I need to resolve this either way)
The dependent projects (and the rest of the solution in general) have all been created interactively with VS, and hence the ProjectRefence
s contain relative paths etc. I've seen mention of this being likely to causing issues - but without a full explanation of why, or when it'll be fixed or how to work around it. In other words, I'm not really interested in e.g. converting the ProjectReference
paths to absolute paths by hand-editing the .csproj.
While it's entirely possible I'm doing something stupid and someone will immediately point out what it is (which would be great), be assured I've spent lots of time poring over /v:diag
outputs etc. (although I havent tried to build a repro from the ground up - this is in the context of a relatively complex overall build)
You may protect your files in ProjectC if you call a target like this first:
Your original solution should work simply by changing
to
The
GetTargetPath
target simply returns theTargetPath
property and doesn't require building.My current workaround is based on this SO question, i.e, I have:
This however will break under TeamBuild (where all the outputs end up in one directory), and also if the names of any of the outputs of the dependent projects change.
EDIT: Also looking for any comments on whether there's a cleaner answer for how to make the hardcoding slightly cleaner than:
and:
As noted in my comment, calling GetTargetPath on the referenced project only returns the Primary output assembly of that project. To get all the referenced copy-local assemblies of the referenced project it's a bit messier.
Add the following to each project that you are referencing that you want to get the CopyLocals of:
My particular situation is that I needed to recreate the Pipeline folder structure for System.AddIn in the bin folder of my top level Host project. This is kinda messy and I was not happy with the MSDN suggested solutions of mucking with the OutputPath - as that breaks on our build server and prevents creating the folder structure in a different project (eg a SystemTest)
So along with adding the above target (using a .targets import), I added the following to a .targets file imported by each "host" that needs the pipeline folder created:
I also needed to add the required PipelineFolder meta data to the actual project references. For example: