Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
388 views
in Technique[技术] by (71.8m points)

c# - MSBuild to copy dynamically generated files as part of project dependency

I have a custom msbuild task that is generating some output files to the output directory ($(TargetDir)) of a ProjectA. Current code is something like this:

<MyCustomTask ...>
   <Output TaskParameter="OutputFiles" ItemName="FileWrites"/>
</MyCustomTask>

A ProjectB is referencing ProjectA but the problem is that when building ProjectB, generated files by MyCustomTask are not copied to the output directory of the ProjectB.

How can we get dynamically generated additional files to be copied as part of project dependency with MSBuild?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...