Wednesday, November 11, 2009 7:42 PM Sean Feldman

ILMerge as NAnt Task

I needed to merge a few a few assemblies into one, and tried pretty much what the author of this blog did. I also wanted to be able to “hide” the namespaces merged into main assembly, so that in case there are 2 identical classes in final assembly, only the class from the primarily assembly would be showed by Visual Studio. In my case, it was custom Logger vs. log4net Logger, which I wanted to “hide”.

ILMerge does support it, but providing /internalize switch. Official documentation explains it better:

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.

The version of NAnt task I ended up is slightly different:

[TaskName("ilmerge")]
public class ILMergeTask : ExternalProgramBase 
{
  private FileSet m_assemblies;
  private string m_logFile;
  private string m_outputFile;
  private string m_primaryFile;
  private bool internalize;
  
  [TaskAttribute("internalize")]
  [BooleanValidator()]
  public virtual bool Internalize
  {
    get { return internalize;}
    set { internalize = value;}
  }
 
 
  [TaskAttribute("program", Required = true)]
  [StringValidator(AllowEmpty = false)]
  public override string ExeName
  {
      get { return base.ExeName; }
      set { base.ExeName = value; }
  }
 
 
  public override string ProgramArguments
  {
      get { return string.Empty; }
  }
 
  [BuildElement("assemblies", Required=true)]
  public virtual FileSet InputAssemblies
  {
        get
        {
            return this.m_assemblies;
        }
        set
        {
            this.m_assemblies = value;
        }
  }
 
  [TaskAttribute("logfile")]
  public virtual string LogFile
  {
        get
        {
            if (this.m_logFile == null)
            {
                return null;
            }
            return this.Project.GetFullPath(this.m_logFile);
        }
        set
        {
            this.m_logFile = StringUtils.ConvertEmptyToNull(value);
        }
  }     
 
  [TaskAttribute("primary", Required=true), StringValidator(AllowEmpty=false)]
  public virtual string PrimaryFile
  {
      get
      {
          if (this.m_primaryFile == null)
          {
              return null;
          }
          return this.Project.GetFullPath(this.m_primaryFile);
      }
      set
      {
          this.m_primaryFile = StringUtils.ConvertEmptyToNull(value);
      }
  }                 
 
  [TaskAttribute("outputfile", Required=true), StringValidator(AllowEmpty=false)]
  public virtual string OutputFile
  {
      get
      {
          if (this.m_outputFile == null)
          {
              return null;
          }
          return this.Project.GetFullPath(this.m_outputFile);
      }
      set
      {
          this.m_outputFile = StringUtils.ConvertEmptyToNull(value);
      }
  }                 
 
 
  protected override void ExecuteTask()
  {
      try
      {
          Log(Level.Info, "Executing ILMerge.exe");
          Log(Level.Info, string.Format("/out:\"{0}\"", m_outputFile));
          Log(Level.Info, string.Format("/log:\"{0}\"", m_logFile));
          Arguments.Add(new Argument(string.Format("/out:\"{0}\"", m_outputFile)));
 
          Log(Level.Info, string.Format("assembly[{0}]: {1}", "primary", m_primaryFile));
          Arguments.Add(new Argument(string.Format("\"{0}\"", m_primaryFile)));
 
          for (int i = 0; i < m_assemblies.FileNames.Count; i++)
          {
              Log(Level.Info, string.Format("assembly[{0}]: {1}", i, m_assemblies.FileNames[i]));
              Arguments.Add(new Argument(string.Format("\"{0}\"", m_assemblies.FileNames[i])));
          }
 
          Arguments.Add(new Argument(string.Format("/log:\"{0}\"", m_logFile)));
          
          if (Internalize)
          {
            Log(Level.Info, "/internalize");
            Arguments.Add(new Argument("/internalize"));
          }
 
          base.FailOnError = false;
          base.ExecuteTask();
      }
      catch (Exception ex)
      {
          throw new BuildException(string.Format("Error executing ILMerge {0}", "test"), Location, ex);
      }
  }
}

Now Internalized property can be utilized:

<ilmerge outputfile="${main.assembly.name}"
         primary="${temp.assembly.name}"
         program="${ilmerge.dir}\ilmerge.exe"
         logfile="${log.file}"
         internalize="true">
  <assemblies>
    <include name="${build.compile.dir}\log4net.dll" />
  </assemblies>
</ilmerge>

 

I was quiet surprised to learn that C#2.0 is used to compile an inline NAnt task, and not 3.0 (auto property did not work).

Filed under:

Comments

No Comments