如何使TeamCity利用MSBuild的增量构建支持?

12

我正在搭建TeamCity (从CruiseControl.NET迁移),但我无法通过MSBuild实现增量构建。

我有一个小的.proj文件,其中包含一个基本的构建脚本,用于从TeamCity中传入一些参数来调用我的解决方案的构建。当我手动调用脚本时,MSBuild的增量构建功能会启动,并在后续运行中完全跳过构建。

当通过Team City调用此脚本时,构建日志每次都显示干净的编译输出。我观察了构建期间的工作目录,发现上一次构建的输出并没有消失。

我还通过远程登录到服务器并从命令提示符运行MSBuild来手动调用该目录中的构建脚本。以这种方式运行它会触发预期的增量构建,第一次调用后就能生效。

即使从面板开始构建,也会进行完整的重建。

我无法确定原因,但某些东西似乎给了MSBuild的印象,认为它正在接收新的更改,并导致每次运行时都执行重建。我在TeamCity文档中找不到什么能解释这个问题的东西 - 我的期望是,如果在源代码控制系统中没有更改,它不会更新工作文件夹。

TeamCity是否传递一些参数到构建过程中触发了重建?我能查看这些参数吗?


经过详细检查MSBuild日志 (/v:d命令行开关),发现每次构建会导致 .NETFramework,Version=v4.0.AssemblyAttributes.cs 文件在 <Agent>\temp\buildTmp 目录中被更新而导致完整重建。

这个文件通常可以在%TMP%\.NETFramework,Version=v4.0.AssemblyAttributes.cs找到;TeamCity正在更改本地临时目录环境变量以引用代理的临时文件夹。不幸的是,当缺少此文件时,生成过程中的Microsoft.Common.targets部分会创建它。在每次构建之前删除"temp"文件会导致它在每次构建时都被创建,并且会在每个项目文件的构建中动态引用。

我需要找到一种方法来防止在每次构建时重新创建此文件。

3个回答

11

对于这个问题的一种解决方法是自定义MSBuild进程,以设置“目标框架模式程序集属性”文件(在问题中提到的文件的正确名称)将被创建的路径。

TargetFrameworkMonikerAssemblyAttributesPath属性在Microsoft.Common.targets中定义,确定文件应该被创建的位置。通过覆盖此属性,可以更改位置以使用不同的位置。

以下是可用于实现合适替代方案的脚本:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
    <PrepareForBuildDependsOn>
        $(PrepareForBuildDependsOn);
        _SetTargetFrameworkMonikerAssemblyAttributesPath
    </PrepareForBuildDependsOn>
</PropertyGroup>

