使用MSBuild任务从csproj获取Content项

Mic*_*ael 2 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>
Run Code Online (Sandbox Code Playgroud)

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

Build:
    c:\code\bin\ProjectA.dll;c:\code\bin\ProjectB.dll
Run Code Online (Sandbox Code Playgroud)

我还想从每个项目中获取Content项而不修改.csproj文件.在微软.targets文件中挖掘之后,我几乎能够使用它:

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

这种方法的问题是没有正确设置RelativeDir.我正在获得完整的路径而不是相对的:

Build:
    c:\ProjectA\MyFolder\MyControl.ascx;c:\ProjectB\MyOtherFolder\MyCSS.css;
Run Code Online (Sandbox Code Playgroud)

代替:

Build:
    MyFolder\MyControl.ascx;MyOtherFolder\MyCSS.css;
Run Code Online (Sandbox Code Playgroud)

是否有可以传递给MSBuild任务的属性,这将使RelativeDir行为正常?

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

Say*_*imi 8

你可以做到这一点,但它不是很直观.我已经在我的博客上讨论了这种类型的技术(目前已经下来了:().

所以创建一个新文件,我把它命名为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>
Run Code Online (Sandbox Code Playgroud)

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

Sayed Ibrahim Hashimi

我的书:Microsoft Build Engine内部:使用MSBuild和Team Foundation Build