// 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) // Ian MacLean (imaclean@gmail.com) // Scott Hernandez (ScottHernandez@hotmail.com) // William E. Caputo (wecaputo@thoughtworks.com | logosity@yahoo.com) using System; using System.Collections; using System.Collections.Specialized; using System.Configuration; using System.Globalization; using System.IO; using System.Reflection; using System.Text; using System.Xml; using Microsoft.Win32; using NAnt.Core.Tasks; using NAnt.Core.Types; using NAnt.Core.Util; namespace NAnt.Core { internal class ProjectSettingsLoader { #region Private Instance Fields private Project _project; private XmlNamespaceManager _nsMgr; #endregion Private Instance Fields #region Private Static Fields /// /// Holds a value indicating whether a scan for tasks, types and functions /// has already been performed for the current runtime framework. /// private static bool ScannedTasks = false; #endregion Private Static Fields #region Internal Instance Constructor /// /// Initializes a new instance of the /// class for the given . /// /// The that should be configured. internal ProjectSettingsLoader(Project project) { _project = project; // setup namespace manager _nsMgr = new XmlNamespaceManager(new NameTable()); _nsMgr.AddNamespace("nant", _nsMgr.DefaultNamespace); } #endregion Internal Instance Constructor #region Protected Instance Properties /// /// Gets the underlying instance. /// /// /// The underlying instance. /// protected Project Project { get { return _project; } } #endregion Protected Instance Properties #region Private Instance Properties /// /// Gets the . /// /// /// The . /// /// /// The defines the current namespace /// scope and provides methods for looking up namespace information. /// private XmlNamespaceManager NamespaceManager { get { return _nsMgr; } } #endregion Private Instance Properties #region Public Instance Methods /// /// Loads and processes settings from the specified /// of the configuration file. /// public void ProcessSettings() { if (Project.ConfigurationNode == null) { return; } // process platform configuration ProcessPlatform(Project.ConfigurationNode.SelectSingleNode( "nant:frameworks/nant:platform[@name='" + Project.PlatformName + "']", NamespaceManager)); // process global properties ProcessGlobalProperties(Project.ConfigurationNode.SelectNodes( "nant:properties/nant:property", NamespaceManager)); } #endregion Public Instance Methods #region Private Instance Methods private void ProcessPlatform(XmlNode platformNode) { // process platform task assemblies if (!ScannedTasks) { FileSet platformTaskAssemblies = new FileSet(); platformTaskAssemblies.BaseDirectory = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); platformTaskAssemblies.Project = Project; platformTaskAssemblies.NamespaceManager = NamespaceManager; platformTaskAssemblies.Parent = Project; // avoid warnings by setting the parent of the fileset platformTaskAssemblies.ID = "platform-task-assemblies"; // avoid warnings by assigning an id XmlNode taskAssembliesNode = platformNode.SelectSingleNode( "nant:task-assemblies", NamespaceManager); if (taskAssembliesNode != null) { platformTaskAssemblies.Initialize(taskAssembliesNode, Project.Properties, null); } // scan platform extensions assemblies LoadTasksTask loadTasks = new LoadTasksTask(); loadTasks.Project = Project; loadTasks.NamespaceManager = NamespaceManager; loadTasks.Parent = Project; loadTasks.TaskFileSet = platformTaskAssemblies; loadTasks.FailOnError = false; loadTasks.Threshold = (Project.Threshold == Level.Debug) ? Level.Debug : Level.Warning; loadTasks.Execute(); // scan NAnt.Core TypeFactory.ScanAssembly(Assembly.GetExecutingAssembly(), loadTasks); } // process the framework nodes of the current platform ProcessFrameworks(platformNode); // process runtime framework task assemblies if (!ScannedTasks) { LoadTasksTask loadTasks = new LoadTasksTask(); loadTasks.Project = Project; loadTasks.NamespaceManager = NamespaceManager; loadTasks.Parent = Project; loadTasks.TaskFileSet = Project.RuntimeFramework.TaskAssemblies; loadTasks.FailOnError = false; loadTasks.Threshold = (Project.Threshold == Level.Debug) ? Level.Debug : Level.Warning; loadTasks.Execute(); // ensure we don't scan task assemblies for the current // runtime framework and platform again ScannedTasks = true; } } /// /// Processes the framework nodes of the given platform node. /// /// An representing the platform on which NAnt is running. private void ProcessFrameworks(XmlNode platformNode) { // determine the framework family name string frameworkFamily = PlatformHelper.IsMono ? "mono" : "net"; // determine the version of the current runtime framework Version frameworkClrVersion = new Version(Environment.Version.ToString(3)); // determine default targetframework string defaultTargetFramework = GetXmlAttributeValue(platformNode, "default"); // deals with xml info from the config file, not build document. foreach (XmlNode frameworkNode in platformNode.SelectNodes("nant:framework", NamespaceManager)) { // skip special elements like comments, pis, text, etc. if (!(frameworkNode.NodeType == XmlNodeType.Element)) { continue; } string name = null; bool isRuntimeFramework = false; try { // get framework attributes name = GetXmlAttributeValue(frameworkNode, "name"); string family = GetXmlAttributeValue(frameworkNode, "family"); Version clrVersion = new Version(GetXmlAttributeValue(frameworkNode, "clrversion")); // check if we're processing the current runtime framework if (family == frameworkFamily && clrVersion == frameworkClrVersion) { isRuntimeFramework = true; } else { // if the name of the target framework, matches a valid // existing framework with a higher or equal version, // then skip further processing // // Note: the runtime framework can never be skipped if (Project.Frameworks.ContainsKey(name)) { FrameworkInfo existingFramework = Project.Frameworks[name]; if (existingFramework.ClrVersion >= clrVersion) { continue; } } } // get framework-specific project node XmlNode projectNode = frameworkNode.SelectSingleNode("nant:project", NamespaceManager); if (projectNode == null) { throw new BuildException(" node has not been defined."); } string tempBuildFile = Path.GetTempFileName(); XmlTextWriter writer = null; Project frameworkProject = null; try { // write project to file writer = new XmlTextWriter(tempBuildFile, Encoding.UTF8); writer.WriteStartDocument(true); writer.WriteRaw(projectNode.OuterXml); writer.Flush(); writer.Close(); // use StreamReader to load build file from to avoid // having location information as part of the error // messages using (StreamReader sr = new StreamReader(new FileStream(tempBuildFile, FileMode.Open, FileAccess.Read, FileShare.Write), Encoding.UTF8)) { XmlDocument projectDoc = new XmlDocument(); projectDoc.Load(sr); // create and execute project frameworkProject = new Project(projectDoc); frameworkProject.BaseDirectory = AppDomain.CurrentDomain.BaseDirectory; frameworkProject.Execute(); } } finally { if (writer != null) { writer.Close(); } if (File.Exists(tempBuildFile)) { File.Delete(tempBuildFile); } } string description = frameworkProject.ExpandProperties( GetXmlAttributeValue(frameworkNode, "description"), Location.UnknownLocation); string version = frameworkProject.ExpandProperties( GetXmlAttributeValue(frameworkNode, "version"), Location.UnknownLocation); string runtimeEngine = frameworkProject.ExpandProperties( GetXmlAttributeValue(frameworkNode, "runtimeengine"), Location.UnknownLocation); string frameworkDir = frameworkProject.ExpandProperties( GetXmlAttributeValue(frameworkNode, "frameworkdirectory"), Location.UnknownLocation); string frameworkAssemblyDir = frameworkProject.ExpandProperties( GetXmlAttributeValue(frameworkNode, "frameworkassemblydirectory"), Location.UnknownLocation); string sdkDir = GetXmlAttributeValue(frameworkNode, "sdkdirectory"); try { sdkDir = frameworkProject.ExpandProperties(sdkDir, Location.UnknownLocation); } catch (BuildException) { // do nothing with this exception as a framework is still // considered valid if the sdk directory is not available // or not configured correctly } // create new FrameworkInfo instance, this will throw an // an exception if the framework is not valid FrameworkInfo info = new FrameworkInfo(name, family, description, new Version(version), clrVersion, frameworkDir, sdkDir, frameworkAssemblyDir, runtimeEngine, frameworkProject); // get framework-specific environment nodes XmlNodeList environmentNodes = frameworkNode.SelectNodes("nant:environment/nant:env", NamespaceManager); // process framework environment nodes info.EnvironmentVariables = ProcessFrameworkEnvironmentVariables( environmentNodes, info); // process framework task assemblies info.TaskAssemblies.Project = frameworkProject; info.TaskAssemblies.NamespaceManager = NamespaceManager; info.TaskAssemblies.Parent = frameworkProject; // avoid warnings by setting the parent of the fileset info.TaskAssemblies.ID = "internal-task-assemblies"; // avoid warnings by assigning an id XmlNode taskAssembliesNode = frameworkNode.SelectSingleNode( "nant:task-assemblies", NamespaceManager); if (taskAssembliesNode != null) { info.TaskAssemblies.Initialize(taskAssembliesNode, frameworkProject.Properties, info); } // check if framework with this identifier already exists // and remove it if current framework has higher CLR version // or if the current framework is the runtime framework if (Project.Frameworks.ContainsKey(info.Name)) { FrameworkInfo existingFramework = Project.Frameworks[info.Name]; if (info.ClrVersion > existingFramework.ClrVersion || isRuntimeFramework) { Project.Frameworks.Remove(info.Name); } else { continue; } } Project.Frameworks.Add(info.Name, info); if (isRuntimeFramework) { // framework matches current runtime, so set it as // current target framework Project.RuntimeFramework = Project.TargetFramework = info; } } catch (Exception ex) { if (isRuntimeFramework) { // current runtime framework is not correctly configured // in NAnt configuration file throw new BuildException(string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("NA1063"), name), ex); } else { if (name != null && name == defaultTargetFramework) { Project.Log(Level.Warning, ResourceUtils.GetString("NA1181"), name, ex.Message); Project.Log(Level.Debug, ex.ToString()); Project.Log(Level.Warning, ""); } else { Project.Log(Level.Verbose, ResourceUtils.GetString("NA1182"), name, ex.Message); Project.Log(Level.Debug, ex.ToString()); Project.Log(Level.Verbose, ""); } } } } if (Project.RuntimeFramework == null) { // information about the current runtime framework should // be added to the NAnt configuration file throw new BuildException(string.Format(CultureInfo.InvariantCulture, ResourceUtils.GetString("NA1062"), frameworkFamily, frameworkClrVersion.ToString())); } if (defaultTargetFramework != null && defaultTargetFramework != "auto") { if (Project.Frameworks.ContainsKey(defaultTargetFramework)) { Project.TargetFramework = Project.Frameworks[defaultTargetFramework]; } else { Project.Log(Level.Warning, ResourceUtils.GetString("NA1178"), defaultTargetFramework, Project.RuntimeFramework.Name); Project.Log(Level.Warning, ""); } } } /// /// Reads the list of global properties specified in the NAnt configuration /// file. /// /// An representing global properties. private void ProcessGlobalProperties(XmlNodeList propertyNodes) { //deals with xml info from the config file, not build document. foreach (XmlNode propertyNode in propertyNodes) { //skip special elements like comments, pis, text, etc. if (!(propertyNode.NodeType == XmlNodeType.Element)) { continue; } // initialize task PropertyTask propertyTask = new PropertyTask(); propertyTask.Parent = propertyTask.Project = Project; propertyTask.NamespaceManager = NamespaceManager; propertyTask.InitializeTaskConfiguration(); // configure using xml node propertyTask.Initialize(propertyNode); // execute task propertyTask.Execute(); } } /// /// Processes the framework environment variables. /// /// An representing framework environment variables. /// The to obtain framework-specific information from. private EnvironmentVariableCollection ProcessFrameworkEnvironmentVariables(XmlNodeList environmentNodes, FrameworkInfo framework) { EnvironmentVariableCollection frameworkEnvironment = null; // initialize framework-specific environment variables frameworkEnvironment = new EnvironmentVariableCollection(); foreach (XmlNode environmentNode in environmentNodes) { // skip non-nant namespace elements and special elements like comments, pis, text, etc. if (!(environmentNode.NodeType == XmlNodeType.Element)) { continue; } // initialize element EnvironmentVariable environmentVariable = new EnvironmentVariable(); environmentVariable.Parent = environmentVariable.Project = framework.Project; environmentVariable.NamespaceManager = NamespaceManager; // configure using xml node environmentVariable.Initialize(environmentNode, framework.Project.Properties, framework); // add to collection of environment variables frameworkEnvironment.Add(environmentVariable); } return frameworkEnvironment; } #endregion Private Instance Methods #region Private Static Methods /// /// Gets the value of the specified attribute from the specified node. /// /// The node of which the attribute value should be retrieved. /// The attribute of which the value should be returned. /// /// The value of the attribute with the specified name or /// if the attribute does not exist or has no value. /// private static string GetXmlAttributeValue(XmlNode xmlNode, string attributeName) { string attributeValue = null; if (xmlNode != null) { XmlAttribute xmlAttribute = (XmlAttribute)xmlNode.Attributes.GetNamedItem(attributeName); if (xmlAttribute != null) { attributeValue = StringUtils.ConvertEmptyToNull(xmlAttribute.Value); } } return attributeValue; } #endregion Private Static Methods } }