【问题标题】:Using a property to conditionally compile a source code file using MSBuild使用属性使用 MSBuild 有条件地编译源代码文件
【发布时间】:2014-12-02 23:59:40
【问题描述】:

我正在尝试设置我正在 Visual Studio 中处理的应用程序,以便在项目文件中包含一组标准的程序集级属性,方法是将几个脚本导入项目文件,因为它们是由 MSBuild 编译的。有问题的属性由从其中一个脚本调用的 MSBuild 任务生成,而另一个脚本包含生成的代码文件作为编译项。

到目前为止,我的自定义任务是生成包含文件 OK 并将其创建的文件的路径传回 MSBuild,但由于某种原因,MSBuild 顽固地拒绝将生成的文件包含在项目的构建中。这可以通过验证编译的 .dll 文件在其版本信息资源中没有来自生成的程序集属性的任何值来确认。

我的自定义任务脚本(Build.targets):

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask AssemblyFile="..\Build.Tasks\$(OutDir)Build.Tasks.dll" TaskName="GenerateAssemblyInfo" />
    <Target Name="GenerateAssemblyAttributes" Condition="'$(SkipGenerateAssemblyAttributes)' == 'false'">
        <GenerateAssemblyInfo AssemblyName="$(AssemblyName)" CompilationSymbols="$(DefineConstants)" IncludeFileFolder="$(AssemblyInfoFileFolder)">
            <Output TaskParameter="IncludeFilePath" PropertyName="AssemblyInfoFilePath"/>
        </GenerateAssemblyInfo>
        <Message Text="Generated AssemblyInfo include file $(AssemblyInfoFilePath)."/>
    </Target>
</Project>

条件编译脚本(Build.props):

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup Condition="Exists('$(AssemblyInfoFilePath)')">
        <Compile Include="$(AssemblyInfoFilePath)"/>
    </ItemGroup>
</Project>

我为测试/演示问题而创建的示例项目:

<Project DefaultTargets="GenerateAssemblyAttributes;Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <AssemblyInfoFileFolder>Properties</AssemblyInfoFileFolder>
    <SkipGenerateAssemblyAttributes>false</SkipGenerateAssemblyAttributes>
  </PropertyGroup>
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    ...
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>BuildTest</RootNamespace>
    <AssemblyName>BuildTest</AssemblyName>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    ...
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    ...
  </PropertyGroup>
  <Import Project="..\Build.Tasks\Build.targets" />
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <Import Project="..\Build.Tasks\Build.props" />
  <ItemGroup>
    <Compile Include="Class1.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

如果在 Build.props 脚本中,我将 $(AssemblyInfoFilePath) 属性替换为生成文件的文字路径(例如 Properties\_AssemblyInfo.cs),或者我将 Build.props 脚本的导入替换为 Compile 项直接引用生成的文件(例如&lt;Compile Include="Properties\_AssemblyInfo.cs" Condition="Exists('Properties\_AssemblyInfo.cs')" /&gt;),然后该文件会像我期望的那样包含在构建中。只要我将$(AssemblyInfoFilePath) 属性放回&lt;Compile&gt; 项,MSBuild 就会再次开始忽略该文件,无论是否存在“存在”条件。

我已经证明$(AssemblyInfoFilePath) 属性是从自定义任务的输出中创建和设置的,两者都来自调用任务的目标(诊断构建输出摘录如下):

Target "GenerateAssemblyAttributes" in file "... Build.Tasks\Build.targets":
  Using "GenerateAssemblyInfo" task from assembly "... Build.Tasks\..\Build.Tasks\bin\Debug\Build.Tasks.dll".
  Task "GenerateAssemblyInfo"
    Build 2.0.5394.41529
  Done executing task "GenerateAssemblyInfo".
  Task "Message"
    Generated AssemblyInfo include file Properties\_AssemblyInfo.cs.
  Done executing task "Message".
Done building target "GenerateAssemblyAttributes" in project "BuildTest.csproj".
Target "BeforeBuild" in file "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets":
...

并且还通过将另一个目标添加到应用程序项目文件,依赖于 GenerateAssemblyAttributes 目标,该目标也使用&lt;Message&gt; 元素来输出属性的值。

