peteris.rocks

Merging .NET Assemblies with MSBuild

Last updated on

Suppose you have an executable Program.exe that depends on Library.dll. For Program.exe to work, Library.dll must always be present in the same directory as Program.exe or must be installed in the Global Assembly Cache (GAC). Sometimes it's more convenient to have just one file with all dependencies embedded. This can be achieved by merging all necessary .NET assemblies into one.

Introduction

ILMerge

ILMerge is a tool developed by Microsoft.

ILMerge is a utility that can be used to merge multiple .NET assemblies into a single assembly. ILMerge takes a set of input assemblies and merges them into one target assembly.

It's available as a NuGet package https://www.nuget.org/packages/ilmerge

ILRepack

ILRepack is an open-source alternative to ILMerge. It also works on mono.

ILRepack is meant at replacing ILMerge / Mono.Merge. The former being closed-source, impossible to customize, slow, resource consuming and many more. The later being deprecated, unsupported, and based on an old version of Mono.Cecil.

It's available as a NuGet package https://www.nuget.org/packages/ILRepack/

IL or Intermediate Language

IL stands for Intermediate Language which is short for Common Intermediate Language (CIL) which is formerly called Microsoft Intermediate Language or MSIL. C#/F#/VisualBasic.NET code is compiled down to bytecode (= MSIL) which is executed by the .NET runtime when a program is launched.

MSBuild

MSBuild is a build tool that is used to build C#/.NET projects. It's used by default in Visual Studio. *.csproj are MSBuild project files.

Usage

We are going to use ILMerge on Windows and ILRepack on mono. It's also possible to just use ILRepack on both Windows and mono.

Open your project (e.g. Project.csproj) in an XML editor and add the following lines at the end of the file in <Project>:

<Target Name="AfterBuild">
  <ItemGroup>
    <MergeAssemblies Include="$(OutputPath)\Program.exe" />
    <MergeAssemblies Include="$(OutputPath)\Library.dll" />
  </ItemGroup>
  <PropertyGroup>
    <OutputAssembly>$(OutputPath)\Program.Standalone.exe</OutputAssembly>
    <Merger Condition="('$(OS)' == 'Windows_NT')">&quot;$(SolutionDir)\packages\ilmerge.2.13.0307\ILMerge.exe&quot;</Merger>
    <Merger Condition="('$(OS)' != 'Windows_NT')">mono --runtime=v4.0.30319 &quot;$(SolutionDir)\packages\ILRepack.1.25.0\tools\ILRepack.exe&quot;</Merger>
  </PropertyGroup>
  <Message Text="MERGING: @(MergeAssemblies->'%(Filename)') into $(OutputAssembly)" Importance="High" />
  <Exec Command="$(Merger) /out:&quot;$(OutputAssembly)&quot; @(MergeAssemblies->'&quot;%(FullPath)&quot;', ' ')" />
</Target>

After every successful build, the assemblies defined in MergeAssemblies will be merged into OutputAssembly. We're doing it by executing either ILMerge.exe on Windows or ILRepack.exe on mono.

It's also possible to merge assemblies only for Release build only:

<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
  ...
</Target>

You will need to have ILMerge and ILRepack packages installed. You can add them in Visual Studio using NuGet Package Manager or by defining your dependencies in the .nuget/packages.config file in your solution root directory:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="ILMerge" version="2.13.0307" />
  <package id="ILRepack" version="1.25.0" />
</packages>