// NAnt - A .NET build tool
// Copyright (C) 2001-2004 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
//
// 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 Microsoft.Win32;
using NAnt.Core;
using NAnt.Core.Util;
using NAnt.VSNet.Tasks;
namespace NAnt.VSNet {
public class ManagedAssemblyReference : AssemblyReferenceBase {
public ManagedAssemblyReference(XmlElement xmlDefinition, ReferencesResolver referencesResolver, ProjectBase parent, GacCache gacCache) : base(xmlDefinition, referencesResolver, parent, gacCache) {
XmlAttribute privateAttribute = xmlDefinition.Attributes["Private"];
if (privateAttribute != null) {
_isPrivateSpecified = true;
_isPrivate = bool.Parse(privateAttribute.Value);
}
// determine name of reference
XmlAttribute assemblyNameAttribute = XmlDefinition.Attributes["AssemblyName"];
if (assemblyNameAttribute != null) {
_name = assemblyNameAttribute.Value;
}
_assemblyFile = ResolveAssemblyReference();
}
#region Override implementation of AssemblyReferenceBase
protected override bool IsPrivate {
get { return _isPrivate; }
}
protected override bool IsPrivateSpecified {
get { return _isPrivateSpecified; }
}
///
/// Resolves an assembly reference.
///
///
/// The full path to the resolved assembly, or
/// if the assembly reference could not be resolved.
///
///
///
/// Visual Studio .NET uses the following search mechanism :
///
///
/// -
///
/// The project directory.
///
///
/// -
///
/// The directories specified in the "ReferencePath" property,
/// which is stored in the .USER file.
///
///
/// -
///
/// The .NET Framework directory (see KB306149)
///
///
/// -
///
///
/// The directories specified under the following registry
/// keys:
///
///
/// -
///
/// HKLM\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders
///
///
/// -
///
/// HKCU\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders
///
///
/// -
///
/// HKLM\SOFTWARE\Microsoft\VisualStudio\<major version>.<minor version>\AssemblyFolders
///
///
/// -
///
/// HKCU\SOFTWARE\Microsoft\VisualStudio\<major version>.<minor version>\AssemblyFolders
///
///
///
///
/// Future versions of Visual Studio .NET will also check
/// in:
///
///
/// -
///
/// HKLM\SOFTWARE\Microsoft\.NETFramework\AssemblyFoldersEx
///
///
/// -
///
/// HKCU\SOFTWARE\Microsoft\.NETFramework\AssemblyFoldersEx
///
///
///
///
///
/// -
///
/// The HintPath.
///
///
///
///
protected override string ResolveAssemblyReference() {
// check if assembly reference was resolved before
if (_assemblyFile != null) {
// if assembly file actually exists, there's no need to resolve
// the assembly reference again
if (File.Exists(_assemblyFile)) {
return _assemblyFile;
}
}
XmlElement referenceElement = XmlDefinition;
string assemblyFileName = Name + ".dll";
// 1. The project directory
// NOT SURE IF THIS IS CORRECT
// 2. The ReferencePath
// NOT SURE WE SHOULD DO THIS ONE
// 3. The .NET Framework directory
string resolvedAssemblyFile = ResolveFromFramework(assemblyFileName);
if (resolvedAssemblyFile != null) {
return resolvedAssemblyFile;
}
// 4. AssemblyFolders
resolvedAssemblyFile = ResolveFromAssemblyFolders(referenceElement,
assemblyFileName);
if (resolvedAssemblyFile != null) {
return resolvedAssemblyFile;
}
// ResolveFromRelativePath will return a path regardless of
// whether the file actually exists
//
// the file might actually be created as result of building
// a project
resolvedAssemblyFile = ResolveFromRelativePath(
referenceElement.GetAttribute("HintPath"));
if (resolvedAssemblyFile != null) {
return resolvedAssemblyFile;
}
// assembly reference could not be resolved
return null;
}
#endregion Override implementation of AssemblyReferenceBase
#region Override implementation of ReferenceBase
///
/// Gets the name of the referenced assembly.
///
///
/// The name of the referenced assembly, or if
/// the name could not be determined.
///
public override string Name {
get { return _name; }
}
#endregion Override implementation of ReferenceBase
#region Private Instance Properties
///
/// Gets the Visual Studio .NET AssemblyFolders registry key matching
/// the current target framework.
///
///
/// The Visual Studio .NET AssemblyFolders registry key matching the
/// current target framework.
///
/// The current target framework is not supported.
///
/// We use the target framework instead of the product version of the
/// containing project file to determine what registry key to scan, as
/// we don't want to use assemblies meant for uplevel framework versions.
///
private string AssemblyFoldersKey {
get {
string visualStudioVersion = Parent.SolutionTask.Project.
TargetFramework.VisualStudioVersion.ToString();
return string.Format(CultureInfo.InvariantCulture,
@"SOFTWARE\Microsoft\VisualStudio\{0}\AssemblyFolders",
visualStudioVersion);
}
}
#endregion Private Instance Properties
#region Private Instance Methods
private string GetComponentAssemblyFolder(XmlElement referenceElement) {
string componentAssemblyFolder = null;
if (referenceElement.Attributes["AssemblyFolderKey"] != null) {
string assemblyFolderKey = referenceElement.Attributes["AssemblyFolderKey"].Value;
RegistryKey registryHive = null;
string[] assemblyFolderKeyParts = assemblyFolderKey.Split('\\');
if (assemblyFolderKeyParts.Length < 2 || assemblyFolderKeyParts.Length > 3) {
throw new BuildException(string.Format(CultureInfo.InvariantCulture,
"Invalid AssemblyFolderKey \"{0}\" for assembly"
+ " reference \"{1}\", referenced by project"
+ " \"{2}\".", assemblyFolderKey, Name, Parent.Name),
Location.UnknownLocation);
}
switch (assemblyFolderKeyParts[0]) {
case "hklm":
registryHive = Registry.LocalMachine;
break;
case "hkcu":
registryHive = Registry.CurrentUser;
break;
default:
throw new BuildException(string.Format(CultureInfo.InvariantCulture,
"Invalid AssemblyFolderKey \"{0}\" for assembly"
+ " reference \"{1}\", referenced by project"
+ " \"{2}\".", assemblyFolderKey, Name, Parent.Name),
Location.UnknownLocation);
}
RegistryKey repositoryKey = null;
// if AssemblyFolderKey has three parts, then the second
// parts specifies the registry key to search
if (assemblyFolderKeyParts.Length == 3) {
switch (assemblyFolderKeyParts[1]) {
case "dn":
repositoryKey = registryHive.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework\AssemblyFolders");
break;
default:
throw new BuildException(string.Format(CultureInfo.InvariantCulture,
"Invalid AssemblyFolderKey \"{0}\" for assembly"
+ " reference \"{1}\", referenced by project"
+ " \"{2}\".", assemblyFolderKey, Name, Parent.Name),
Location.UnknownLocation);
}
} else {
repositoryKey = registryHive.OpenSubKey(AssemblyFoldersKey);
}
if (repositoryKey != null) {
RegistryKey componentKey = repositoryKey.OpenSubKey(
assemblyFolderKeyParts[assemblyFolderKeyParts.Length - 1]);
if (componentKey != null) {
string folder = componentKey.GetValue(string.Empty) as string;
if (folder != null) {
componentAssemblyFolder = folder;
} else {
Log(Level.Debug, "Default value for AssemblyFolder"
+ " \"{0}\" does not exist or is not a string"
+ " value.", assemblyFolderKey);
}
} else {
Log(Level.Debug, "Component key for AssemblyFolder \"{0}\""
+ " does not exist.", assemblyFolderKey);
}
} else {
Log(Level.Debug, "Repository for AssemblyFolder \"{0}\" does"
+ " not exist.", assemblyFolderKey);
}
}
return componentAssemblyFolder;
}
protected override string ResolveFromAssemblyFolders(XmlElement referenceElement, string fileName) {
string resolvedAssemblyFile = null;
string componentAssemblyFolder = GetComponentAssemblyFolder(
referenceElement);
if (componentAssemblyFolder != null) {
StringCollection folderList = new StringCollection();
folderList.Add(componentAssemblyFolder);
resolvedAssemblyFile = ResolveFromFolderList(folderList,
fileName);
}
if (resolvedAssemblyFile == null) {
resolvedAssemblyFile = base.ResolveFromAssemblyFolders(
referenceElement, fileName);
}
return resolvedAssemblyFile;
}
#endregion Private Instance Methods
#region Private Instance Fields
private readonly string _assemblyFile;
private readonly bool _isPrivateSpecified;
private readonly bool _isPrivate;
private readonly string _name = string.Empty;
#endregion Private Instance Fields
}
}