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