Problem: I install TeamCity as a build server for an ASP.NET MVC project. I am using Powershell with psake to run msbuild against our .csproj file and create a deployable package. From the build server, I can open powershell, run the script, and since there are no changes to the source code, msbuild does not regenerate project DLLs. BUT, when I call the same script from the TeamCity web interface, msbuild ALWAYS rebuilds and regenerates the DLL files, even if there are no changes. Not what AFAIK should do.
I narrowed this problem down to one step. To make it simple, I configured my TeamCity configurator, so it does not use any control source, it starts one step of the "powershell" assembly, which calls my powershell script.
The powershell script runs one command:
exec { &$msbuild $ProjectFile /t:Package "/p:PackageLocation=$PackageFile;OutDir=$TempPath;Configuration=$Config;SolutionDir=$BaseDir\Source\" /v:m }
When I call the script manually from the powershell command line, I see:
CoreCompile: Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files.
When I call the same script through TeamCity, I see:
[11:11:26]: CoreCompile: [11:11:26]: c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Csc.exe /noconfig ... <SNIP> [11:11:32]: CopyFilesToOutputDirectory: [11:11:32]: Copying file from "obj\Demo\Website.Web.dll" to "d:\deploy\Build\package\Demo\temp\Website.Web.dll". [11:11:32]: Website.Web -> d:\deploy\Build\package\Demo\temp\Website.Web.dll [11:11:32]: Copying file from "obj\Demo\Website.Web.pdb" to "d:\deploy\Build\package\Demo\temp\Website.Web.pdb". [11:11:32]: _CopyWebApplicationLegacy: [11:11:32]: Copying Web Application Project Files for Website.Web [11:11:32]: Copying file from "obj\Demo\Website.Web.dll" to "d:\deploy\Build\package\Demo\temp\_PublishedWebsites\Website.Web\bin\Website.Web.dll". [11:11:32]: Copying file from "obj\Demo\Website.Web.pdb" to "d:\deploy\Build\package\Demo\temp\_PublishedWebsites\Website.Web\bin\Website.Web.pdb". [11:11:32]: Copying file from "d:\deploy\Build\package\Demo\temp\Website.Data.dll" to "d:\deploy\Build\package\Demo\temp\_PublishedWebsites\Website.Web\bin\Website.Data.dll". [11:11:32]: Copying file from "d:\deploy\Build\package\Demo\temp\Website.Data.pdb" to "d:\deploy\Build\package\Demo\temp\_PublishedWebsites\Website.Web\bin\Website.Data.pdb".
Any ideas why running this script from TeamCity causes msbuild to detect changes and rebuild, but does the same script not work?
UPDATE: Thinking that this might be caused by some quirk with the TeamCity Powershell runner, I just tried to make a batch file that passes the script to the Powershell.exe file and called it using the Command Line runner:
C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -File D:\deploy\Build\run-build.ps1 && exit /b %ERRORLEVEL%
and I get the same behavior. If I call this batch file from the command line, msbuild skips compilation. If I call it from TeamCity, the DLLs will be recompiled.
UPDATE # 2: Eureka! I turned on diagnostic debugging in msbuild and found the reason for the forced recompilation. This is caused by the goal of the GenerateTargetFrameworkMonikerAttribute . Here are the key bits from the output log:
[15:23:28]: Target "GenerateTargetFrameworkMonikerAttribute" in file "c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets" from project "d:\deploy\source\Website.Data\Website.Data.csproj" (target "BeforeCompile" depends on it): [15:23:28]: Building target "GenerateTargetFrameworkMonikerAttribute" completely. [15:23:28]: Output file "C:\TeamCity\buildAgent\temp\buildTmp\.NETFramework,Version=v4.0.AssemblyAttributes.cs" does not exist. [15:23:28]: Using "WriteLinesToFile" task from assembly "Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". [15:23:28]: Task "WriteLinesToFile" [15:23:28]: Done executing task "WriteLinesToFile". [15:23:28]: Done building target "GenerateTargetFrameworkMonikerAttribute" in project "SMM.Data.csproj".
It appears that this target creates / updates the AssemblyAttributes file in the TEMP directory, as specified in the TEMP environment variable. Apparently, TeamCity overrides the TEMP environment variable and sets it: C: \ TeamCity \ buildAgent \ temp \ buildTmp , and this directory is cleared before each build.
I see this if I call Get-ChildItem Env: from powershell:
TEMP C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp TMP C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp
But if I call it from a powershell script as called from TeamCity:
TEMP C:\TeamCity\buildAgent\temp\buildTmp TMP C:\TeamCity\buildAgent\temp\buildTmp
The key is that after this file is regenerated:
[15:23:28]: Building target "CoreCompile" completely. [15:23:28]: Input file "C:\TeamCity\buildAgent\temp\buildTmp\.NETFramework,Version=v4.0.AssemblyAttributes.cs" is newer than output file "obj\Demo\SMM.Data.pdb".
And that's why the whole project is recompiled.
When I run the script from Powershell, the temp directory does not change or is not cleared, and the build is done as expected.
So, does anyone know how I can either change the directory created by this AssemblyAttributes file, or tell TeamCity to use a different TEMP directory? I must believe that this is a problem that others are facing.
Thanks!