I have finally managed to perform automatically the copy from Project B without having to modify it. IIya was not so far from the solution, but the fact is that I cannot generate statically as the list of files to generate from Project A with MyCustomTask is dynamic. After digging more into Microsoft.Common.targets
, I have found that ProjectB will get the list of output from Project A by calling the target GetCopyToOutputDirectoryItems
. This target is dependent from AssignTargetPaths
which itself is dependent on the target list property AssignTargetPathsDependsOn
.
So in order to generate dynamically content and to get this content being copied automatically through standard project dependency, we need to hook Project A at two different places:
- In
AssignTargetPathsDependsOn
as it is called indirectly by Project B on Project A through GetCopyToOutputDirectoryItems. And also it is indirectly called by Project A when PrepareResource
is called. Here, we are just outputing the list of files that will be generated (by Project A) or consumed by Project B. AssignTargetPathsDependsOn will call a custom task MyCustomTaskList
which is only responsible to output the list of files (but not to generate them), this list of files will create dynamic "Content" with CopyOutputDirectory
.
- In
BuildDependsOn
in order to actually generate the content in Project A. This will call MyCustomTask
that will generate the content.
All of this was setup like this in ProjectA:
<!-- In Project A -->
<!-- Task to generate the files -->
<UsingTask TaskName="MyCustomTask" AssemblyFile="$(PathToMyCustomTaskAssembly)"/>
<!-- Task to output the list of generated of files - It doesn't generate the file -->
<UsingTask TaskName="MyCustomTaskList" AssemblyFile="$(PathToMyCustomTaskAssembly)"/>
<!-- 1st PART : When Project A is built, It will generate effectively the files -->
<PropertyGroup>
<BuildDependsOn>
MyCustomTaskTarget;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
<Target Name="MyCustomTaskTarget">
<!-- Call MyCustomTask generate the files files that will be generated by MyCustomTask -->
<MyCustomTask
ProjectDirectory="$(ProjectDir)"
IntermediateDirectory="$(IntermediateOutputPath)"
Files="@(MyCustomFiles)"
RootNamespace="$(RootNamespace)"
>
</MyCustomTask>
</Target>
<!-- 2nd PART : When Project B is built, It will call GetCopyToOutputDirectoryItems on ProjectA so we need to generate this list when it is called -->
<!-- For this we need to override AssignTargetPathsDependsOn in order to generate the list of files -->
<!-- as GetCopyToOutputDirectoryItems ultimately depends on AssignTargetPathsDependsOn -->
<!-- Content need to be generated before AssignTargets, because AssignTargets will prepare all files to be copied later by GetCopyToOutputDirectoryItems -->
<!-- This part is also called from ProjectA when target 'PrepareResources' is called -->
<PropertyGroup>
<AssignTargetPathsDependsOn>
$(AssignTargetPathsDependsOn);
MyCustomTaskListTarget;
</AssignTargetPathsDependsOn>
</PropertyGroup>
<Target Name="MyCustomTaskListTarget">
<!-- Call MyCustomTaskList generating the list of files that will be generated by MyCustomTask -->
<MyCustomTaskList
ProjectDirectory="$(ProjectDir)"
IntermediateDirectory="$(IntermediateOutputPath)"
Files="@(MyCustomFiles)"
RootNamespace="$(RootNamespace)"
>
<Output TaskParameter="ContentFiles" ItemName="MyCustomContent"/>
</MyCustomTaskList>
<ItemGroup>
<!--Generate the lsit of content generated by MyCustomTask -->
<Content Include="@(MyCustomContent)" KeepMetadata="Link;CopyToOutputDirectory"/>
</ItemGroup>
</Target>
This method is working with anykind of C# projects that is using Common.Targets (so It is working with pure Desktop, WinRT XAML App or Windows Phone 8 projects).