MSBuild 如何将相同的任务传递给多个项目

MSBuild How to pass same task to multiple projects

我想 运行 从一个构建文件在多个项目上执行相同的任务,而不必将任务复制到每个项目文件。

我正在获取我的解决方案中的所有项目文件并且:

<ItemGroup>
    <ProjectsToBuild Include="..\Modules\**\*csproj"/>
</ItemGroup>

然后我在每个 ProjectToBuild 上调用 MSBuild 任务

<MSBuild Projects ="@(ProjectsToBuild)" 
             Targets="DoStuff"
             ContinueOnError ="false" 
             Properties="Configuration=$(Configuration)">
    <Output ItemName="OutputFiles" TaskParameter="TargetOutputs"/>
</MSBuild>

这不起作用,因为目标必须存在于您正在构建的项目中。

来自 MSDN

目标必须出现在所有项目文件中。如果他们不这样做,则会发生构建错误。

有什么方法可以将 DoStuff 任务传递给正在传递到 MSBuild 任务中的项目?

MSBuild 导入元素有帮助吗?

https://msdn.microsoft.com/en-us/library/92x05xfs.aspx

只需将您的任务放入一个通用任务文件中,然后将其导入到您的每个项目中。

我认为没有任何方法可以 "inject" 您的任务到其他项目中。

正如 Wolf5 所述,导入一些具有共同目标 and/or 任务的文件是执行此操作的方法。在我们的项目中,我们有 common.targets xml 文件,其中包含如下代码:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <!-- Build file for common build targets -->

  <Import Project="$(MSBuildExtensionsPath)/ExtensionPack/4.0/MSBuild.ExtensionPack.VersionNumber.targets" />
  <UsingTask TaskName="MSBuild.ExtensionPack.Compression.Zip" AssemblyFile="$(MSBuildExtensionsPath)/ExtensionPack/4.0/MSBuild.ExtensionPack.dll" />
  <UsingTask TaskName="BuildRevisionTask" AssemblyName="OurMSBuildTasks, Version=1.0.0.0, Culture=neutral, PublicKeyToken=318ab5a03b21183a"/>


  <PropertyGroup>    
    <!-- Set this property to change the branch where the build is working in -->
    <Branch>MAIN</Branch>
  </PropertyGroup>


  <!-- Called after the workspace initialization -->
  <Target Name="AfterinitializeWorkspace">
    <CallTarget Targets="PrintInfo"/>
  </Target>
</Project>

您可以在何处使用和其他语句将一些常见的共享构建步骤放在一起,这些步骤会在您的构建场景中重复出现。然后在对应于特定构建的 .proj 文件中,您只需像这样引用此 common.targets 文件:

<Import Project="common.targets"/>

并使用共享公共构建 steps/targets/tasks/properties。

由于这个项目有来自不同时区和地点的几个不同的人,我不想添加另一个要求让人们每次都手动更新他们的 proj 文件。不过我最终还是如愿以偿了。

<Target Name="BuildAll" DependsOnTargets="$(BuildAllDependsOn)"/>

<PropertyGroup>
    <BuildAllDependsOn>UpdateProjects;CoreBuild</BuildAllDependsOn>
</PropertyGroup>

<ItemGroup>
    <ProjectsToBuild Include="$(MSBuildProjectDirectory)\..\MyProjects\**\*csproj" Exclude="$(MSBuildProjectDirectory)\..\MyProjects\**\Team*csproj;$(MSBuildProjectDirectory)\..\MyProjects\**\Image*csproj"/>
</ItemGroup>

<Target Name="CoreBuild">
<Message Text="CoreBuild" />
    <MSBuild Projects ="@(ProjectsToBuild)" 
             Targets ="DoStuff" 
             ContinueOnError ="false" 
             Properties="Configuration=$(Configuration)">
        <Output ItemName="OutputFiles" TaskParameter="TargetOutputs"/>
    </MSBuild>
</Target>

<Target Name="UpdateProjects">
    <Message Text="$(MSBuildProjectDirectory)" />
    <ItemGroup>
      <CSProjects Include="$(MSBuildProjectDirectory)\..\MyProjects\**\*csproj" Exclude="$(MSBuildProjectDirectory)\..\MyProjects\**\Team*csproj;$(MSBuildProjectDirectory)\..\MyProjects\**\Image*csproj" />
    </ItemGroup>
    <Message Text="Updating Projects:[@(CSProjects)]" />
    <UpdateProject ProjectPath="%(CSProjects.Identity)" ImportsPath="$(MSBuildProjectDirectory)\projectImports.Targets" ModuleTargets="$(MSBuildProjectDirectory)\DoStuff.Targets" CommunityTasksPath="$(MSBuildProjectDirectory)" />
 </Target>

<UsingTask
  TaskName="UpdateProject"
  TaskFactory="CodeTaskFactory"
  AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
  <ParameterGroup>
      <ProjectPath Required="true" />
      <ImportsPath Required="true" />
      <ModuleTargets Required="true" />
      <CommunityTasksPath Required="true" />
  </ParameterGroup>
  <Task>
      <Reference Include="System.Xml" />
      <Reference Include="System.Xml.Linq" />
      <Using Namespace="System" />
      <Using Namespace="System.Linq" />
      <Using Namespace="System.Xml.Linq" />
      <Code Type="Fragment" Language="cs">
          <![CDATA[
              var document = XDocument.Load(ProjectPath);
              XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
              var alreadyAdded = document.Descendants(ns + "Target").Any(el => (string)el.Attribute("DependsOnTargets").Value == "DoStuff");
              if (!alreadyAdded)
                  {
                      var importsDoc = XDocument.Load(ImportsPath);
                      importsDoc.Descendants(ns + "Import").FirstOrDefault().Attribute("Project").Value = ModuleTargets;
                      importsDoc.Descendants(ns + "MSBuildCommunityTasksPath").FirstOrDefault().Value = CommunityTasksPath;
                      document.Descendants(ns + "Project").FirstOrDefault().Add(importsDoc.Root.Elements());
                      document.Save(ProjectPath);
                  }
          ]]>
      </Code>
  </Task>
</UsingTask>

我首先调用 UpdateProjects,它使用任务的位置更新每个项目文件。我使用代码块 (linq to xml) 来查找和更新项目文件。如果它已经被更新,它不会添加相同的东西两次。然后我在每个项目文件上调用构建并告诉 msbuild 添加到每个项目文件的目标的名称。