我只是不明白为什么,如果包含生成的源代码文件路径的属性是从自定义任务的输出中正确设置的,那么 MSBuild 的行为就像它甚至不存在一样?使用 Visual Studio 2005 和 MSBuild 2.0。

编辑:所以,如果我像这样修改我的 Build.targets 文件:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask AssemblyFile="..\Build.Tasks\$(OutDir)Build.Tasks.dll" TaskName="GenerateAssemblyInfo" />
    <Target Name="GenerateAssemblyAttributes" Condition="'$(SkipGenerateAssemblyAttributes)' == 'false'">
        <GenerateAssemblyInfo AssemblyName="$(AssemblyName)" CompilationSymbols="$(DefineConstants)" IncludeFileFolder="$(AssemblyInfoFileFolder)">
            <Output TaskParameter="IncludeFilePath" PropertyName="AssemblyInfoFilePath"/>
        </GenerateAssemblyInfo>
        <Message Text="Generated AssemblyInfo include file $(AssemblyInfoFilePath)."/>
    <CreateItem Include="$(AssemblyInfoFilePath)" Condition ="Exists('$(AssemblyInfoFilePath)')">
      <Output TaskParameter="Include" ItemName="Compile"/>
    </CreateItem>
    </Target>
</Project>

并从应用程序项目文件中删除 Build.props 文件,然后构建最终“正确”,因为自定义任务生成的源代码文件包含在构建中。不幸的是,MSBuild 随后继续为正在构建的项目中包含(引用)的每个应用程序项目中的第一个源代码文件产生以下错误:

错误 147 在“Sources”参数中多次指定了“source_code_file.cs”项。 “来源”参数不支持重复项目。 C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.CSharp.targets

如果执行自定义任务并为生成的源代码文件创建(添加)“编译”项的目标仅指该源代码文件的路径,那么无关的源代码文件如何在像 MSBuild 声称的那样发送到编译器的源代码文件列表?

【问题讨论】:

    标签: visual-studio msbuild


    【解决方案1】:

    这是评估顺序。在执行任何任务之前评估&lt;Itemgroup&gt;。 相反,将项目添加为生成它们的任务的一部分,而不是在全局级别。这对旧版本的 MSBuild 来说不太好:我不知道何时添加了将 itemgroups 简单地放入任务元素的功能。

    此外,通配符的 Include 只会添加执行时存在的内容,因此您会遇到这样的问题:它不适用于干净的构建,但在第二次尝试后似乎可以正常工作,如果它正在解析路径(就像你硬编码一样)。

    【讨论】:

    • 好的,我担心可能是这种情况。我可能不得不重新考虑我的方法。感谢您指引正确的方向。
    • 我做了类似的事情,效果很好:将文件添加到生成它们的任务中的项目组中,而不是在全局级别。您是否有机会使用更新的 MSBuild?
    • 我相信我已经按照您的建议解决了我遇到的问题,并对导致构建失败的相关问题进行了一些额外的研究。
    【解决方案2】:

    我现在拥有包含生成的源代码文件的 MSBuild,其路径在我的自定义构建任务的属性输出中指定,通过将项目添加到“编译”项目组来编译我的每个应用程序程序集在调用自定义任务的目标中(如上所述)。

    编译器报告的“重复源”问题已通过在应用程序项目文件中添加另一个目标以在编译程序集之前从“编译”项组中删除重复项来解决,例如

    <?xml version="1.0" encoding="utf-8"?>
    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Target Name="RemoveSourceCodeDuplicates" >
        <RemoveDuplicates Inputs="@(Compile)">
          <Output TaskParameter="Filtered" ItemName="Compile"/>
        </RemoveDuplicates>
      </Target>
    </Project>
    

    并在应用程序项目文件中:

    <Project DefaultTargets="GenerateAssemblyAttributes;Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      ...
      <Import Project="..\Build.Tasks\RemoveDuplicates.targets"/>
      <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
      ...
    </Project>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-17
      • 1970-01-01
      相关资源
      最近更新 更多