context: // NAnt - A .NET build tool
  // Copyright (C) 2001-2005 Gert Driesen
  //
  // 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
  //
  // Gert Driesen (drieseng@users.sourceforge.net)
  
  using System;
  using System.Collections;
  using System.Collections.Specialized;
  using System.Globalization;
  using System.IO;
  using System.Xml;
  
  using NAnt.Core;
  using NAnt.Core.Util;
  
  using NAnt.VisualCpp.Types;
  
  using NAnt.VSNet.Types;
  
  namespace NAnt.VSNet {
      /// <summary>
      /// Represents a Visual C++ project configuration.
      /// </summary>
      public class VcProjectConfiguration : VcConfigurationBase {
          #region Internal Instance Constructors
  
          internal VcProjectConfiguration(XmlElement elem, VcProject parentProject, DirectoryInfo outputDir) : base(elem, parentProject, outputDir) {
              // determine relative output directory (outdir)
              XmlAttribute outputDirAttribute = elem.Attributes["OutputDirectory"];
              if (outputDirAttribute != null) {
                  _rawRelativeOutputDir = outputDirAttribute.Value;
              }
  
              // get intermediate directory and expand macros 
              XmlAttribute intermidiateDirAttribute = elem.Attributes["IntermediateDirectory"];
              if (intermidiateDirAttribute != null) {
                  _rawIntermediateDir = intermidiateDirAttribute.Value;
              }
  
              // get referencespath directory and expand macros
              XmlAttribute referencesPathAttribute = elem.Attributes["ReferencesPath"];
              if (referencesPathAttribute != null) {
                  _rawReferencesPath = StringUtils.ConvertEmptyToNull(referencesPathAttribute.Value);
              }
  
              string managedExtentions = GetXmlAttributeValue(elem, "ManagedExtensions");
              if (managedExtentions != null) {
                  switch(managedExtentions.ToLower()) {
                      case "false":
                      case "0":
                          _managedExtensions = false;
                          break;
                      case "true":
                      case "1":
                          _managedExtensions = true;
                          break;
                      default:
                          throw new BuildException(String.Format("ManagedExtensions '{0}' is not supported yet.",managedExtentions));
                  }
              }
  
              // get configuration type
              string type = GetXmlAttributeValue(elem, "ConfigurationType");
              if (type != null) {
                  _type = (ConfigurationType) Enum.ToObject(typeof(ConfigurationType), 
                      int.Parse(type, CultureInfo.InvariantCulture));
              }
  
              string wholeProgramOptimization = GetXmlAttributeValue(elem, "WholeProgramOptimization");
              if (wholeProgramOptimization != null) {
                  _wholeProgramOptimization = string.Compare(wholeProgramOptimization.Trim(), "true", true, CultureInfo.InvariantCulture) == 0;
              }
              
              string characterSet = GetXmlAttributeValue(elem, "CharacterSet");
              if (characterSet != null) {
                  _characterSet = (CharacterSet) Enum.ToObject(typeof(CharacterSet), 
                      int.Parse(characterSet, CultureInfo.InvariantCulture));
              }
  
              // get MFC settings
              string useOfMFC = GetXmlAttributeValue(elem, "UseOfMFC");
              if (useOfMFC != null) {
                  _useOfMFC = (UseOfMFC) Enum.ToObject(typeof(UseOfMFC), 
                      int.Parse(useOfMFC, CultureInfo.InvariantCulture));
              }
  
              // get ATL settings
              string useOfATL = GetXmlAttributeValue(elem, "UseOfATL");
              if (useOfATL != null) {
                  _useOfATL = (UseOfATL) Enum.ToObject(typeof(UseOfATL), 
                      int.Parse(useOfATL, CultureInfo.InvariantCulture));
              }
  
              _linkerConfiguration = new LinkerConfig(this);
          }
  
          #endregion Internal Instance Constructors
  
          #region Public Instance Properties
  
          public ConfigurationType Type {
              get { return _type; }
          }
  
          public bool WholeProgramOptimization {
              get { return _wholeProgramOptimization; }
          }
  
          /// <summary>
          /// Tells the compiler which character set to use.
          /// </summary>
          public CharacterSet CharacterSet {
              get { return _characterSet; }
          }
  
          /// <summary>
          /// Gets a value indicating whether Managed Extensions for C++ are 
          /// enabled.
          /// </summary>
          public bool ManagedExtensions {
              get { return _managedExtensions; }
          }
  
          /// <summary>
          /// Gets a value indicating how MFC is used by the configuration.
          /// </summary>
          public UseOfMFC UseOfMFC {
              get { return _useOfMFC; }
          }
  
          /// <summary>
          /// Gets a value indicating how ATL is used by the configuration.
          /// </summary>
          public UseOfATL UseOfATL {
              get { return _useOfATL; }
          }
  
          #endregion Public Instance Properties
  
          #region Internal Instance Properties
  
          internal string RawRelativeOutputDir {
              get { return _rawRelativeOutputDir;}
          }
  
          internal string RawIntermediateDir {
              get { return _rawIntermediateDir;}
          }
  
          internal string RawReferencesPath {
              get { return _rawReferencesPath; }
          }
  
          internal LinkerConfig LinkerConfiguration {
              get { return _linkerConfiguration; }
          }
  
          /// <summary>
          /// Gets the list of files to link in the order in which they are 
          /// defined in the project file.
          /// </summary>
          internal ArrayList ObjFiles {
              get {
                  // lazy init
                  if (!_initialized) {
                      Initialize();
                  }
                  return _objFiles;
              }
          }
  
          /// <summary>
          /// Holds the C++ sources for each build configuration.
          /// </summary>
          /// <remarks>
          /// The key of the hashtable is a build configuration, and the
          /// value is an ArrayList holding the C++ source files for that
          /// build configuration.
          /// </remarks>
          internal Hashtable SourceConfigs {
              get {
                  // lazy init
                  if (!_initialized) {
                      Initialize();
                  }
                  return _sourceConfigs;
              }
          }
  
          /// <summary>
          /// Gets the resources for each build configuration.
          /// </summary>
          /// <remarks>
          /// The key of the hashtable is a build configuration, and the
          /// value is an ArrayList holding the resources files for that
          /// build configuration.
          /// </remarks>
          internal Hashtable RcConfigs {
              get {
                  // lazy init
                  if (!_initialized) {
                      Initialize();
                  }
                  return _rcConfigs;
              }
          }
  
          /// <summary>
          /// Get the IDL files for each build configuration.
          /// </summary>
          /// <remarks>
          /// The key of the hashtable is a build configuration, and the
          /// value is an ArrayList holding the IDL files for that build 
          /// configuration.
          /// </remarks>
          internal Hashtable IdlConfigs {
              get {
                  // lazy init
                  if (!_initialized) {
                      Initialize();
                  }
                  return _idlConfigs;
              }
          }
  
          #endregion Internal Instance Properties
  
          #region Private Instance Properties
  
          /// <summary>
          /// Gets the target path for usage in macro expansion.
          /// </summary>
          /// <value>
          /// The target path, or a zero-length string if there's no output file 
          /// for this configuration.
          /// </value>
          private string TargetPath {
              get {
                  string targetPath = string.Empty;
  
                  switch (Type) {
                      case ConfigurationType.Application:
                          string applicationOutput = GetToolSetting(VcConfigurationBase.LinkerTool, "OutputFile");
                          if (String.IsNullOrEmpty(applicationOutput)) {
                              applicationOutput = ExpandMacros("$(OutDir)/$(ProjectName).exe");
                          }
                          targetPath = FileUtils.CombinePaths(Project.ProjectDirectory.FullName, applicationOutput);
                          break;
                      case ConfigurationType.DynamicLibrary:
                          string libraryOutput = GetToolSetting(VcConfigurationBase.LinkerTool, "OutputFile");
                          if (String.IsNullOrEmpty(libraryOutput)) {
                              libraryOutput = ExpandMacros("$(OutDir)/$(ProjectName).dll");
                          }
                          targetPath = FileUtils.CombinePaths(Project.ProjectDirectory.FullName, libraryOutput);
                          break;
                      case ConfigurationType.StaticLibrary:
                          string librarianOutput = GetToolSetting(VcConfigurationBase.LibTool, "OutputFile");
                          if (String.IsNullOrEmpty(librarianOutput)) {
                              librarianOutput = ExpandMacros("$(OutDir)/$(ProjectName).lib");
                          }
                          targetPath = FileUtils.CombinePaths(Project.ProjectDirectory.FullName, librarianOutput);
                          break;
                      case ConfigurationType.Makefile:
                          string nmakeOutput = GetToolSetting(VcConfigurationBase.NMakeTool, "Output");
                          if (!String.IsNullOrEmpty(nmakeOutput)) {
                              targetPath = FileUtils.CombinePaths(Project.ProjectDirectory.FullName, nmakeOutput);
                          }
                          break;
                      case ConfigurationType.Utility:
                          break;
                  }
  
                  return targetPath;
              }
          }
  
          #endregion Private Instance Properties
  
          #region Override implementation of ConfigurationBase
  
          /// <summary>
          /// Get the directory in which intermediate build output will be stored 
          /// for this configuration.
          /// </summary>
          /// <remarks>
          /// <para>
          /// This is a directory relative to the project directory named 
          /// <c>obj\&lt;configuration name&gt;</c>.
          /// </para>
          /// <para>
          /// <c>.resx</c> and <c>.licx</c> files will only be recompiled if the
          /// compiled resource files in the <see cref="ObjectDir" /> are not 
          /// uptodate.
          /// </para>
          /// </remarks>
          public override DirectoryInfo ObjectDir {
              get { 
                  return new DirectoryInfo(FileUtils.CombinePaths(Project.ProjectDirectory.FullName, 
                      IntermediateDir));
              }
          }
  
          /// <summary>
          /// Get the path of the output directory relative to the project
          /// directory.
          /// </summary>
          public override string RelativeOutputDir {
              get { return ExpandMacros(RawRelativeOutputDir); }
          }
  
          #endregion Override implementation of ConfigurationBase
  
          #region Override implementation of VcConfigurationBase
  
          /// <summary>
          /// Gets the intermediate directory, specified relative to project 
          /// directory.
          /// </summary>
          /// <value>
          /// The intermediate directory, specified relative to project directory.
          /// </value>
          public override string IntermediateDir {
              get { return ExpandMacros(RawIntermediateDir); }
          }
  
          /// <summary>
          /// Gets the absolute path for the output file.
          /// </summary>
          /// <value>
          /// The absolute path for the output file, or <see langword="null" /> 
          /// if there's no output file for this configuration.
          /// </value>
          public override string OutputPath {
              get {
                  // lazy init
                  if (!_initialized) {
                      Initialize();
                  }
                  return _outputPath;
              }
          }
  
          /// <summary>
          /// Gets a comma-separated list of directories to scan for assembly
          /// references.
          /// </summary>
          /// <value>
          /// A comma-separated list of directories to scan for assembly
          /// references, or <see langword="null" /> if no additional directories
          /// should scanned.
          /// </value>
          public override string ReferencesPath {
              get { return ExpandMacros(RawReferencesPath); }
          }
  
          internal string GetToolSetting(string toolName, string settingName, ExpansionHandler expander) {
              return GetToolSetting(toolName, settingName, (string) null, expander);
          }
  
          public override string GetToolSetting(string toolName, string settingName, string defaultValue) {
              return GetToolSetting(toolName, settingName, defaultValue, 
                  new ExpansionHandler(ExpandMacros));
          }
  
          internal string GetToolSetting(string toolName, string settingName, string defaultValue, ExpansionHandler expander) {
              string setting = null;
  
              Hashtable toolSettings = (Hashtable) Tools[toolName];
              if (toolSettings != null) {
                  setting = (string) toolSettings[settingName];
  
gt:                 if (setting != null) {