【发布时间】: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 项直接引用生成的文件(例如<Compile Include="Properties\_AssemblyInfo.cs" Condition="Exists('Properties\_AssemblyInfo.cs')" />),然后该文件会像我期望的那样包含在构建中。只要我将$(AssemblyInfoFilePath) 属性放回<Compile> 项,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 目标,该目标也使用<Message> 元素来输出属性的值。
我只是不明白为什么,如果包含生成的源代码文件路径的属性是从自定义任务的输出中正确设置的,那么 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