我正在设置TeamCity(从CruiseControl.NET迁移),我正在努力让它通过MSBuild执行增量构建。
我有一个小的.proj文件,其中包含一个基本的构建脚本,用于调用我的解决方案的构建,其中一些参数来自TeamCity。当我手动调用脚本时,MSBuild的Incremental Build功能会启动并完全在后续运行时跳过构建。
通过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”文件会导致在每次构建时创建它,并在每个项目文件的构建中动态引用。
我需要找到一种方法来阻止在每次构建时重新创建此文件。
答案 0 :(得分:11)
此问题的解决方法是自定义MSBuild进程以设置“Target Framework Moniker Assembly Attributes”文件(问题中提到的文件的正确名称)的路径。
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 "$(TargetFrameworkMonikerAssemblyAttributesPath)"" Importance="low" />
</Target>
目标仅在TEAMCITY_VERSION
被指定为属性时执行,该属性应该在TeamCity代理执行构建时执行。
注意: PropertyGroup
的子元素应分别在一行中。它们已分布在多行中以提高可读性,但额外的换行会导致脚本失败。
当目标运行时,它会尝试根据注册表中定义的用户环境变量构建合适的路径,首先查找TMP
和TEMP
,然后再回到用户的配置文件文件夹最后是C:\Windows\Temp
目录。这与System.Path.GetTempPath()记录的顺序相匹配,并且应该导致在TeamCity之外匹配MSBuild执行的行为。
这应该保存为系统中的某个.targets文件,并使用<Import>
元素导入到TeamCity服务器正在构建的项目的.csproj文件中。我在MSBuild扩展目录(C:\Program Files\MSBuild\
)下添加了脚本,并通过添加以下import元素引用它:
<Import Project="$(MSBuildExtensionsPath)\TeamCity\TeamCity.Incremental.targets" />
导入元素的位置/顺序无关紧要,但我建议在每个.csproj文件中出现的<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
之后加上它。
答案 1 :(得分:1)
正如Paul Turner所提到的,调整TargetFrameworkMonikerAssemblyAttributesPath解决了这个问题。我没有在Microsoft的构建系统脚本中与High Magick作战,而是添加了一个环境变量来在TeamCity项目参数中设置TargetFrameworkMonikerAssemblyAttributesPath。
在TeamCity的项目设置中,我将 env.TargetFrameworkMonikerAssemblyAttributesDir 设置为%env.windir%\ Temp 。
答案 2 :(得分:1)
TeamCity 2017.3中仍然存在此问题。
我希望找到一个更容易跟踪解决方案,而不是接受答案详细说明,所以我做了以下内容:
.NETFramework,Version=v4.7.AssemblyAttributes.cs
文件的副本签入我的VCS copy "%system.teamcity.build.workingDir%\<path_to_AssemblyAttributes.cs Dir>\." "%env.TEMP%\."
这将首次使用初始VCS结帐时的时间戳复制我的AssemblyAttributes文件版本,
随后MSBuild似乎认为它是同一个文件,因为时间戳将保持一致,现在可以正确执行增量构建,可以从代理上的构建日志验证。