// 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 } }