2011-02-02 52 views
1

我正在尝试为我们的源代码树编写构建脚本。这棵树由(大量)解决方案组成,它们之间有组件引用。我已经创建了一个包含所有解决方案的ItemGroup,并正在对此ItemGroup进行批量生成解决方案。MSBuild - 比较ItemGroups元数据

我还需要将一些项目输出复制到“exes output”文件夹中,每个文件夹位于它们自己的文件夹中。我已将一些元数据附加到指向要从中获取输出的项目的解决方案项目。因为我可能有多个项目要从每个解决方案中输出,所以我通过为元数据提供传递给ItemGroup的Include的值来单独创建ItemGroup。这工作愉快,我可以批量这个动态创建的ItemGroup。

我想要做的最后一个步骤是让我头痛的是,对于这些输出项目的一些,我想指定一个特殊的文件夹名称。现在,我可以通过改变目标内的元数据正在做的工作,这样做:

<?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)" /> 
    <!-- e.g. <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> 

然而,这种构建脚本需要由小组中的所有开发人员,不是所有的人都保持熟悉MSBuild。因此,我想在脚本的顶部定义另一个ItemGroup,它定义了任何特殊名称。然后,他们可以忽略所有的目标和任务,只是保持ItemGroups,就像这样:

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

不过,我试图让DoBuild目标来更新元数据的任何方式落在它的脸上。我想我能做到这一点:

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

但在OutputProjects项目组代码,批次和然后在OutputNames项目组,这样的条件是不正确的(的参数比较一个总是空)。

不幸的是,在这个阶段,无法更改解决方案结构或输出文件夹结构要求。 MSBuild有什么窍门可以帮助我吗?我并不反对包含一个自定义任务来完成这项工作,但如果可能的话,我会选择直接的MSBuild解决方案。

如果它有所作为,我正在使用MSBuild v4。

回答

3

啊。偶尔玩回答。

首先,我正在调查this post关于获取两个项目组的交集。因此,我改变了我的OutputNames项目组具有相同的标识OutputProjects的ItemGroup:

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

这让我批上%(身份),并得到这样的交集:

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

然而,当还参照在相同任务的OutputFolder元数据,即成为配料的一部分以及导致以下从未打印到输出:

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

但是,通过使用TR ansform在属性,而不是直接访问,它不被视为配料的一部分:

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

因此,我可以做以下更新我的元数据:

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