// NAnt - A .NET build tool
// Copyright (C) 2001-2003 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Gerry Shaw (gerry_shaw@yahoo.com)
// Aaron Anderson (gerry_shaw@yahoo.com)
// Ian MacLean (ian@maclean.ms)
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using NAnt.DotNet.Types;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Tasks;
using NAnt.Core.Types;
using NAnt.Core.Util;
namespace NAnt.Win32.Tasks {
///
/// Imports a type library to a .NET assembly (wraps Microsoft's tlbimp.exe).
///
///
///
/// This task lets you easily create interop assemblies. By default, it will
/// not reimport if the underlying COM TypeLib or reference has not changed.
///
///
/// See the Microsoft.NET Framework SDK documentation for details.
///
///
///
/// Import LegacyCOM.dll to DotNetAssembly.dll.
///
///
/// ]]>
///
///
///
///
/// Generate an assembly named "Interop.MSVidCtlLib.dll" for the
/// MS Video Control 1.0 Type Library, transforming any [out, retval]
/// parameters of methods on dispinterfaces in the type library into
/// return values in the managed library.
///
///
///
///
///
///
///
/// ]]>
///
///
[TaskName("tlbimp")]
[ProgramLocation(LocationType.FrameworkSdkDir)]
public class TlbImpTask : ExternalProgramBase {
#region Private Instance Fields
private FileInfo _outputFile;
private string _namespace;
private string _asmVersion;
private bool _delaySign;
private bool _primary;
private FileInfo _publicKeyFile;
private FileInfo _keyFile;
private string _keyContainer;
private AssemblyFileSet _references = new AssemblyFileSet();
private bool _strictref;
private bool _sysarray;
private string _transform;
private bool _unsafe;
private FileInfo _typelib;
private StringBuilder _argumentBuilder;
// framework configuration settings
private bool _supportsTransform = true;
#endregion Private Instance Fields
#region Public Instance Properties
///
/// Specifies the name of the output file.
///
///
/// The name of the output file.
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("output", Required=true)]
public FileInfo OutputFile {
get { return _outputFile; }
set { _outputFile = value; }
}
///
/// Specifies the namespace in which to produce the assembly.
///
///
/// The namespace in which to produce the assembly.
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("namespace")]
public string Namespace {
get { return _namespace; }
set { _namespace = StringUtils.ConvertEmptyToNull(value); }
}
///
/// Specifies the version number of the assembly to produce.
///
///
///
/// The version number of the assembly to produce.
///
///
/// The version number should be in the format major.minor.build.revision.
///
///
/// See the Microsoft.NET Framework SDK documentation for details.
///
///
[TaskAttribute("asmversion")]
public string AsmVersion {
get { return _asmVersion; }
set { _asmVersion = StringUtils.ConvertEmptyToNull(value); }
}
///
/// Specifies whether the resulting assembly should be signed with a
/// strong name using delayed signing. The default is .
///
///
/// if the resulting assembly should be signed
/// with a strong name using delayed signing; otherwise, .
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("delaysign")]
[BooleanValidator()]
public bool DelaySign {
get { return _delaySign; }
set { _delaySign = value; }
}
///
/// Specifies whether a primary interop assembly should be produced for
/// the specified type library. The default is .
///
///
/// if a primary interop assembly should be
/// produced; otherwise, .
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("primary")]
[BooleanValidator()]
public bool Primary {
get { return _primary; }
set { _primary = value; }
}
///
/// Specifies the file containing the public key to use to sign the
/// resulting assembly.
///
///
/// The file containing the public key to use to sign the resulting
/// assembly.
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("publickey")]
public FileInfo PublicKeyFile {
get { return _publicKeyFile; }
set { _publicKeyFile = value; }
}
///
/// Specifies the publisher's official public/private key pair with which
/// the resulting assembly should be signed with a strong name.
///
///
/// The keyfile to use to sign the resulting assembly with a strong name.
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("keyfile")]
public FileInfo KeyFile {
get { return _keyFile; }
set { _keyFile = value; }
}
///
/// Specifies the key container in which the public/private key pair
/// should be found that should be used to sign the resulting assembly
/// with a strong name.
///
///
/// The key container containing a public/private key pair that should
/// be used to sign the resulting assembly.
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("keycontainer")]
public string KeyContainer {
get { return _keyContainer; }
set {_keyContainer = StringUtils.ConvertEmptyToNull(value); }
}
///
/// Specifies the assembly files to use to resolve references to types
/// defined outside the current type library.
///
///
/// The assembly files to use to resolve references to types defined
/// outside the current type library.
///
/// See the Microsoft.NET Framework SDK documentation for details.
[BuildElement("references")]
public AssemblyFileSet References {
get { return _references; }
set { _references = value; }
}
///
/// Specifies whether a type library should not be imported if all
/// references within the current assembly or the reference assemblies
/// cannot be resolved. The default is .
///
///
/// if a type library should not be imported if
/// all references cannot be resolved; otherwise, .
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("strictref")]
[BooleanValidator()]
public bool StrictRef {
get { return _strictref; }
set { _strictref = value; }
}
///
/// Specifies whether to import a COM style SafeArray as a managed
/// class type. The default is .
///
///
/// if a COM style SafeArray should be imported
/// as a managed class type; otherwise,
/// .
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("sysarray")]
[BooleanValidator()]
public bool SysArray {
get { return _sysarray; }
set { _sysarray = value; }
}
///
/// Specifies how to transform the metadata [.NET 1.1 or higher].
///
[TaskAttribute("transform")]
public string Transform {
get { return _transform; }
set { _transform = StringUtils.ConvertEmptyToNull(value); }
}
///
/// Specifies the source type library that gets passed to the type
/// library importer.
///
///
/// The source type library that gets passed to the type library
/// importer.
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("typelib", Required=true)]
public FileInfo TypeLib {
get { return _typelib; }
set { _typelib = value; }
}
///
/// Specifies whether interfaces should be produced without .NET Framework
/// security checks. The default is .
///
///
/// if interfaces without .NET Framework security
/// checks should be produced; otherwise, .
///
/// See the Microsoft.NET Framework SDK documentation for details.
[TaskAttribute("unsafe")]
[BooleanValidator()]
public bool Unsafe {
get { return _unsafe; }
set { _unsafe = value; }
}
///
/// Indicates whether tlbimp supports transforming metadata for
/// a given target framework. The default is .
///
[FrameworkConfigurable("supportstransform")]
public bool SupportsTransform {
get { return _supportsTransform; }
set { _supportsTransform = value; }
}
#endregion Public Instance Properties
#region Override implementation of ExternalProgramBase
///
/// Gets the command line arguments for the external program.
///
///
/// The command line arguments for the external program.
///
public override string ProgramArguments {
get {
if (_argumentBuilder != null) {
return _argumentBuilder.ToString();
} else {
return null;
}
}
}
///
/// Imports the type library to a .NET assembly.
///
protected override void ExecuteTask() {
// ensure base directory is set, even if fileset was not initialized
// from XML
if (References.BaseDirectory == null) {
References.BaseDirectory = new DirectoryInfo(Project.BaseDirectory);
}
// check to see if any of the underlying interop dlls or the typelibs have changed
// otherwise, it's not necessary to reimport.
if (NeedsCompiling()) {
// using a stringbuilder vs. StreamWriter since this program will not accept response files.
_argumentBuilder = new StringBuilder();
_argumentBuilder.Append("\"" + TypeLib.FullName + "\"");
// any option that specifies a file name must be wrapped in quotes
// to handle cases with spaces in the path.
_argumentBuilder.AppendFormat(" /out:\"{0}\"", OutputFile.FullName);
// suppresses the Microsoft startup banner display
_argumentBuilder.Append(" /nologo");
if (AsmVersion != null) {
_argumentBuilder.AppendFormat(" /asmversion:\"{0}\"", AsmVersion);
}
if (Namespace != null) {
_argumentBuilder.AppendFormat(" /namespace:\"{0}\"", Namespace);
}
if (Primary) {
_argumentBuilder.Append(" /primary");
}
if (Unsafe) {
_argumentBuilder.Append(" /unsafe");
}
if (DelaySign) {
_argumentBuilder.Append(" /delaysign");
}
if (PublicKeyFile != null) {
_argumentBuilder.AppendFormat(" /publickey:\"{0}\"", PublicKeyFile.FullName);
}
if (KeyFile != null) {
_argumentBuilder.AppendFormat(" /keyfile:\"{0}\"", KeyFile.FullName);
}
if (KeyContainer != null) {
_argumentBuilder.AppendFormat(" /keycontainer:\"{0}\"", KeyContainer);
}
if (StrictRef) {
_argumentBuilder.Append(" /strictref");
}
if (SysArray) {
_argumentBuilder.Append(" /sysarray");
}
if (Transform != null) {
if (SupportsTransform) {
_argumentBuilder.AppendFormat(" /transform:\"{0}\"", Transform);
}
}
if (Verbose) {
// displays extra information
_argumentBuilder.Append(" /verbose");
} else {
// suppresses all output except for errors
_argumentBuilder.Append(" /silent");
}
foreach (string fileName in References.FileNames) {
_argumentBuilder.AppendFormat(" /reference:\"{0}\"", fileName);
}
// call base class to do the work
base.ExecuteTask();
}
}
#endregion Override implementation of ExternalProgramBase
#region Public Static Methods
///
/// Returns the path of the type library, removing the identifier of
/// the type library from the specified string.
///
/// The path from which to extract the path of the type library.
///
/// The path of the type library without the type library identifier.
///
///
/// An example of a path which includes the identifier of the type
/// library (in this case "2") is
/// C:\WINDOWS\system32\msvidctl.dll\2.
///
public static string ExtractTypeLibPath(string path) {
Regex regex = new Regex(@"^.*\\\d+$", RegexOptions.IgnorePatternWhitespace
| RegexOptions.Multiline | RegexOptions.IgnoreCase);
if (regex.IsMatch(path)) {
return path.Substring(0, path.LastIndexOf("\\"));
}
return path;
}
#endregion Public Static Methods
#region Protected Instance Methods
///
/// Determines whether the type library needs to be imported again.
///
///
/// if the type library needs to be imported;
/// otherwise, .
///
protected virtual bool NeedsCompiling() {
// return true as soon as we know we need to compile
if (!OutputFile.Exists) {
Log(Level.Verbose, "Output file '{0}' does not exist, recompiling.",
OutputFile.FullName);
return true;
}
string typeLibPath = ExtractTypeLibPath(TypeLib.FullName);
// check if the type library was updated since the interop assembly was generated
string fileName = FileSet.FindMoreRecentLastWriteTime(typeLibPath, OutputFile.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' has been updated, recompiling.",
fileName);
return true;
}
// check if the reference assemblies were updated since the interop assembly was generated
fileName = FileSet.FindMoreRecentLastWriteTime(References.FileNames, OutputFile.LastWriteTime);
if (fileName != null) {
Log(Level.Verbose, "'{0}' has been updated, recompiling.",
fileName);
return true;
}
// if we made it here then we don't have to reimport the typelib.
return false;
}
#endregion Protected Instance Methods
}
}