81

I want to merge one .NET DLL assembly and one C# Class Library project referenced by a VB.NET Console Application project into one command-line console executable.

I can do this with ILMerge from the command-line, but I want to integrate this merging of reference assemblies and projects into the Visual Studio project. From my reading, I understand that I can do this through a MSBuild Task or a Target and just add it to a C#/VB.NET Project file, but I can find no specific example since MSBuild is large topic. Moreover, I find some references that add the ILMerge command to the Post-build event.

  1. How do I integrate ILMerge into a Visual Studio (C#/VB.NET) project, which are just MSBuild projects, to merge all referenced assemblies (copy-local=true) into one assembly?

  2. How does this tie into a possible ILMerge.Targets file?

  3. Is it better to use the Post-build event?

AMissico
  • 21,470
  • 7
  • 78
  • 106
  • You can also use "Post Build String" to do that as mentioned [Here][1] [1]: http://stackoverflow.com/questions/2961357/using-ilmerge-with-net-4-libraries/5408079#5408079 – Zain Ali Jan 18 '14 at 11:58

9 Answers9

71

The "MSBuild ILMerge task" (or MSBuild.ILMerge.Task) NuGet package makes this process quite simple. It defaults to merging any "copy local" references into your main assembly.

Note: Although the packages have similar names, this one is different from ILMerge.MSBuild.Tasks that Davide Icardi mentioned in his answer. The one I'm suggesting here was first published in August 2014.

Community
  • 1
  • 1
Holistic Developer
  • 2,458
  • 1
  • 18
  • 27
  • 4
    A solution as it should be! +1 – Ray Oct 31 '14 at 19:53
  • 1
    This should be the accepted solution. Makes it really painless. Thank you! – Alek Davis Oct 31 '14 at 22:20
  • 1
    Nice one :D Not perfect since it "forgot" to include extra files in the output directory but still good ^^ – Ethenyl Jan 09 '15 at 09:48
  • 1
    Works very nice: include package, build and you are done! It is the on with an icon. – Remco Mar 13 '15 at 23:29
  • 2
    I can't find any documentation for MSBuild ILMerge, how do I use it? Does anyone have an example or instructions? – MrDysprosium Oct 13 '16 at 15:42
  • @MrDysprosium See `ILMerge.doc` that is included with the ILMerge NuGet package. Since "MSBuild ILMerge task" has a dependency on ILMerge, after adding it I also have the ILMerge package. For example, I can find the doc at `\packages\ILMerge.2.14.1208\tools\ILMerge.doc`. – Holistic Developer Oct 14 '16 at 18:54
  • This is way easier than I had ever imagined! Thanks :) – Metoniem Aug 06 '19 at 13:32
  • This is nice! **No** editing `*.csproj` files with this solution. – Jim Aug 06 '19 at 18:15
  • Has this to be configured somewhere? I installed it by packages.config but it still doesn't work without the SSH dll I need. – Standard Aug 06 '20 at 09:51
  • @wernersbacher not sure what your other DLL is, but ILMerge will only handle managed code. If you have a native code dependency, you'd still have to ship that separately. – Holistic Developer Aug 07 '20 at 04:09
  • How do you exclude one of the PackageReferences from the ILMerge? Seems to have issues with Log4net, but I can't find how to not package that one up? – devonuto Jun 28 '21 at 08:52
  • Are there any instructions on how to use this? – indigo Sep 21 '22 at 19:17
  • @indigo: 1. Add a reference to the package. 2. For any assembly you want merged into your main assembly, go to the reference properties and ensure that Copy Local is set to true. – Holistic Developer Sep 23 '22 at 20:34
22

Here an alternative solution:

1) Install ILMerge.MSBuild.Tasks package from nuget

PM> Install-Package ILMerge.MSBuild.Tasks

