// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
namespace WixToolset.BuildTasks
{
using System;
using System.Collections.Generic;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.IO;
///
/// This task searches for paths to references using the order specified in SearchPaths.
///
public class ResolveWixReferences : Task
{
///
/// Token value used in SearchPaths to indicate that the item's HintPath metadata should
/// be searched as a full file path to resolve the reference.
/// Must match wix.targets, case sensitive.
///
private const string HintPathToken = "{HintPathFromItem}";
///
/// Token value used in SearchPaths to indicate that the item's Identity should
/// be searched as a full file path to resolve the reference.
/// Must match wix.targets, case sensitive.
///
private const string RawFileNameToken = "{RawFileName}";
///
/// The list of references to resolve.
///
[Required]
public ITaskItem[] WixReferences
{
get;
set;
}
///
/// The directories or special locations that are searched to find the files
/// on disk that represent the references. The order in which the search paths are listed
/// is important. For each reference, the list of paths is searched from left to right.
/// When a file that represents the reference is found, that search stops and the search
/// for the next reference starts.
///
/// This parameter accepts the following types of values:
/// A directory path.
/// {HintPathFromItem}: Specifies that the task will examine the HintPath metadata
/// of the base item.
/// TODO : {CandidateAssemblyFiles}: Specifies that the task will examine the files
/// passed in through the CandidateAssemblyFiles parameter.
/// TODO : {Registry:_AssemblyFoldersBase_, _RuntimeVersion_, _AssemblyFoldersSuffix_}:
/// TODO : {AssemblyFolders}: Specifies the task will use the Visual Studio.NET 2003
/// finding-assemblies-from-registry scheme.
/// TODO : {GAC}: Specifies the task will search in the GAC.
/// {RawFileName}: Specifies the task will consider the Include value of the item to be
/// an exact path and file name.
///
public string[] SearchPaths
{
get;
set;
}
///
/// The filename extension(s) to be checked when searching.
///
public string[] SearchFilenameExtensions
{
get;
set;
}
///
/// Output items that contain the same metadata as input references and have been resolved to full paths.
///
[Output]
public ITaskItem[] ResolvedWixReferences
{
get;
private set;
}
///
/// Resolves reference paths by searching for referenced items using the specified SearchPaths.
///
/// True on success, or throws an exception on failure.
public override bool Execute()
{
List resolvedReferences = new List();
foreach (ITaskItem reference in this.WixReferences)
{
ITaskItem resolvedReference = ResolveWixReferences.ResolveReference(reference, this.SearchPaths, this.SearchFilenameExtensions, this.Log);
this.Log.LogMessage(MessageImportance.Low, "Resolved path {0}", resolvedReference.ItemSpec);
resolvedReferences.Add(resolvedReference);
}
this.ResolvedWixReferences = resolvedReferences.ToArray();
return true;
}
///
/// Resolves a single reference item by searcheing for referenced items using the specified SearchPaths.
/// This method is made public so the resolution logic can be reused by other tasks.
///
/// The referenced item.
/// The paths to search.
/// Filename extensions to check.
/// Logging helper.
/// The resolved reference item, or the original reference if it could not be resolved.
public static ITaskItem ResolveReference(ITaskItem reference, string[] searchPaths, string[] searchFilenameExtensions, TaskLoggingHelper log)
{
if (reference == null)
{
throw new ArgumentNullException("reference");
}
if (searchPaths == null)
{
// Nothing to search, so just return the original reference item.
return reference;
}
if (searchFilenameExtensions == null)
{
searchFilenameExtensions = new string[] { };
}
// Copy all the metadata from the source
TaskItem resolvedReference = new TaskItem(reference);
log.LogMessage(MessageImportance.Low, "WixReference: {0}", reference.ItemSpec);
// Now find the resolved path based on our order of precedence
foreach (string searchPath in searchPaths)
{
log.LogMessage(MessageImportance.Low, "Trying {0}", searchPath);
if (searchPath.Equals(HintPathToken, StringComparison.Ordinal))
{
string path = reference.GetMetadata("HintPath");
log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
if (File.Exists(path))
{
resolvedReference.ItemSpec = path;
break;
}
}
else if (searchPath.Equals(RawFileNameToken, StringComparison.Ordinal))
{
log.LogMessage(MessageImportance.Low, "Trying path {0}", resolvedReference.ItemSpec);
if (File.Exists(resolvedReference.ItemSpec))
{
break;
}
if (ResolveWixReferences.ResolveFilenameExtensions(resolvedReference,
resolvedReference.ItemSpec, searchFilenameExtensions, log))
{
break;
}
}
else
{
string path = Path.Combine(searchPath, Path.GetFileName(reference.ItemSpec));
log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
if (File.Exists(path))
{
resolvedReference.ItemSpec = path;
break;
}
if (ResolveWixReferences.ResolveFilenameExtensions(resolvedReference,
path, searchFilenameExtensions, log))
{
break;
}
}
}
// Normalize the item path
resolvedReference.ItemSpec = resolvedReference.GetMetadata("FullPath");
return resolvedReference;
}
///
/// Helper method for checking filename extensions when resolving references.
///
/// The reference being resolved.
/// Full filename path without extension.
/// Filename extensions to check.
/// Logging helper.
/// True if the item was resolved, else false.
private static bool ResolveFilenameExtensions(ITaskItem reference, string basePath, string[] filenameExtensions, TaskLoggingHelper log)
{
foreach (string filenameExtension in filenameExtensions)
{
string path = basePath + filenameExtension;
log.LogMessage(MessageImportance.Low, "Trying path {0}", path);
if (File.Exists(path))
{
reference.ItemSpec = path;
return true;
}
}
return false;
}
}
}