I wanted to IlMerge an assembly in a TeamCity project. TeamCity has a Powershell build step that you can use to run your own arbitrary scripts. Here’s how I did it
TeamCity Configuration
Powershell Script
<#
.SYNOPSIS
IlMerges an assembly with its dependencies. Depends on nuget being installed in the PATH.
.PARAMETER targetProject
The name of the project to be ilmerged
.PARAMETER outputAssembly
The name of the ilmerged assembly when it is created
.PARAMETER buildConfiguration
The build configuration used to create the assembly. Used to locate the assembly under the project. The usual format is Project/bin/Debug
.PARAMETER targetPlatform
Defaults to .NET 4
.PARAMETER mvc3
If the project is an Mvc3 project, the MVC3 assemblies need to be added to the ilmerge list. The script assumes that the MVC3 assemblies are installed in the default location.
.PARAMETER internalize
Adds the /internalize flag to the merged assembly to prevent namespace conflicts.
.EXAMPLE
ilmerge.ps1 -targetProject "MyMVC3Project" -mvc3
#>
param(
[parameter(Mandatory=$true)] $targetProject,
$outputAssembly = "$targetProject.dll",
$buildConfiguration = "",
$targetPlatform = "v4,c:\windows\Microsoft.NET\Framework\v4.0.30319",
[switch] $mvc3,
[switch] $internalize
)
function Get-ScriptDirectory
{
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}
function Get-Mvc3Dependencies()
{
$system_web_mvc = """c:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 3\Assemblies\System.Web.Mvc.dll"""
$system_web_webpages = """c:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\System.Web.WebPages.dll"""
$system_web_razor = """c:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\System.Web.Razor.dll"""
$system_web_webpages_razor = """c:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\System.Web.WebPages.Razor.dll"""
$mvc3Assemblies = "$system_web_mvc $system_web_webpages $system_web_razor $system_web_webpages_razor"
return $mvc3Assemblies
}
function Get-InputAssemblyNames($buildDirectory)
{
$assemblyNames = Get-ChildItem -Path $buildDirectory -Filter *.dll | ForEach-Object { """" + $_.FullName + """" }
write-host "Assemblies to merge: $assemblyNames"
$inArgument = [System.String]::Join(" ", $assemblyNames)
return $inArgument
}
function Get-BuildDirectory($solutionDirectoryFullName)
{
$targetProjectDirectory = "$solutionDirectoryFullName\$targetProject"
$result = "$targetProjectDirectory\bin"
if ($buildConfiguration -ne "")
{
$result = Join-Path $result $buildConfiguration
}
return $result
}
try
{
$scriptPath= Get-ScriptDirectory
$scriptDirectory = new-object System.IO.DirectoryInfo $scriptPath
$solutionDirectory = $scriptDirectory.Parent
$solutionDirectoryFullName = $solutionDirectory.FullName
$ilMergeAssembly = "$solutionDirectoryFullName\.ilmerge\IlMerge\IlMerge.exe"
$publishDirectory = "$solutionDirectoryFullName\Publish"
$outputAssemblyFullPath = "$publishDirectory\$outputAssembly"
$buildDirectory = Get-BuildDirectory $solutionDirectoryFullName
"Script Directory : $scriptPath"
"Solution Directory: $solutionDirectoryFullName"
"Build Directory : $buildDirectory"
"Publish Directory : $publishDirectory"
$outArgument = "/out:$publishDirectory/$outputAssembly"
$inArgument = Get-InputAssemblyNames $buildDirectory
# MVC3 assemblies are not a part of the .NET Framework, but neither are they a nuget package.
# They have to be referenced directly.
if ($mvc3 -eq $true)
{
$mvcAssemblies = Get-Mvc3Dependencies
$inArgument = "$inArgument $mvcAssemblies"
}
$cmd = "$ilMergeAssembly /t:library /targetPlatform:""$targetPlatform"" $outArgument $inArgument"
if ($internalize)
{
$cmd = $cmd + " /internalize"
}
"Installing ilmerge"
nuget install IlMerge -outputDirectory .ilmerge -ExcludeVersion
"Ensuring that publication directory exists"
if ([System.IO.Directory]::Exists($publishDirectory) -eq $false)
{
[System.IO.Directory]::CreateDirectory($publishDirectory)
}
"Running Command: $cmd"
$result = Invoke-Expression $cmd
"Getting assembly info for $outputAssemblyFullPath"
$outputAssemblyInfo = New-Object System.IO.FileInfo $outputAssemblyFullPath
if ($outputAssemblyInfo.Length -eq 0)
{
$outputAssemblyInfo.Delete
}
$outputAssemblyInfo
if ($outputAssemblyInfo.Exists -eq $false)
{
throw "Output assembly not created by ilmerge script."
Exit -1
}
elseif ($outputAssemblyInfo.Length -eq 0)
{
$outputAssemblyInfo.Delete();
Exit -1;
}
else
{
"Output assembly created successfully at $outputAssemblyFullPath."
Exit 0;
}
}
catch
{
throw
Exit -1
}
I had a situation in which one of my configuration parameters in a TeamCity build did not have a name or a value. Because it was supposedly used by one of my build steps I couldn’t delete it. Because it didn’t have a name or value I couldn’t run the build. I tried editing the value, but the name is uneditable.
The answer is that I had a double-% character on one of my build steps. TeamCity uses the % character as a flag to indicate a configuration parameter. I had %PARAMETER%%. in one of the build step fields. All I had to do is change it to %PARAMETER% and everything was fine.