2) Edit the *.csproj file of the project that you want to merge by adding the code below:

  <!-- Code to merge the assemblies into one:setup.exe -->
  <UsingTask TaskName="ILMerge.MSBuild.Tasks.ILMerge" AssemblyFile="$(SolutionDir)\packages\ILMerge.MSBuild.Tasks.1.0.0.3\tools\ILMerge.MSBuild.Tasks.dll" />
  <Target Name="AfterBuild">
    <ItemGroup>
      <MergeAsm Include="$(OutputPath)$(TargetFileName)" />
      <MergeAsm Include="$(OutputPath)LIB1_To_MERGE.dll" />
      <MergeAsm Include="$(OutputPath)LIB2_To_MERGE.dll" />
    </ItemGroup>
    <PropertyGroup>
      <MergedAssembly>$(ProjectDir)$(OutDir)MERGED_ASSEMBLY_NAME.exe</MergedAssembly>
    </PropertyGroup>
    <Message Text="ILMerge @(MergeAsm) -&gt; $(MergedAssembly)" Importance="high" />
    <ILMerge InputAssemblies="@(MergeAsm)" OutputFile="$(MergedAssembly)" TargetKind="SameAsPrimaryAssembly" />
  </Target>

3) Build your project as usual.

Davide Icardi
  • 11,919
  • 8
  • 56
  • 77
  • It's a really pit this package isn't better maintained. I'd been using it but it's missing the ability to set TargetPlatform which is significant for .NET 4.5/.NET 4.0 compat. – Eoin Campbell Mar 05 '13 at 16:38
  • @davidlcardi how to i replace the targetfilename, dll_to_merge etc – Smith Mar 23 '13 at 04:31
  • @Smith TargetFileName is already a msbuild variable so you don't need to replace it. The LIB1_To_Merge.dll are fixed names. Probably with some more complex msbuild scripts you can find all referenced assemblies, but I don't know how to do it. – Davide Icardi Mar 23 '13 at 10:13
  • @DavideIcardi pls hel me see if you can respond to this http://stackoverflow.com/questions/15588086/integrating-ilmerge-into-visual-studio-build-process-causes-error – Smith Mar 23 '13 at 14:49
  • I prefer this method a lot more than the most voted one. :) – Javid May 11 '16 at 21:17
  • 1
    Is there a way to also make this sign my assembly using my key file somehow? – Steven Lemmens Apr 23 '20 at 11:19
18

Some more information that might be useful to some people implementing Scott Hanselman's solution.

When I first set this up it would complain about not being able to resolve references to System.Core, etc. It is something to do with .NET 4 support. Including a /lib argument pointing to the .NET 4 Framework directory fixes it (in fact just include the $(MSBuildBinPath)).

/lib:$(MSBuildBinPath)

I then found that IlMerge would hang while merging. It was using a bit of CPU and a lot of RAM but wasn't outputting anything. I found the fix on stackoverflow of course.

/targetplatform:v4

I also found that some of the MSBuild properties used in Scott's blog article relied on executing MsBuild from the project's directory, so I tweaked them a bit.

I then moved the targets & ilmerge.exe to the tools folder of our source tree which required another small tweak to the paths...

I finally ended up with the following Exec element to replace the one in Scott's original article:

<Exec Command="&quot;$(MSBuildThisFileDirectory)Ilmerge.exe&quot; /lib:$(MSBuildBinPath) /targetplatform:v4 /out:@(MainAssembly) &quot;$(MSBuildProjectDirectory)\@(IntermediateAssembly)&quot; @(IlmergeAssemblies->'&quot;%(FullPath)&quot;', ' ')" /> 

UPDATE I also found Logic Labs answer about keeping the CopyLocal behaviour and just excluding ilMerged assemblies from CopyLocal essential if you are using Nuget packages. Otherwise you need to specify a /lib argument for each package directory of referenced assemblies that aren't being merged.

Community
  • 1
  • 1
Jason Duffett
  • 3,428
  • 2
  • 23
  • 23
  • Acturally I ended up with downloading the ILMerge.exe, adding it to source control and writing some command into Post-build Event – Haobo Mar 30 '16 at 03:28
16

The article Mixing Languages in a Single Assembly in Visual Studio seamlessly with ILMerge and MSBuild at http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx demonstrates how to use ILMerge and MSBuild within a Visual Studio Project.

AMissico
  • 21,470
  • 7
  • 78
  • 106
  • 1
    There is an issue with this code, if you don't want to ILMerge all your references. See [my answer](http://stackoverflow.com/a/9608238/182363) – Logic Labs Mar 07 '12 at 20:05
9

One issue I found with the article at: http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx.

