// NAnt - A .NET build tool // Copyright (C) 2002-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 // // Scott Hernandez (ScottHernandez@hotmail.com) // Gert Driesen (gert.driesen@ardatis.com) using System; using System.IO; using System.Configuration; using System.Reflection; using System.Globalization; using System.Xml; namespace NAnt.Console { /// /// Stub used to created and launch real ConsoleDriver /// class in Core assembly. /// public class ConsoleStub { #region Static Constructor static ConsoleStub() { // check a class in mscorlib to determine if we're running on Mono if (Type.GetType("System.MonoType", false) != null) { FrameworkFamily = "mono"; } else { FrameworkFamily = "net"; } // check for non-Unix platforms - see FAQ for more details // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F int platform = (int) Environment.OSVersion.Platform; if (platform != 4 && platform != 128) { Platform = "win32"; } else { Platform = "unix"; } } #endregion Static Constructor #region Private Static Properties private static string FrameworkVersion { get { if (_frameworkVersion != null) { return _frameworkVersion; } XmlNode nantNode = (XmlNode) ConfigurationSettings.GetConfig("nant"); if (nantNode == null) { System.Console.WriteLine("The \"nant\" section in the NAnt" + " configuration file ({0}) is not available.", AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); return null; } XmlElement frameworkNode = (XmlElement) nantNode.SelectSingleNode("frameworks/platform[@name='" + Platform + "']/framework[@family='" + FrameworkFamily + "' and @clrversion='" + Environment.Version.ToString(3) + "']"); if (frameworkNode == null) { System.Console.WriteLine("The NAnt configuration file ({0})" + " does not have a node for the current" + " runtime framework.", AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); System.Console.WriteLine(string.Empty); System.Console.WriteLine("Please add a node" + " with family '{0}' and clrversion '{1}' under the" + " '{2}' platform node.", FrameworkFamily, Environment.Version.ToString(3), Platform); return null; } else { _frameworkVersion = frameworkNode.GetAttribute("version"); } return _frameworkVersion; } } #endregion Private Static Properties #region Public Static Methods /// /// Entry point for executable /// /// Command Line arguments /// The result of the real execution [STAThread] public static int Main(string[] args) { AppDomain cd = AppDomain.CurrentDomain; AppDomain executionAD = cd; string nantShadowCopyFilesSetting = ConfigurationSettings.AppSettings.Get("nant.shadowfiles"); string nantCleanupShadowCopyFilesSetting = ConfigurationSettings.AppSettings.Get("nant.shadowfiles.cleanup"); if (FrameworkVersion == null) { // signal error return 1; } string frameworkFamilyLibDir = Path.Combine("lib", FrameworkFamily); string frameworkVersionLibDir = Path.Combine(frameworkFamilyLibDir, FrameworkVersion); string privateBinPath = null; // add lib// dir to privatebinpath if it exists if (Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, frameworkVersionLibDir))) { privateBinPath += (privateBinPath != null) ? Path.PathSeparator + frameworkVersionLibDir : frameworkVersionLibDir; } // add lib/ dir to privatebinpath if it exists if (Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, frameworkFamilyLibDir))) { privateBinPath += (privateBinPath != null) ? Path.PathSeparator + frameworkFamilyLibDir : frameworkFamilyLibDir; } // add privatebinpath of current domain to privatebinpath if (AppDomain.CurrentDomain.SetupInformation.PrivateBinPath != null) { privateBinPath += Path.PathSeparator + AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; } if (nantShadowCopyFilesSetting != null && bool.Parse(nantShadowCopyFilesSetting) == true) { logger.Debug(string.Format( CultureInfo.InvariantCulture, "Shadowing files({0}) -- cleanup={1}", nantShadowCopyFilesSetting, nantCleanupShadowCopyFilesSetting)); System.AppDomainSetup myDomainSetup = new System.AppDomainSetup(); myDomainSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; logger.Debug(string.Format( CultureInfo.InvariantCulture, "NAntDomain.PrivateBinPath={0}", myDomainSetup.PrivateBinPath)); myDomainSetup.PrivateBinPath = privateBinPath; myDomainSetup.ApplicationName = "NAnt"; // copy the config file location myDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; logger.Debug(string.Format( CultureInfo.InvariantCulture, "NAntDomain.ConfigurationFile={0}", myDomainSetup.ConfigurationFile)); // yes, cache the files myDomainSetup.ShadowCopyFiles = "true"; // shadowcopy everything in base directory of appdomain and // privatebinpath myDomainSetup.ShadowCopyDirectories = myDomainSetup.ApplicationBase + Path.PathSeparator + myDomainSetup.PrivateBinPath; logger.Debug(string.Format( CultureInfo.InvariantCulture, "NAntDomain.ShadowCopyDirectories={0}", myDomainSetup.ShadowCopyDirectories)); // try to cache in .\cache folder, if that fails, let the system // figure it out. string cachePath = Path.Combine(myDomainSetup.ApplicationBase, "cache"); DirectoryInfo cachePathInfo = null; try { cachePathInfo = Directory.CreateDirectory(cachePath); } catch (Exception e) { System.Console.WriteLine("Failed to create: {0}. Using default CachePath." + e.ToString(), cachePath); } finally { if(cachePathInfo != null) { myDomainSetup.CachePath = cachePathInfo.FullName; } logger.Debug(string.Format( CultureInfo.InvariantCulture, "NAntDomain.CachePath={0}", myDomainSetup.CachePath)); } // create the domain. executionAD = AppDomain.CreateDomain(myDomainSetup.ApplicationName, AppDomain.CurrentDomain.Evidence, myDomainSetup); logger.Debug(string.Format( CultureInfo.InvariantCulture, "NAntDomain.SetupInfo:\n{0}", executionAD.SetupInformation)); } // use helper object to hold (and serialize) args for callback. logger.Debug(string.Format( CultureInfo.InvariantCulture, "Creating HelperArgs({0})", args.ToString())); HelperArguments helper = new HelperArguments(args, privateBinPath); executionAD.DoCallBack(new CrossAppDomainDelegate(helper.CallConsoleRunner)); // unload if remote/new appdomain if (!cd.Equals(executionAD)) { string cachePath = executionAD.SetupInformation.CachePath; logger.Debug(string.Format( CultureInfo.InvariantCulture, "Unloading '{0}' AppDomain", executionAD.FriendlyName)); AppDomain.Unload(executionAD); if (nantCleanupShadowCopyFilesSetting != null && bool.Parse(nantCleanupShadowCopyFilesSetting) == true) { logger.Debug(string.Format( CultureInfo.InvariantCulture, "Unloading '{0}' AppDomain", executionAD.FriendlyName)); try { logger.Debug(string.Format( CultureInfo.InvariantCulture, "Cleaning up CacheFiles in '{0}'", cachePath)); Directory.Delete(cachePath, true); } catch (FileNotFoundException ex) { logger.Error("Files not found.", ex); } catch (Exception ex) { System.Console.WriteLine("Unable to delete cache path '{1}'.\n\n{0}.", ex.ToString(), cachePath); } } } if (helper == null || helper.ExitCode == -1) { logger.Debug(string.Format( CultureInfo.InvariantCulture, "Return Code null or -1")); throw new ApplicationException("No return code set!"); } else { logger.Debug(string.Format( CultureInfo.InvariantCulture, "Return Code = {0}", helper.ExitCode)); return helper.ExitCode; } } #endregion Public Static Methods #region Private Static Fields private static readonly string FrameworkFamily; private static readonly string Platform; private static string _frameworkVersion; private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); #endregion Private Static Fields /// /// Helper class for invoking the application entry point in NAnt.Core /// and passing the command-line arguments. /// [Serializable()] private class HelperArguments : MarshalByRefObject { #region Public Instance Constructors /// /// Initializes a new instance of the /// class with the specified command-line arguments. /// /// The commandline arguments passed to NAnt.exe. /// Directories relative to the base directory of the AppDomain to probe for missing assembly references. public HelperArguments(string[] args, string probePaths) { _args = args; _probePaths = probePaths; } #endregion Public Instance Constructors #region Public Instance Properties /// /// Gets the status that the build process returned when it exited. /// /// /// The code that the build process specified when it terminated. /// public int ExitCode { get { return _exitCode; } } #endregion Public Instance Properties #region Public Instance Methods /// /// Invokes the application entry point in NAnt.Core. /// public void CallConsoleRunner() { // explicitly add the lib directory to privatebinpath although // its added to privatebinpath in the config file, as entries // in the config file are not reflected in SetupInformation if (Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "lib"))) { AppDomain.CurrentDomain.AppendPrivatePath("lib"); } // add framework specific entries to privatebinpath if (_probePaths != null) { foreach (string probePath in _probePaths.Split(Path.PathSeparator)) { logger.Debug(string.Format(CultureInfo.InvariantCulture, "Adding '{0}' to private bin path.", probePath)); AppDomain.CurrentDomain.AppendPrivatePath(probePath); } } MethodInfo mainMethodInfo = null; //load the core by name! Assembly nantCore = AppDomain.CurrentDomain.Load("NAnt.Core"); logger.Info(string.Format( CultureInfo.InvariantCulture, "NAnt.Core Loaded: {0}", nantCore.FullName)); //get the ConsoleDriver by name Type consoleDriverType = nantCore.GetType("NAnt.Core.ConsoleDriver", true, true); //find the Main Method, this method is less than optimal, but other methods failed. foreach (MethodInfo methodInfo in consoleDriverType.GetMethods(BindingFlags.Static | BindingFlags.Public)) { if (methodInfo.Name.Equals("Main")) { mainMethodInfo = methodInfo; break; } } // invoke the Main method and pass the command-line arguments as parameter. _exitCode = (int) mainMethodInfo.Invoke(null, new object[] {_args}); logger.Debug(string.Format( CultureInfo.InvariantCulture, "'{0}' returned {1}", mainMethodInfo.ToString(), ExitCode)); } #endregion Public Instance Methods #region Private Instance Fields private string[] _args; private string _probePaths; private int _exitCode = -1; #endregion Private Instance Fields #region Private Static Fields private static readonly log4net.ILog logger = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); #endregion Private Static Fields } } }