简体   繁体   中英

MSBuild How to pass same task to multiple projects

I am wanting to run the same task on multiple projects from one build file without having to copy the task to each project file.

I am getting all project files in my solution and:

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

I am then calling the MSBuild task on each ProjectToBuild

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

This doesn't work, as the Target must exist in the project you are building.

From MSDN

The targets must occur in all the project files. If they do not, a build error occurs.

Is there a way I can pass the DoStuff task to the projects that are being passed into the MSBuild task?

Would the MSBuild Import Element help?

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

Just put your tasks into a common task file, and import it into each of your projects.

I do not believe there is any way to "inject" your task into other projects.

As stated by Wolf5, importing some file with common targets and/or tasks is the way to do this. In our project we have common.targets xml file with something like following code:

<?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>

Where you can use and other statements to put together some common shared build steps that reappear in your build scenario. Then in the .proj file corresponding to particular build you just reference this common.targets file like this:

<Import Project="common.targets"/>

And use the shared common build steps/targets/tasks/properties.

Since this project has a few different people from different timezones and locations, I didn't want to add another requirement for people to update their proj files manually everytime. I was able to do what I wanted in the end however.

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

I first call UpdateProjects which updates each projects file with the location of the task. I use a code block (linq to xml) to find and update the project file. If it has already been updated, it won't add the same thing twice. I then call build on each project file and tell msbuild the name of the target that was added to each project file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM