我有一个是产生一些文件输出到项目A的输出目录($(TARGETDIR))的自定义MSBuild任务。 当前的代码是这样的:
<MyCustomTask ...>
<Output TaskParameter="OutputFiles" ItemName="FileWrites"/>
</MyCustomTask>
一个项目B的参考项目A,但问题是,建设项目B时,产生由MyCustomTask文件不会被复制到项目B的输出目录。
我们怎样才能动态生成的额外文件复制与MSBuild项目依赖的一部分吗?
我终于设法无需修改,它会自动执行从B项目的副本。 IIya不是从溶液中,到目前为止,但事实是,我不能静态地生成的文件列表,以从项目A与MyCustomTask是动态的。 挖掘更多的进入后Microsoft.Common.targets
,我发现项目B将通过调用目标得到项目A输出的列表GetCopyToOutputDirectoryItems
。 这个目标是依赖从AssignTargetPaths
这本身就是依赖于目标列表属性AssignTargetPathsDependsOn
。
因此,为了生成动态内容,并获得该内容被通过标准的项目依赖自动复制,我们需要在两个不同的地方挂钩A计划 :
- 在
AssignTargetPathsDependsOn
,因为它是通过GetCopyToOutputDirectoryItems间接项目B上项目A调用。 并且还间接地项目A调用时PrepareResource
被调用。 在这里,我们只是outputing将生成(按项目A)或项目B消耗的文件列表。 AssignTargetPathsDependsOn将调用自定义任务MyCustomTaskList
其只负责输出文件的列表(而不是生成它们),文件将创建动态的“内容”与此列表CopyOutputDirectory
。 - 在
BuildDependsOn
实际上为了生成项目A的内容。 这将调用MyCustomTask
将生成的内容。
所有这一切都是建立像这样的项目A:
<!-- 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>
这种方法正在与anykind正在使用Common.Targets C#项目(所以它正在与纯桌面的WinRT XAML应用程序或Windows Phone 8个项目)。
像这样的东西似乎工作,手动包含到项目A的.csproj的(记住VS有偶尔解决通配符成绝对路径和覆盖.csproj的一个坏习惯),或者通过自定义任务本身的动态注入。 此外,VS开放缓存itemgroups,因此可能无法复制文件或失败,如果他们在那里,但删除的构建。 在这种情况下,项目需要重新加载或VS重新启动重新评估itemgroups。 MSBuild的,TFS等应始终工作。
<ItemGroup>
<Content Include="$(TargetDir)\*.txt">
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
如果你已经这样做了自己构建与MSBuild的,你可以添加一个复制任务 ,以推动自己周围的文件吗?
据我了解,你想通过写ProjectB.msbuild仅此行添加额外的步骤:
<Import Project="ProjectA.msbuild" />
为了实现它,你可以写类似的项目A:
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn);MyCustomTask</BuildDependsOn>
</PropertyGroup>
这增加了你的任务就是建设任务的依赖性列表。
请参阅有关详细信息,这个问题: 了StyleCop MS建立的魔力? 谁是调用了StyleCop目标是什么?