If you have any references that you do not wish to ILMerge then the code in the article fails because it overrides the default CopyLocal behaviour to do nothing.

To fix this - Instead of:

<Target Name="_CopyFilesMarkedCopyLocal"/> 

Add this entry to the targets file instead (.NET 3.5 only) (to filter out the non-ilmerge copylocal files, and treat them as normal)

<Target Name="AfterResolveReferences">
    <Message Text="Filtering out ilmerge assemblies from ReferenceCopyLocalPaths" Importance="High" />
    <ItemGroup>
        <ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.IlMerge)'=='true'" />
    </ItemGroup>
</Target>
Logic Labs
  • 404
  • 3
  • 9
  • 1
    Oh yes! This is DEFINITELY needed if you are using Nuget, otherwise you'd have to specify the package contents folder of each referenced assembly that you are not merging. Thank you! – Jason Duffett Jun 14 '12 at 09:16
4

This is a great article that will show you how to merge your referenced assemblies into the output assembly. It shows exactly how to merge assemblies using msbuild.

takrl
  • 6,356
  • 3
  • 60
  • 69
Rohan West
  • 9,262
  • 3
  • 37
  • 64
  • 1
    Easy to overlook his updated and more detailed blog entry, so I will reference it here http://www.clariusconsulting.net/blogs/kzu/archive/2009/02/23/LeveragingILMergetosimplifydeploymentandyourusersexperience.aspx. – AMissico Mar 31 '10 at 21:10
  • The article i referenced is more recent but less detailed, it has some interesting comments. – Rohan West Mar 31 '10 at 21:45
  • @WernerCD Edited the answer to provide a fixed link via internet archive. And [here's the second one](https://web.archive.org/web/20110119025018/http://www.clariusconsulting.net/blogs/kzu/archive/2009/02/23/LeveragingILMergetosimplifydeploymentandyourusersexperience.aspx). – takrl Feb 22 '17 at 12:31
1

My 2 cents - I picked up @Jason's response and made it work for my solution where I wanted to generate the *.exe in the bin/Debug folder with all *.dlls inside the same folder.

<Exec Command="&quot;$(SolutionDir)packages\ILMerge.2.13.0307\Ilmerge.exe&quot; /wildcards /out:&quot;$(SolutionDir)..\$(TargetFileName)&quot; &quot;$(TargetPath)&quot; $(OutDir)*.dll" /> 

Note: This solution is obviously hardcoded into the ILMerge nuget package version. Please let me know if you have some suggestions to improve.

rui
  • 11,015
  • 7
  • 46
  • 64
1

Edit the *.csproj file of the project that you want to merge by adding the code below:

<Target Name="AfterBuild" Condition=" '$(ConfigurationName)' == 'Release' " BeforeTargets="PostBuildEvent">
  <CreateItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)'=='.dll'">
    <Output ItemName="AssembliesToMerge" TaskParameter="Include" />
  </CreateItem>
  <Exec Command="&quot;$(SolutionDir)packages\ILMerge.3.0.29\tools\net452\ILMerge.exe&quot; /internalize:&quot;$(MSBuildProjectPath)ilmerge.exclude&quot; /ndebug  /out:@(MainAssembly)  &quot;@(IntermediateAssembly)&quot; @(AssembliesToMerge->'&quot;%(FullPath)&quot;', ' ')" />
  <Delete Files="@(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')" />
</Target>

Notes:

  1. Replace $(SolutionDir)packages\ILMerge.3.0.29\tools\net452\ILMerge.exe with whatever path you have the ILMerge.exe in.
  2. You can remove the Condition in the target to also merge on Debug but then the Debugger might not work
  3. If you are not excluding anything you can remove: /internalize:&quot;$(MSBuildProjectPath)ilmerge.exclude&quot;
N T
  • 438
  • 6
  • 11
0

Check out this article by Jomo. He has a quick process to hack ILMerge into the msbuild system

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    The article Mixing Languages in a Single Assembly in Visual Studio seamlessly with ILMerge and MSBuild at http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx is based on his blog and enhances the technique to allow you to selectively merge by setting ILMerge=True/False in the project file. The article is much more detailed then Jomo's blog entry. – AMissico Mar 31 '10 at 21:03