<Target 
    Name="_SetTargetFrameworkMonikerAssemblyAttributesPath"
    Condition="'$(TEAMCITY_VERSION)' != ''">

    <PropertyGroup>
        <TargetFrameworkMonikerAssemblyAttributesDir
            Condition="'$(TargetFrameworkMonikerAssemblyAttributesDir)' == ''">
            $([MSBuild]::GetRegistryValue("HKEY_CURRENT_USER\Environment", "TMP"))
        </TargetFrameworkMonikerAssemblyAttributesDir>
        <TargetFrameworkMonikerAssemblyAttributesDir
            Condition="'$(TargetFrameworkMonikerAssemblyAttributesDir)' == ''">
            $([MSBuild]::GetRegistryValue("HKEY_CURRENT_USER\Environment", "TEMP"))
        </TargetFrameworkMonikerAssemblyAttributesDir>
        <TargetFrameworkMonikerAssemblyAttributesDir 
            Condition="'$(TargetFrameworkMonikerAssemblyAttributesDir)' == ''">
            $(USERPROFILE)
        </TargetFrameworkMonikerAssemblyAttributesDir>
        <TargetFrameworkMonikerAssemblyAttributesDir
            Condition="'$(TargetFrameworkMonikerAssemblyAttributesDir)' == ''">
            $([System.IO.Path]::Combine('$(WINDIR)', 'Temp'))
        </TargetFrameworkMonikerAssemblyAttributesDir>
        <TargetFrameworkMonikerAssemblyAttributesPath>
            $([System.IO.Path]::Combine('$(TargetFrameworkMonikerAssemblyAttributesDir)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))
        </TargetFrameworkMonikerAssemblyAttributesPath>
    </PropertyGroup>

    <Message Text="Target Framework Moniker Assembly Attributes path is &quot;$(TargetFrameworkMonikerAssemblyAttributesPath)&quot;" Importance="low" />

</Target>

只有在指定了TEAMCITY_VERSION属性时,目标才会被执行,这应该是当TeamCity代理执行构建时。

注意:PropertyGroup的子元素应该放在单独的一行上。它们已经分散在多行上以增加可读性,但额外的换行符会导致脚本执行失败。

当目标运行时,它会根据用户在注册表中定义的环境变量构建一个合适的路径,首先查找TMPTEMP,然后回退到用户的个人文件夹,最后到 C:\Windows\Temp 目录。 这与 System.Path.GetTempPath() 文档记录的顺序相匹配,并应该产生与在TeamCity之外执行MSBuild相匹配的行为。

这应该保存为 .targets 文件并在使用 <Import> 元素将其导入到由 TeamCity 服务器构建的项目的 .csproj 文件中。我将脚本添加到了我的 MSBuild 扩展目录 (C:\Program Files\MSBuild\) 下,并通过添加以下导入元素引用它:

<Import Project="$(MSBuildExtensionsPath)\TeamCity\TeamCity.Incremental.targets" />

Import元素的位置/顺序并不重要,但是我建议将它放在<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> 之后,因为这个元素应该出现在每个 .csproj 文件中。


使用此脚本时,我遇到了以下错误:C:\Program Files (x86)\MSBuild\TeamCity\TeamCity.Incremental.targets(27, 9): error MSB4184: 表达式 "[System.IO.Path]::Combine( C:\Windows\system32\config\systemprofile\AppData\Local\Temp , .NETFramework,Version=v4.0.AssemblyAttributes.cs)" 无法评估。路径中有非法字符。 - Petrus Theron
你有没有注意到代码示例下方的注释?那很可能是导致问题的原因。 - Paul Turner
我认为我已经遵循了注释,但可能只是自己在瞎想。您可以发布或链接到您的最终版本吗? - Petrus Theron
2
目前的版本可在以下链接中找到:https://github.com/PyPup/Banjo/blob/master/Build/TeamCity.Incremental.targets - Paul Turner
我希望我能再次点赞。我不记得我是如何解决这个问题的,所以搜索并找到了这个答案,我在过去已经点过赞了。 - CoderDennis

2

调整TargetFrameworkMonikerAssemblyAttributesPath可以解决这个问题,正如Paul Turner所提到的那样。与其在Microsoft的构建系统脚本中进行斗争,我在TeamCity项目参数中添加了一个环境变量来设置TargetFrameworkMonikerAssemblyAttributesPath。

在TeamCity的项目设置中,我将env.TargetFrameworkMonikerAssemblyAttributesDir设置为%env.windir%\Temp


1
这个问题在TeamCity 2017.3中仍然存在。我想找到一个更容易跟踪的解决方法,而不是接受答案中详细说明的方法,所以我做了以下步骤:
1. 我将我的.NETFramework,Version=v4.7.AssemblyAttributes.cs文件的副本检入我的VCS。 2. 我为正在使用MSBuild的构建配置添加了一个新的构建步骤,并设置了以下属性:
- Runner Type: Command Line - Step Name: CopyAssemblyAttributesFile - Run: Custom script - Custom Script: copy "%system.teamcity.build.workingDir%\\." "%env.TEMP%\."
这将复制我的AssemblyAttributes文件版本,并具有来自初始VCS检出的时间戳。
随后,由于时间戳保持一致,因此MSBuild似乎认为它是相同的文件,并且现在可以正确执行增量构建,可以从代理的构建日志中验证。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接