使用MSBuild任务从csproj获取Content项

时间:2009-07-02 20:36:14

标签: msbuild

我有一个MSBuild文件,我正在构建这样的C#项目:

<ItemGroup>
    <ProjectsToBuild Include="./source/ProjectA/ProjectA.csproj"/>
    <ProjectsToBuild Include="./source/ProjectB/ProjectB.csproj"/>
</ItemGroup>

<Target Name="Build">
    <MSBuild Projects="@(ProjectsToBuild)" Targets="Build">
        <Output ItemName="ProjectOutputs" TaskParameter="TargetOutputs"/>
    </MSBuild>
    <Message Text="@ProjectOutputs"/>
</Target>

我成功获取了一个包含所有已构建的.dll文件的项目:

Build:
    c:\code\bin\ProjectA.dll;c:\code\bin\ProjectB.dll

我还想从每个项目中获取Content项而不修改.csproj文件。在浏览了Microsoft .targets文件之后,我几乎能够使用它:

<MSBuild Projects="@(ProjectsToBuild)" Targets="ContentFilesProjectOutputGroup">
    <Output ItemName="ContentFiles" TaskParameter="TargetOutputs"/>
</MSBuild>
<Message Text="@(ContentFiles->'%(RelativeDir)')"/>

这种方法的问题是没有正确设置RelativeDir。我得到了完整的道路,而不是亲戚:

Build:
    c:\ProjectA\MyFolder\MyControl.ascx;c:\ProjectB\MyOtherFolder\MyCSS.css;

而不是:

Build:
    MyFolder\MyControl.ascx;MyOtherFolder\MyCSS.css;

我可以传递给MSBuild任务的属性是否会使RelativeDir正常运行?

或者,更好的是,有更简单的方法来获取内容项吗?

2 个答案:

答案 0 :(得分:7)

你可以这样做,但它不是很直观。我已经在我的blog上讨论了这种类型的技术(当前已经失效了:()。

因此,创建一个新文件,我将其命名为GetContentFiles.proj,如下所示。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup>
    <Projects Include="WindowsFormsApplication1\WindowsFormsApplication1.csproj"/>
  </ItemGroup>

  <!-- This target will be executed once for each file declared in the Project target -->
  <Target Name="PrintFiles" Outputs="%(Projects.Identity)">

    <Message Text="PrintFiles" Importance="high"/>

    <MSBuild Projects="$(MSBuildProjectFile)"
             Targets="GetContentFiles"
             Properties="ProjectToGetFiles=%(Projects.Identity)">
      <Output ItemName="projContent" TaskParameter="TargetOutputs"/>
    </MSBuild>

    <Message Text="ProjContent: @(projContent)" Importance="high"/>

    <!-- Transform the projContent to have correct path -->

    <!-- 
    Get the relative path to the project itself, this serves as the base for
    the Content files path
    -->
    <PropertyGroup>
      <_ProjRelativeDir>%(Projects.RelativeDir)</_ProjRelativeDir>
    </PropertyGroup>

    <!-- This item will contain the item with the corrected path values -->
    <ItemGroup>
      <ProjContentFixed Include="@(projContent->'$(_ProjRelativeDir)%(RelativeDir)%(Filename)%(Extension)')"/>
    </ItemGroup>

    <!-- Create a new item with the correct relative dirs-->
    <Error Condition="!Exists('%(ProjContentFixed.FullPath)')"
           Text="File not found at [%(ProjContentFixed.FullPath)]"/>
  </Target>

  <Import Project="$(ProjectToGetFiles)" Condition="'$(ProjectToGetFiles)'!=''"/>

  <Target Name="GetContentFiles" Condition="'$(ProjectToGetFiles)'!=''" Outputs="@(Content)">
    <Message Text="Content : @(Content)" Importance="high"/>
    <Message Text="Inside GetContentFiles" Importance="high"/>    
  </Target>

</Project>

我会尝试解释这一点,但可能很难遵循。如果您需要我扩展它,请告诉我。此文件有两个目标 PrintFiles GetContentFiles 。此文件的入口点是PrintFiles目标,因为这是您要调用的目标。因此,您调用 PrintFiles 目标,然后使用MSBuild task调用GetContentFiles目标,同时传递 ProjectToGetFiles 属性的值。因此,Import elemnent将被执行。所以你真正做的是获取 ProjectToGetFiles 属性中定义的项目并将其扩展为包含目标GetContentFiles(以及GetContentFiles.proj文件中的任何其他内容)。所以我们实际上扩展该文件。我称这种技术为“MSBuild继承”,因为。因此,在GetContentFiles目标中,我们可以访问在ProjectToGetFiles属性中声明的所有属性和项。所以我通过简单地将内容项的内容放入目标的输出中来利用它,原始文件可以使用MSBuild中的 TargetOutputs 访问它任务。

您在帖子中提到您想要将路径值更正为正确的值。这里的问题是在.csproj文件中所有项都是相对于原始项目文件声明的。因此,如果以这种方式从不同目录中的文件“扩展”项目文件,则必须手动更正文件路径值。我已经在 PrintFiles 目标中完成了这项工作,请查看。

如果您执行命令 msbuild GetContentFile.proj / fl / t:PrintFiles ,结果将是:

Build started 7/3/2009 12:56:35 AM.
Project "C:\Data\Development\My Code\Community\MSBuild\FileWrites\GetContentFile.proj" on node 0 (PrintFiles target(s)).
  PrintFiles
Project "C:\Data\Development\My Code\Community\MSBuild\FileWrites\GetContentFile.proj" (1) is building "C:\Data\Development\My Co
de\Community\MSBuild\FileWrites\GetContentFile.proj" (1:2) on node 0 (GetContentFiles target(s)).
  Content : Configs\Config1.xml;Configs\Config2.xml
  Inside GetContentFiles
Done Building Project "C:\Data\Development\My Code\Community\MSBuild\FileWrites\GetContentFile.proj" (GetContentFiles target(s)).

PrintFiles:
  ProjContent: Configs\Config1.xml;Configs\Config2.xml
Done Building Project "C:\Data\Development\My Code\Community\MSBuild\FileWrites\GetContentFile.proj" (PrintFiles target(s)).


Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.03

Sayed Ibrahim Hashimi

我的书:Inside the Microsoft Build Engine : Using MSBuild and Team Foundation Build

答案 1 :(得分:1)

如果这有助于其他人 - 请使用TargetPath而不是RelativeDir:

<MSBuild Projects="@(ProjectsToBuild)" Targets="ContentFilesProjectOutputGroup">
    <Output ItemName="ContentFiles" TaskParameter="TargetOutputs"/>
</MSBuild>
<Message Text="@(ContentFiles->'%(TargetPath)')"/>

这将为您提供每个项目的相对路径。