// 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 (gert.driesen@ardatis.com)
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 {
///
/// Represents a Visual C++ project configuration.
///
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) {
_managedExtensions = string.Compare(managedExtentions.Trim(), "true", true, CultureInfo.InvariantCulture) == 0;
}
// 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; }
}
///
/// Tells the compiler which character set to use.
///
public CharacterSet CharacterSet {
get { return _characterSet; }
}
///
/// Gets a value indicating whether Managed Extensions for C++ are
/// enabled.
///
public bool ManagedExtensions {
get { return _managedExtensions; }
}
///
/// Gets a value indicating how MFC is used by the configuration.
///
public UseOfMFC UseOfMFC {
get { return _useOfMFC; }
}
///
/// Gets a value indicating how ATL is used by the configuration.
///
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; }
}
///
/// Gets the list of files to link in the order in which they are
/// defined in the project file.
///
internal ArrayList ObjFiles {
get {
// lazy init
if (!_initialized) {
Initialize();
}
return _objFiles;
}
}
///
/// Holds the C++ sources for each build configuration.
///
///
/// The key of the hashtable is a build configuration, and the
/// value is an ArrayList holding the C++ source files for that
/// build configuration.
///
internal Hashtable SourceConfigs {
get {
// lazy init
if (!_initialized) {
Initialize();
}
return _sourceConfigs;
}
}
///
/// Gets the resources for each build configuration.
///
///
/// The key of the hashtable is a build configuration, and the
/// value is an ArrayList holding the resources files for that
/// build configuration.
///
internal Hashtable RcConfigs {
get {
// lazy init
if (!_initialized) {
Initialize();
}
return _rcConfigs;
}
}
///
/// Get the IDL files for each build configuration.
///
///
/// The key of the hashtable is a build configuration, and the
/// value is an ArrayList holding the IDL files for that build
/// configuration.
///
internal Hashtable IdlConfigs {
get {
// lazy init
if (!_initialized) {
Initialize();
}
return _idlConfigs;
}
}
#endregion Internal Instance Properties
#region Private Instance Properties
///
/// Gets the target path for usage in macro expansion.
///
///
/// The target path, or a zero-length string if there's no output file
/// for this configuration.
///
private string TargetPath {
get {
string targetPath = string.Empty;
switch (Type) {
case ConfigurationType.Application:
string applicationOutput = GetToolSetting(VcConfigurationBase.LinkerTool, "OutputFile");
if (StringUtils.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 (StringUtils.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 (StringUtils.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 (!StringUtils.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
///
/// Get the directory in which intermediate build output will be stored
/// for this configuration.
///
///
///
/// This is a directory relative to the project directory named
/// obj\<configuration name>.
///
///
/// .resx and .licx files will only be recompiled if the
/// compiled resource files in the are not
/// uptodate.
///
///
public override DirectoryInfo ObjectDir {
get {
return new DirectoryInfo(FileUtils.CombinePaths(Project.ProjectDirectory.FullName,
IntermediateDir));
}
}
///
/// Get the path of the output directory relative to the project
/// directory.
///
public override string RelativeOutputDir {
get { return ExpandMacros(RawRelativeOutputDir); }
}
#endregion Override implementation of ConfigurationBase
#region Override implementation of VcConfigurationBase
///
/// Gets the intermediate directory, specified relative to project
/// directory.
///
///
/// The intermediate directory, specified relative to project directory.
///
public override string IntermediateDir {
get { return ExpandMacros(RawIntermediateDir); }
}
///
/// Gets the absolute path for the output file.
///
///
/// The absolute path for the output file, or
/// if there's no output file for this configuration.
///
public override string OutputPath {
get {
// lazy init
if (!_initialized) {
Initialize();
}
return _outputPath;
}
}
///
/// Gets a comma-separated list of directories to scan for assembly
/// references.
///
///
/// A comma-separated list of directories to scan for assembly
/// references, or if no additional directories
/// should scanned.
///
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];
if (setting != null) {
// expand macros
return expander(setting);
}
}
if (setting == null && defaultValue != null) {
return expander(defaultValue);
}
return setting;
}
public override Hashtable GetToolArguments(string toolName, VcArgumentMap argMap, VcArgumentMap.ArgGroup ignoreGroup) {
return GetToolArguments(toolName, argMap, ignoreGroup, new ExpansionHandler(ExpandMacros));
}
internal Hashtable GetToolArguments(string toolName, VcArgumentMap argMap, VcArgumentMap.ArgGroup ignoreGroup, ExpansionHandler expander) {
Hashtable args = CollectionsUtil.CreateCaseInsensitiveHashtable();
Hashtable toolSettings = (Hashtable) Tools[toolName];
if (toolSettings != null) {
foreach (DictionaryEntry de in toolSettings) {
string arg = argMap.GetArgument((string) de.Key, expander((string) de.Value), ignoreGroup);
if (arg != null) {
args[(string) de.Key] = arg;
}
}
}
return args;
}
///
/// Expands the given macro.
///
/// The macro to expand.
///
/// The expanded macro.
///
///
/// The macro is not supported.
/// -or-
/// The macro is not implemented.
/// -or-
/// The macro cannot be expanded.
///
///
/// Expansion of a given macro is not yet implemented.
///
protected internal override string ExpandMacro(string macro) {
// perform case-insensitive expansion of macros
switch (macro.ToLower(CultureInfo.InvariantCulture)) {
case "targetname": // E.g. WindowsApplication1
return Path.GetFileNameWithoutExtension(Path.GetFileName(
TargetPath));
case "targetpath": // E.g. C:\Doc...\Visual Studio Projects\WindowsApplications1\bin\Debug\WindowsApplications1.exe
return TargetPath;
case "targetext": // E.g. .exe
return Path.GetExtension(TargetPath);
case "targetfilename": // E.g. WindowsApplications1.exe
return Path.GetFileName(TargetPath);
case "targetdir": // E.g. C:\Doc...\Visual Studio Projects\WindowsApplications1\bin\Debug
return Path.GetDirectoryName(TargetPath) + (TargetPath.EndsWith(
Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture))
? "" : Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture));
default:
return base.ExpandMacro(macro);
}
}
#endregion Override implementation of VcConfigurationBase
#region Private Instance Methods
private void Initialize() {
VcProject vcProject = (VcProject) Project;
// determine directory for storing intermediate build output for
// current project build configuration
string intermediateDir = FileUtils.CombinePaths(vcProject.ProjectDirectory.FullName,
IntermediateDir);
foreach (object projectFile in vcProject.ProjectFiles) {
string fileName = null;
VcConfigurationBase fileConfig = null;
// the array list contains either strings or hashtables
if (projectFile is string) {
fileName = (string) projectFile;
} else {
Hashtable fileConfigurations = (Hashtable) projectFile;
// obtain file configuration for current build configuration
VcFileConfiguration configuration = (VcFileConfiguration)
fileConfigurations[Name];
if (configuration != null && configuration.ExcludeFromBuild) {
continue;
}
fileConfig = configuration;
// determine relative path
if (configuration == null) {
// obtain relative path for other build configuration
// as the relative is the same anyway
foreach (DictionaryEntry de in fileConfigurations) {
configuration = (VcFileConfiguration) de.Value;
break;
}
}
fileName = configuration.RelativePath;
}
string ext = Path.GetExtension(fileName).ToLower(CultureInfo.InvariantCulture);
// if there's no specific file configuration (for the current
// build configuration), then use the project configuration
if (fileConfig == null) {
fileConfig = this;
}
switch (ext) {
case ".cpp":
case ".c":
if (!_sourceConfigs.ContainsKey(fileConfig)) {
_sourceConfigs[fileConfig] = new ArrayList(1);
}
// add file to list of sources to build with this config
((ArrayList) _sourceConfigs[fileConfig]).Add(fileName);
// register output file for linking
_objFiles.Add(vcProject.GetObjOutputFile(fileName,
fileConfig, intermediateDir));
break;
case ".rc":
if (!_rcConfigs.ContainsKey(fileConfig)) {
_rcConfigs[fileConfig] = new ArrayList(1);
}
// add file to list of resources to build with this config
((ArrayList) _rcConfigs[fileConfig]).Add(fileName);
// register output file for linking
_objFiles.Add(vcProject.GetResourceOutputFile(fileName,
fileConfig));
break;
case ".idl":
case ".odl": // ODL is used for old OLE objects
if (!_idlConfigs.ContainsKey(fileConfig)) {
_idlConfigs[fileConfig] = new ArrayList(1);
}
// add file to list of idl's to build with this config
((ArrayList) _idlConfigs[fileConfig]).Add(fileName);
break;
}
}
switch (Type) {
case ConfigurationType.StaticLibrary:
_outputPath = GetLibrarianOutputFile(intermediateDir);
break;
case ConfigurationType.Application:
case ConfigurationType.DynamicLibrary:
_outputPath = GetLinkerOutputFile();
break;
case ConfigurationType.Makefile:
string nmakeOutput = GetToolSetting(VcConfigurationBase.NMakeTool, "Output");
if (!StringUtils.IsNullOrEmpty(nmakeOutput)) {
_outputPath = FileUtils.CombinePaths(Project.ProjectDirectory.FullName, nmakeOutput);
}
break;
}
// mark initialization complete
_initialized = true;
}
private string GetLibrarianOutputFile(string intermediateDir) {
if (_objFiles.Count == 0) {
return null;
}
string outFile = GetToolSetting(VcConfigurationBase.LibTool,
"OutputFile", "$(OutDir)/$(ProjectName).lib");
// if OutputFile is explicitly set to an empty string, VS.NET
// uses file name of first obj file (in intermediate directory)
if (StringUtils.IsNullOrEmpty(outFile)) {
outFile = FileUtils.CombinePaths(intermediateDir,
Path.GetFileNameWithoutExtension((string) _objFiles[0])
+ ".lib");
} else {
outFile = FileUtils.CombinePaths(Project.ProjectDirectory.FullName,
outFile);
}
return outFile;
}
private string GetLinkerOutputFile() {
const string noinherit = "$(noinherit)";
string addDeps = GetToolSetting(VcConfigurationBase.LinkerTool, "AdditionalDependencies");
if (!StringUtils.IsNullOrEmpty(addDeps)) {
// remove noherit macro from addDeps
if (addDeps.ToLower(CultureInfo.InvariantCulture).IndexOf(noinherit) != -1) {
addDeps = addDeps.Remove(addDeps.ToLower(CultureInfo.InvariantCulture).IndexOf(noinherit), noinherit.Length);
}
string[] depParts = addDeps.Split(' ');
for (int i = 0; i < depParts.Length; i++) {
string addDep = depParts[i];
if (Path.GetExtension(addDep) == ".obj") {
_objFiles.Insert(i, addDep);
}
}
}
if (_objFiles.Count == 0) {
return null;
}
string extension = string.Empty;
switch (Type) {
case ConfigurationType.Application:
extension = ".exe";
break;
case ConfigurationType.DynamicLibrary:
extension = ".dll";
break;
}
// output file name
string outFile = GetToolSetting(VcConfigurationBase.LinkerTool,
"OutputFile", "$(OutDir)/$(ProjectName)" + extension);
// if OutputFile is explicitly set to an empty string, VS.NET
// uses file name of first obj file (in the current directory) and
// extention based on configuration type
if (StringUtils.IsNullOrEmpty(outFile)) {
outFile = FileUtils.CombinePaths(Project.ProjectDirectory.FullName,
Path.GetFileNameWithoutExtension((string) _objFiles[0]) +
extension);
}
if (SolutionTask.OutputDir != null) {
outFile = FileUtils.CombinePaths(SolutionTask.OutputDir.FullName,
Path.GetFileName(outFile));
} else {
outFile = FileUtils.CombinePaths(Project.ProjectDirectory.FullName,
outFile);
}
return outFile;
}
#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
#region Private Instance Fields
private readonly string _rawRelativeOutputDir;
private readonly string _rawIntermediateDir;
private readonly string _rawReferencesPath;
private readonly ConfigurationType _type;
private readonly bool _wholeProgramOptimization;
private readonly bool _managedExtensions;
private readonly CharacterSet _characterSet = CharacterSet.NotSet;
private readonly UseOfMFC _useOfMFC = UseOfMFC.NotUsing;
private readonly UseOfATL _useOfATL = UseOfATL.NotUsing;
private readonly LinkerConfig _linkerConfiguration;
private bool _initialized;
///
/// Holds the output path for this build configuration.
///
///
/// Lazy initialized by .
///
private string _outputPath;
///
/// Holds list of files to link in the order in which they are defined
/// in the project file.
///
private readonly ArrayList _objFiles = new ArrayList();
///
/// Holds the C++ sources for each build configuration.
///
///
/// The key of the hashtable is a build configuration, and the
/// value is an ArrayList holding the C++ source files for that
/// build configuration.
///
private readonly Hashtable _sourceConfigs = new Hashtable();
///
/// Holds the resources for each build configuration.
///
///
/// The key of the hashtable is a build configuration, and the
/// value is an ArrayList holding the resources files for that
/// build configuration.
///
private readonly Hashtable _rcConfigs = new Hashtable();
///
/// Holds the IDL files for each build configuration.
///
///
/// The key of the hashtable is a build configuration, and the
/// value is an ArrayList holding the IDL files for that build
/// configuration.
///
private readonly Hashtable _idlConfigs = new Hashtable();
#endregion Private Instance Fields
///
/// The type of output for a given configuration.
///
public enum ConfigurationType {
///
/// A Makefile.
///
Makefile = 0,
///
/// Application (.exe).
///
Application = 1,
///
/// Dynamic Library (.dll).
///
DynamicLibrary = 2,
///
/// Static Library (.lib).
///
StaticLibrary = 4,
///
/// Utility.
///
Utility = 10
}
internal class LinkerConfig {
#region Private Instance Constructor
internal LinkerConfig(VcProjectConfiguration projectConfig) {
_projectConfig = projectConfig;
}
#endregion Private Instance Constructor
#region Public Instance Properties
///
/// Gets a instance representing the
/// absolute path to the import library to generate.
///
///
/// A representing the absolute path to the
/// import library to generate, or if no
/// import library must be generated.
///
public FileInfo ImportLibrary {
get {
string defaultImportLibrary = null;
if (!Project.IsManaged(_projectConfig.Name)) {
defaultImportLibrary = "$(OutDir)/$(TargetName).lib";
}
string importLibrary = StringUtils.ConvertEmptyToNull(
_projectConfig.GetToolSetting(VcConfigurationBase.LinkerTool,
"ImportLibrary", defaultImportLibrary));
if (importLibrary == null) {
// no import library must be generated
return null;
}
if (_projectConfig.SolutionTask.OutputDir != null) {
importLibrary = FileUtils.CombinePaths(
_projectConfig.SolutionTask.OutputDir.FullName,
Path.GetFileName(importLibrary));
} else {
importLibrary = FileUtils.CombinePaths(
Project.ProjectDirectory.FullName, importLibrary);
}
return new FileInfo(importLibrary);
}
}
#endregion Public Instance Properties
#region Private Instance Properties
private VcProject Project {
get { return (VcProject) _projectConfig.Project; }
}
#endregion Private Instance Properties
#region Private Instance Fields
private readonly VcProjectConfiguration _projectConfig;
#endregion Private Instance Fields
}
}
}