MSBuild - Compare ItemGroups Metadata

I am trying to write a build script for our source tree. This tree consists of a (large) number of solutions with assembly references between them. I created an element group containing all the solutions, and am working on this ItemGroup to create solutions.

I also need to copy some project output to the "exes output" folder, each of which is in their own folder. I have attached some metadata to the decision element that points to the projects I want to exit from. Since I can potentially have more than one project to output from each solution, I do this by providing metadata to the value that is passed to the ItemGroup Include to create the ItemGroup separately. This works with pleasure, and I can execute this dynamically created ItemGroup.

The last step that I want to take, which causes me a headache, is that for some of these output projects I want to specify a special folder name. Now I can do this by changing the metadata inside the target that does the work, for example:

<?xml version="1.0" encoding="utf-8"?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> <!-- Define all the solutions to build, and any projects we want to handle the output of --> <ItemGroup> <SolutionsToBuild Include="$(MSBuildProjectDirectory)\Solution1\Solution1.sln"> <ProjectsToOutput> $(MSBuildProjectDirectory)\Solution1\Project1A\Project1A.csproj; $(MSBuildProjectDirectory)\Solution1\Project1B\Project1B.csproj; </ProjectsToOutput> </SolutionsToBuild> <SolutionsToBuild Include="$(MSBuildProjectDirectory)\Solution2\Solution2.sln"> <ProjectsToOutput> $(MSBuildProjectDirectory)\Solution2\Project2A\Project2A.csproj; $(MSBuildProjectDirectory)\Solution2\Project2B\Project2B.csproj; </ProjectsToOutput> </SolutionsToBuild> </ItemGroup> <Target Name="Build"> <CallTarget Targets="DoBuild" /> </Target> <Target Name="DoBuild" Outputs="%(SolutionsToBuild.Identity)"> <Message Text="Building project: %(SolutionsToBuild.FullPath)" /> <!-- eg <MSBuild Projects="%(SolutionsToBuild.FullPath)" /> --> <PropertyGroup> <ProjectsToOutputIncludeMask>%(SolutionsToBuild.ProjectsToOutput)</ProjectsToOutputIncludeMask> </PropertyGroup> <ItemGroup> <OutputProjects Include="$(ProjectsToOutputIncludeMask)" /> <!-- Now create the OutputTo metadata --> <!-- Default to the same name as the project file --> <OutputProjects> <OutputTo>%(OutputProjects.FileName)</OutputTo> </OutputProjects> <!-- Now override specific projects OutputTo metadata --> <OutputProjects Condition="'%(OutputProjects.FileName)' == 'Project1A'"> <OutputTo>ArbitraryFolder1</OutputTo> </OutputProjects> <OutputProjects Condition="'%(OutputProjects.FileName)' == 'Project2B'"> <OutputTo>ArbitraryFolder2</OutputTo> </OutputProjects> </ItemGroup> <Message Text=" Outputting project: %(OutputProjects.FullPath) -> %(OutputTo)" /> </Target> </Project> 

However, this script assembly must be supported by all developers on the team, not all of whom are familiar with MSBuild. So I would like to define another ItemGroup at the top of the script that defines any special names. Then they can ignore all goals and objectives and simply support groups of elements, for example:

 <ItemGroup> <SolutionsToBuild Include="$(MSBuildProjectDirectory)\Solution1\Solution1.sln"> <ProjectsToOutput> $(MSBuildProjectDirectory)\Solution1\Project1A\Project1A.csproj; $(MSBuildProjectDirectory)\Solution1\Project1B\Project1B.csproj; </ProjectsToOutput> </SolutionsToBuild> <SolutionsToBuild Include="$(MSBuildProjectDirectory)\Solution2\Solution2.sln"> <ProjectsToOutput> $(MSBuildProjectDirectory)\Solution2\Project2A\Project2A.csproj; $(MSBuildProjectDirectory)\Solution2\Project2B\Project2B.csproj; </ProjectsToOutput> </SolutionsToBuild> <OutputNames Include="Project1A"> <OutputFolder>ArbitraryFolder1</OutputFolder> </OutputNames> <OutputNames Include="Project2B"> <OutputFolder>ArbitraryFolder2</OutputFolder> </OutputNames> </ItemGroup> 

However, in some way I tried to get the DoBuild target to update metadata, it falls on her face. I thought I could do this:

  <!-- Now override specific projects OutputTo metadata --> <OutputProjects Condition="'%(OutputProjects.FileName)' == '%(OutputNames.Identity)'"> <OutputTo>%(OutputNames.OutputFolder)</OutputTo> </OutputProjects> 

But this code is split into a group of OutputProjects elements, and then into a group of OutputNames elements, so the condition is never true (one of the comparison arguments is always empty).

At this stage, I, unfortunately, cannot change the structure of the solution structure or the structure of the structure of the output folders. Is there some kind of MSBuild trick I'm missing that might help me here? I would not mind including a custom task to do this work, but if possible, would prefer a direct MSBuild solution.

If that matters, I am using MSBuild v4.

+1
source share
1 answer

Oh. Stumbled upon an answer while playing with this.

First, I researched this post about the intersection of two groups of objects. Therefore, I changed the group of OutputNames elements to an identical identity, such as ProductGroup OutputProjects:

 <OutputNames Include="$(MSBuildProjectDirectory)\Solution1\Project1A\Project1A.csproj"> <OutputFolder>ArbitraryFolder1</OutputFolder> </OutputNames> <OutputNames Include="$(MSBuildProjectDirectory)\Solution2\Project2B\Project2B.csproj"> <OutputFolder>ArbitraryFolder2</OutputFolder> </OutputNames> 

This allows me to load% (Identity) and get this intersection:

 <Message Condition="'%(Identity)' != '' and '@(OutputProjects)' != '' and '@(OutputNames)' != ''" Text="Found a match for %(Identity)" /> 

However, when the OutputFolder metadata is also referenced in the same Task that became part of the batch processing, as well as the result of subsequent printing:

 <Message Condition="'%(Identity)' != '' and '@(OutputProjects)' != '' and '@(OutputNames)' != ''" Text="Found a match for %(Identity): %(OutputNames.OutputFolder)" /> 

But using conversion over a property instead of direct access, it is not considered as part of batch processing:

 <Message Condition="'%(Identity)' != '' and '@(OutputProjects)' != '' and '@(OutputNames)' != ''" Text="Found a match for %(Identity): @(OutputNames->'%(OutputFolder)')" /> 

Therefore, to update my metadata, I can do the following:

  <!-- Now override specific projects OutputTo metadata --> <OutputProjects Condition="'%(Identity)' != '' AND '@(OutputProjects)' != '' AND '@(OutputNames)' != ''"> <OutputTo>@(OutputNames->'%(OutputFolder)')</OutputTo> </OutputProjects> 
+3
source

Source: https://habr.com/ru/post/1335951/


All Articles