11

I'm trying something exploratory:

Say I have a library "coolproject" and it has dependencies to one.dll, two.dll, and three.dll.

I would like to use ILMerge to combine coolproject with one.dll and two.dll but NOT three.dll. At the completion of the merge I should have coolproject.dll and three.dll. Wherever I intend to use coolproject.dll I must also reference three.dll

Is this even possible? Whenever I try it I get. Unresolved assembly reference not allowed: three. I'm omitting three.dll by setting "Copy Local" = false.

[EDIT]:

Executed as the following:

ILMerge /targetplatform:v2 /log /internalize /out:bin\coolproject.dll obj\Debug\coolproject.dll C:\Users\Nick\Projects\test\bin\one.dll C:\Users\Nick\Projects\test\bin\two.dll
NickSuperb
  • 1,174
  • 1
  • 8
  • 28
  • How are you calling ILMerge? The error message is right IIRC, you do need `three.dll` there (or somewhere else where ILMerge can see it), but that won't automatically mean it's merged as well. –  Jan 24 '13 at 19:40
  • Is it for a WPF project? – Bob. Jan 24 '13 at 19:41
  • @Bob this is a standard C# library. – NickSuperb Jan 24 '13 at 19:50
  • @hvd I'm using a build target. What I just noticed however is this article. http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx Although its not exactly related it is using a special target to determine the list of assemblies. Worth a shot. – NickSuperb Jan 24 '13 at 19:52
  • For the purposes of your question, "I'm calling ILMerge (arguments)" and "I've created a build target that calls ILMerge (arguments)" mean the same thing: you'd need to show the arguments. That was what I meant to ask, sorry if I was unclear. –  Jan 24 '13 at 19:57
  • @hvd Added arguments to the question – NickSuperb Jan 24 '13 at 20:04
  • That surprises me. I do something very similar, with multiple DLLs in the same directory but only merging two of them, but that did not take any special effort. I'll check the exact command line I use when I can. –  Jan 24 '13 at 20:42
  • I've double-checked now that `ILMerge /targetplatform:v4 /internalize /out:O.dll A.dll B.dll` works for me. A.dll references B.dll, C.dll and D.dll, they are all in the same directory. O.dll contains A.dll and B.dll's methods, and requires C.dll and D.dll at runtime. –  Jan 24 '13 at 22:38
  • I think the key is "all in the same directory" This was not the case for me. See my answer below. – NickSuperb Feb 07 '13 at 16:56

3 Answers3

9

The reason this does not work ended up being rather simple: ILMerge must be able to locate the dll that you have omitted from the list.

When evaluating dependencies in the target library, ILMerge by default checks various locations to identify the dependent library (\bin, GAC, etc) even if you omitted it from the command line list. If it cannot locate this library you must specify its location using the \lib switch. Otherwise you will see the Unresolved assembly reference not allowed: three error.

Example:

ILMerge /lib:..\three\bin\three.dll /targetplatform:v2 /log /internalize /out:bin\coolproject.dll obj\Debug\coolproject.dll C:\Users\Nick\Projects\test\bin\one.dll C:\Users\Nick\Projects\test\bin\two.dll
NickSuperb
  • 1,174
  • 1
  • 8
  • 28
  • 1
    Does this leave three.dll as an external reference in the merged DLL, or does it also internalize it? Thx. – Mightymuke Feb 06 '13 at 21:36
  • 1
    This results in three.dll being an external reference which was the intent in this case. This could also be a last-ditch solution to type and namespace collision errors for internalized assemblies. – NickSuperb Feb 07 '13 at 16:54
  • 2
    If I try to specify a DLL in the /lib: argument, ILMerge fails with "Specified search directory 'C:\a\b\c.dll' not found." – Joshua Evensen Jan 08 '14 at 20:34
  • You will need to use a project variable in the build file to reference the directory correctly (other than base C:\) For example: $(ProjectDir)..\a\b\c.dll – Will Feb 16 '16 at 01:31
  • it seems you can only use /lib with a directory? – joe Jun 02 '21 at 04:57
0

I think you're looking for the 'exclude' parameter for the /internalize switch. For example, if you use

/internalize:excludes.txt

where the file excludes.txt contains

three.dll

it will internalize one.dll and two.dll but leave three.dll as an external dependancy.

Here are a couple of blog posts that go into further detail:

Update: From the documentation:

2.10 ExcludeFile

public string ExcludeFile { get; set; }

This property is used only in conjunction with the Internalize property (Section 2.12). When this is set before calling Merge, it indicates the path and filename that will be used to identify types that are not to have their visibility modified. If Internalize is true, but ExcludeFile is "", then all types in any assembly other than the primary assembly are made non-public. Setting this property implicitly sets Internalize to true.

The contents of the file should be one regular expression per line. The syntax is that defined in the .NET namespace System.Text.RegularExpressions for regular expressions. The regular expressions are matched against each type's full name, e.g., "System.Collections.IList". If the match fails, it is tried again with the assembly name (surrounded by square brackets) prepended to the type name. Thus, the pattern “[A].*” excludes all types in assembly A from being made non-public. (The backslashes are required because the string is treated as a regular expression.) The pattern “N.T” will match all types named T in the namespace named N no matter what assembly they are defined in.

It is important to note that the regular expressions are not anchored to the beginning of the string; if this is desired, use the appropriate regular expression operator characters to do so.

2.12 Internalize

public bool Internalize { get; set; }

This controls whether types in assemblies other than the primary assembly have their visibility modified. When it is true, then all non-exempt types that are visible outside of their assembly have their visibility modified so that they are not visible from outside of the merged assembly. A type is exempt if its full name matches a line from the ExcludeFile (Section 2.10) using the .NET regular expression engine.

Mightymuke
  • 5,094
  • 2
  • 31
  • 42
  • 1
    Wish the down-voter had left a comment. This is what I do with my project and it works well, not sure what the issue is here. – Mightymuke Jan 29 '13 at 01:10
  • Not sure but the issue did not have to do with the visibility of merged items but rather the "physical" inclusion of that item in the assembly. As which the internalize and exclude options are not relevant. – NickSuperb Feb 07 '13 at 17:00
0

I have written a little tool to deal with this.

The source code can be found @ https://github.com/leppie/ReferenceRemover.

In you case it will be used as:

ReferenceRemover three.dll "(one|two).*" coolproject.dll

Explanation of args:

  1. Assembly to inspect
  2. Regex to match references
  3. The assembly it need to redirect to

The output is three.dll with its references adjusted.

leppie
  • 115,091
  • 17
  • 196
  • 297