Add support for NuSpecs

This brings over the NuGet.Core code that existed for
loading NuSpecs.  I also added the File list to the
manifest to support builing a package from a
nuspec, and the ability to save a nuspec.
This commit is contained in:
Eric St. John 2016-01-12 10:12:55 -08:00
parent 592fef980c
commit 6e1c39d764
8 changed files with 656 additions and 5 deletions

View file

@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
namespace NuGet
{
/// <summary>
/// Represents an empty framework folder in NuGet 2.0+ packages.
/// An empty framework folder is represented by a file named "_._".
/// </summary>
internal sealed class EmptyFrameworkFolderFile : PhysicalPackageFile
{
public EmptyFrameworkFolderFile(string directoryPathInPackage) :
base(() => Stream.Null)
{
if (directoryPathInPackage == null)
{
throw new ArgumentNullException("directoryPathInPackage");
}
TargetPath = System.IO.Path.Combine(directoryPathInPackage, Constants.PackageEmptyFileName);
}
}
}

View file

@ -2,6 +2,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
@ -24,6 +26,8 @@ namespace NuGet
public ManifestMetadata Metadata { get; }
public ICollection<ManifestFile> Files { get; } = new List<ManifestFile>();
/// <summary>
/// Saves the current manifest to the specified stream.
/// </summary>
@ -53,9 +57,37 @@ namespace NuGet
int version = Math.Max(minimumManifestVersion, ManifestVersionUtility.GetManifestVersion(Metadata));
var schemaNamespace = (XNamespace)ManifestSchemaUtility.GetSchemaNamespace(version);
new XDocument(
var document = new XDocument(
new XElement(schemaNamespace + "package",
Metadata.ToXElement(schemaNamespace))).Save(stream);
Metadata.ToXElement(schemaNamespace)));
var fileElement = Files.ToXElement(schemaNamespace);
if (fileElement != null)
{
document.Root.Add(fileElement);
}
document.Save(stream);
}
public static Manifest ReadFrom(Stream stream)
{
XDocument document = XDocument.Load(stream);
var schemaNamespace = GetSchemaNamespace(document);
return document.Root.ReadManifest(schemaNamespace);
}
private static string GetSchemaNamespace(XDocument document)
{
string schemaNamespace = ManifestSchemaUtility.SchemaVersionV1;
var rootNameSpace = document.Root.Name.Namespace;
if (rootNameSpace != null && !String.IsNullOrEmpty(rootNameSpace.NamespaceName))
{
schemaNamespace = rootNameSpace.NamespaceName;
}
return schemaNamespace;
}
public static Manifest Create(PackageBuilder copy)

View file

@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace NuGet
{
public class ManifestFile
{
public ManifestFile(string source, string target, string exclude)
{
Source = source;
Target = target;
Exclude = exclude;
}
public string Source { get; }
public string Target { get; }
public string Exclude { get; }
}
}

View file

@ -61,6 +61,8 @@ namespace NuGet
public bool RequireLicenseAcceptance { get; set; }
public bool DevelopmentDependency { get; set; }
public string Description { get; set; }
public string Summary { get; set; }

View file

@ -19,6 +19,7 @@ namespace NuGet
{
private const string DefaultContentType = "application/octet";
internal const string ManifestRelationType = "manifest";
private bool _includeEmptyDirectories = false;
public PackageBuilder()
{
@ -264,6 +265,43 @@ namespace NuGet
}
}
public void Populate(ManifestMetadata manifestMetadata)
{
Id = manifestMetadata.Id;
Version = manifestMetadata.Version;
Title = manifestMetadata.Title;
AppendIfNotNull(Authors, manifestMetadata.Authors);
AppendIfNotNull(Owners, manifestMetadata.Owners);
IconUrl = manifestMetadata.IconUrl;
LicenseUrl = manifestMetadata.LicenseUrl;
ProjectUrl = manifestMetadata.ProjectUrl;
RequireLicenseAcceptance = manifestMetadata.RequireLicenseAcceptance;
DevelopmentDependency = manifestMetadata.DevelopmentDependency;
Description = manifestMetadata.Description;
Summary = manifestMetadata.Summary;
ReleaseNotes = manifestMetadata.ReleaseNotes;
Language = manifestMetadata.Language;
Copyright = manifestMetadata.Copyright;
MinClientVersion = manifestMetadata.MinClientVersion;
if (manifestMetadata.Tags != null)
{
Tags.AddRange(ParseTags(manifestMetadata.Tags));
}
AppendIfNotNull(DependencySets, manifestMetadata.DependencySets);
AppendIfNotNull(FrameworkAssemblies, manifestMetadata.FrameworkAssemblies);
AppendIfNotNull(PackageAssemblyReferences, manifestMetadata.PackageAssemblyReferences);
}
public void PopulateFiles(string basePath, IEnumerable<ManifestFile> files)
{
foreach (var file in files)
{
AddFiles(basePath, file.Source, file.Target, file.Exclude);
}
}
private void WriteManifest(ZipArchive package, int minimumManifestVersion)
{
string path = Id + Constants.ManifestExtension;
@ -310,6 +348,39 @@ namespace NuGet
return extensions;
}
private void AddFiles(string basePath, string source, string destination, string exclude = null)
{
List<PhysicalPackageFile> searchFiles = PathResolver.ResolveSearchPattern(basePath, source, destination, _includeEmptyDirectories).ToList();
ExcludeFiles(searchFiles, basePath, exclude);
if (!PathResolver.IsWildcardSearch(source) && !PathResolver.IsDirectoryPath(source) && !searchFiles.Any())
{
// TODO: Resources
throw new FileNotFoundException(
String.Format(CultureInfo.CurrentCulture, "NuGetResources.PackageAuthoring_FileNotFound {0}", source));
}
Files.AddRange(searchFiles);
}
private static void ExcludeFiles(List<PhysicalPackageFile> searchFiles, string basePath, string exclude)
{
if (String.IsNullOrEmpty(exclude))
{
return;
}
// One or more exclusions may be specified in the file. Split it and prepend the base path to the wildcard provided.
var exclusions = exclude.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in exclusions)
{
string wildCard = PathResolver.NormalizeWildcardForExcludedFiles(basePath, item);
PathResolver.FilterPackageFiles(searchFiles, p => p.SourcePath, new[] { wildCard });
}
}
private static void CreatePart(ZipArchive package, string path, Stream sourceStream)
{
if (PackageHelper.IsManifest(path))
@ -324,6 +395,15 @@ namespace NuGet
}
}
/// <summary>
/// Tags come in this format. tag1 tag2 tag3 etc..
/// </summary>
private static IEnumerable<string> ParseTags(string tags)
{
return from tag in tags.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
select tag.Trim();
}
private static bool IsPrereleaseDependency(PackageDependency dependency)
{
return dependency.VersionRange.MinVersion?.IsPrerelease == true ||
@ -340,7 +420,7 @@ namespace NuGet
return version == null || version.Release.Length <= 20;
}
private void WriteOpcManifestRelationship(ZipArchive package, string path)
private static void WriteOpcManifestRelationship(ZipArchive package, string path)
{
ZipArchiveEntry relsEntry = package.CreateEntry("_rels/.rels", CompressionLevel.Optimal);
@ -374,9 +454,17 @@ namespace NuGet
}
// Generate a relationship id for compatibility
private string GenerateRelationshipId()
private static string GenerateRelationshipId()
{
return "R" + Guid.NewGuid().ToString("N").Substring(0, 16);
}
private static void AppendIfNotNull<T>(List<T> collection, IEnumerable<T> toAdd)
{
if (toAdd != null)
{
collection.AddRange(toAdd);
}
}
}
}

View file

@ -7,6 +7,7 @@ using System.Linq;
using System.Xml.Linq;
using NuGet.Frameworks;
using NuGet.Packaging.Core;
using NuGet.Versioning;
namespace NuGet
{
@ -21,6 +22,7 @@ namespace NuGet
private const string FrameworkAssembly = "frameworkAssembly";
private const string AssemblyName = "assemblyName";
private const string Dependencies = "dependencies";
private const string Files = "files";
public static XElement ToXElement(this ManifestMetadata metadata, XNamespace ns)
{
@ -34,6 +36,7 @@ namespace NuGet
elem.Add(new XElement(ns + "version", metadata.Version.ToString()));
AddElementIfNotNull(elem, ns, "title", metadata.Title);
elem.Add(new XElement(ns + "requireLicenseAcceptance", metadata.RequireLicenseAcceptance));
elem.Add(new XElement(ns + "developmentDependency", metadata.DevelopmentDependency));
AddElementIfNotNull(elem, ns, "authors", metadata.Authors, authors => string.Join(",", authors));
AddElementIfNotNull(elem, ns, "owners", metadata.Owners, owners => string.Join(",", owners));
AddElementIfNotNull(elem, ns, "licenseUrl", metadata.LicenseUrl);
@ -71,6 +74,78 @@ namespace NuGet
return elem;
}
public static Manifest ReadManifest(this XElement element, XNamespace ns)
{
if (element.Name != ns + "package")
{
return null;
}
var metadataElement = element.Element(ns + "metadata");
if (metadataElement == null)
{
return null;
}
ManifestMetadata metadata = new ManifestMetadata();
metadata.MinClientVersionString = metadataElement.Attribute("minClientVersion")?.Value;
metadata.Id = metadataElement.Element(ns + "id")?.Value;
metadata.Version = ConvertIfNotNull(metadataElement.Element(ns + "version")?.Value, s => new NuGetVersion(s));
metadata.Title = metadataElement.Element(ns + "title")?.Value;
metadata.RequireLicenseAcceptance = ConvertIfNotNull(metadataElement.Element(ns + "requireLicenseAcceptance")?.Value, s => bool.Parse(s));
metadata.DevelopmentDependency = ConvertIfNotNull(metadataElement.Element(ns + "developmentDependency")?.Value, s => bool.Parse(s));
metadata.Authors = ConvertIfNotNull(metadataElement.Element(ns + "authors")?.Value, s => s.Split(','));
metadata.Owners = ConvertIfNotNull(metadataElement.Element(ns + "owners")?.Value, s => s.Split(','));
metadata.LicenseUrl = ConvertIfNotNull(metadataElement.Element(ns + "licenseUrl")?.Value, s => new Uri(s));
metadata.ProjectUrl = ConvertIfNotNull(metadataElement.Element(ns + "projectUrl")?.Value, s => new Uri(s));
metadata.IconUrl = ConvertIfNotNull(metadataElement.Element(ns + "iconUrl")?.Value, s => new Uri(s));
metadata.Description = metadataElement.Element(ns + "description")?.Value;
metadata.Summary = metadataElement.Element(ns + "summary")?.Value;
metadata.ReleaseNotes = metadataElement.Element(ns + "releaseNotes")?.Value;
metadata.Copyright = metadataElement.Element(ns + "copyright")?.Value;
metadata.Language = metadataElement.Element(ns + "language")?.Value;
metadata.Tags = metadataElement.Element(ns + "tags")?.Value;
metadata.DependencySets = GetItemSetsFromGroupableXElements(
ns,
metadataElement,
Dependencies,
"dependency",
TargetFramework,
GetPackageDependencyFromXElement,
(tfm, deps) => new PackageDependencySet(tfm, deps));
metadata.PackageAssemblyReferences = GetItemSetsFromGroupableXElements(
ns,
metadataElement,
References,
Reference,
TargetFramework,
GetPackageReferenceFromXElement,
(tfm, refs) => new PackageReferenceSet(tfm, refs)).ToArray();
metadata.FrameworkAssemblies = GetFrameworkAssembliesFromXElement(ns, metadataElement);
Manifest manifest = new Manifest(metadata);
var files = GetManifestFilesFromXElement(ns, element);
if (files != null)
{
foreach(var file in files)
{
manifest.Files.Add(file);
}
}
return manifest;
}
public static XElement ToXElement(this IEnumerable<ManifestFile> fileList, XNamespace ns)
{
return GetXElementFromManifestFiles(ns, fileList);
}
private static XElement GetXElementFromGroupableItemSets<TSet, TItem>(
XNamespace ns,
IEnumerable<TSet> objectSets,
@ -128,11 +203,45 @@ namespace NuGet
return new XElement(ns + parentName, childElements.ToArray());
}
private static IEnumerable<TSet> GetItemSetsFromGroupableXElements<TSet, TItem>(
XNamespace ns,
XElement parent,
string rootName,
string elementName,
string identifierAttributeName,
Func<XElement, TItem> getItemFromXElement,
Func<string, IEnumerable<TItem>, TSet> getItemSet)
{
XElement rootElement = parent.Element(ns + rootName);
if (rootElement == null)
{
return Enumerable.Empty<TSet>();
}
var groups = rootElement.Elements(ns + Group);
if (groups == null || !groups.Any())
{
// no groupable sets, all are ungroupable
return new[] { getItemSet(null, rootElement.Elements(ns + elementName).Select(e => getItemFromXElement(e))) };
}
return groups.Select(g =>
getItemSet(g.Attribute(identifierAttributeName)?.Value,
g.Elements(ns + elementName).Select(e => getItemFromXElement(e))));
}
private static XElement GetXElementFromPackageReference(XNamespace ns, string reference)
{
return new XElement(ns + Reference, new XAttribute(File, reference));
}
private static string GetPackageReferenceFromXElement(XElement element)
{
return element.Attribute(File)?.Value;
}
private static XElement GetXElementFromPackageDependency(XNamespace ns, PackageDependency dependency)
{
return new XElement(ns + "dependency",
@ -140,6 +249,12 @@ namespace NuGet
dependency.VersionRange != null ? new XAttribute("version", dependency.VersionRange.ToString()) : null);
}
private static PackageDependency GetPackageDependencyFromXElement(XElement element)
{
return new PackageDependency(element.Attribute("id").Value,
ConvertIfNotNull(element.Attribute("version")?.Value, s => VersionRange.Parse(s)));
}
private static XElement GetXElementFromFrameworkAssemblies(XNamespace ns, IEnumerable<FrameworkAssemblyReference> references)
{
if (references == null || !references.Any())
@ -157,6 +272,53 @@ namespace NuGet
null)));
}
private static IEnumerable<FrameworkAssemblyReference> GetFrameworkAssembliesFromXElement(XNamespace ns, XElement parent)
{
var frameworkAssembliesElement = parent.Element(ns + FrameworkAssemblies);
if (frameworkAssembliesElement == null)
{
return null;
}
return frameworkAssembliesElement.Elements(ns + FrameworkAssembly).Select(e =>
new FrameworkAssemblyReference(e.Attribute(AssemblyName).Value,
e.Attribute("targetFramework")?.Value?.Split(',')?
.Select(tf => NuGetFramework.Parse(tf)) ?? Enumerable.Empty<NuGetFramework>()));
}
private static XElement GetXElementFromManifestFiles(XNamespace ns, IEnumerable<ManifestFile> files)
{
if (files == null || !files.Any())
{
return null;
}
return new XElement(ns + Files,
files.Select(file =>
new XElement(ns + File,
new XAttribute("src", file.Source),
new XAttribute("target", file.Source),
new XAttribute("exclude", file.Exclude)
)));
}
private static IEnumerable<ManifestFile> GetManifestFilesFromXElement(XNamespace ns, XElement parent)
{
var filesElement = parent.Element(ns + Files);
if (filesElement == null)
{
return null;
}
return filesElement.Elements(ns + File).Select(f =>
new ManifestFile(f.Attribute("src").Value,
f.Attribute("target").Value,
f.Attribute("exclude").Value));
}
private static void AddElementIfNotNull<T>(XElement parent, XNamespace ns, string name, T value)
where T : class
{
@ -178,5 +340,16 @@ namespace NuGet
}
}
}
private static TDest ConvertIfNotNull<TDest, TSource>(TSource value, Func<TSource, TDest> convert)
{
if (value != null)
{
var converted = convert(value);
return converted;
}
return default(TDest);
}
}
}

View file

@ -11,7 +11,12 @@ namespace NuGet
public class PackageReferenceSet
{
public PackageReferenceSet(IEnumerable<string> references)
: this(null, references)
: this((NuGetFramework)null, references)
{
}
public PackageReferenceSet(string targetFramework, IEnumerable<string> references)
: this(targetFramework != null ? NuGetFramework.Parse(targetFramework) : null, references)
{
}

View file

@ -0,0 +1,304 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace NuGet
{
public static class PathResolver
{
/// <summary>
/// Returns a collection of files from the source that matches the wildcard.
/// </summary>
/// <param name="source">The collection of files to match.</param>
/// <param name="getPath">Function that returns the path to filter a package file </param>
/// <param name="wildcards">The wildcards to apply to match the path with.</param>
/// <returns></returns>
public static IEnumerable<T> GetMatches<T>(IEnumerable<T> source, Func<T, string> getPath, IEnumerable<string> wildcards)
{
var filters = wildcards.Select(WildcardToRegex);
return source.Where(item =>
{
string path = getPath(item);
return filters.Any(f => f.IsMatch(path));
});
}
/// <summary>
/// Removes files from the source that match any wildcard.
/// </summary>
public static void FilterPackageFiles<T>(ICollection<T> source, Func<T, string> getPath, IEnumerable<string> wildcards)
{
var matchedFiles = new HashSet<T>(GetMatches(source, getPath, wildcards));
var itemsToRemove = source.Where(matchedFiles.Contains).ToArray();
foreach (var item in itemsToRemove)
{
source.Remove(item);
}
}
public static string NormalizeWildcardForExcludedFiles(string basePath, string wildcard)
{
if (wildcard.StartsWith("**", StringComparison.OrdinalIgnoreCase))
{
// Allow any path to match the first '**' segment, see issue 2891 for more details.
return wildcard;
}
basePath = NormalizeBasePath(basePath, ref wildcard);
return Path.Combine(basePath, wildcard);
}
private static Regex WildcardToRegex(string wildcard)
{
var pattern = Regex.Escape(wildcard);
if (Path.DirectorySeparatorChar == '/')
{
// regex wildcard adjustments for *nix-style file systems
pattern = pattern
.Replace(@"\*\*/", ".*") //For recursive wildcards /**/, include the current directory.
.Replace(@"\*\*", ".*") // For recursive wildcards that don't end in a slash e.g. **.txt would be treated as a .txt file at any depth
.Replace(@"\*", @"[^/]*(/)?") // For non recursive searches, limit it any character that is not a directory separator
.Replace(@"\?", "."); // ? translates to a single any character
}
else
{
// regex wildcard adjustments for Windows-style file systems
pattern = pattern
.Replace("/", @"\\") // On Windows, / is treated the same as \.
.Replace(@"\*\*\\", ".*") //For recursive wildcards \**\, include the current directory.
.Replace(@"\*\*", ".*") // For recursive wildcards that don't end in a slash e.g. **.txt would be treated as a .txt file at any depth
.Replace(@"\*", @"[^\\]*(\\)?") // For non recursive searches, limit it any character that is not a directory separator
.Replace(@"\?", "."); // ? translates to a single any character
}
return new Regex('^' + pattern + '$', RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
}
internal static IEnumerable<PhysicalPackageFile> ResolveSearchPattern(string basePath, string searchPath, string targetPath, bool includeEmptyDirectories)
{
string normalizedBasePath;
IEnumerable<SearchPathResult> searchResults = PerformWildcardSearchInternal(basePath, searchPath, includeEmptyDirectories, out normalizedBasePath);
return searchResults.Select(result =>
result.IsFile
? new PhysicalPackageFile
{
SourcePath = result.Path,
TargetPath = ResolvePackagePath(normalizedBasePath, searchPath, result.Path, targetPath)
}
: new EmptyFrameworkFolderFile(ResolvePackagePath(normalizedBasePath, searchPath, result.Path, targetPath))
{
SourcePath = result.Path
}
);
}
public static IEnumerable<string> PerformWildcardSearch(string basePath, string searchPath)
{
string normalizedBasePath;
var searchResults = PerformWildcardSearchInternal(basePath, searchPath, includeEmptyDirectories: false, normalizedBasePath: out normalizedBasePath);
return searchResults.Select(s => s.Path);
}
private static IEnumerable<SearchPathResult> PerformWildcardSearchInternal(string basePath, string searchPath, bool includeEmptyDirectories, out string normalizedBasePath)
{
if (!searchPath.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase)
&& Path.DirectorySeparatorChar != '/')
{
//If the system's DirectorySeparatorChar is '/' we're probably dealing with Mac or *nix
// In any case, if '/' is the separator, we don't want to trim off the first char ever
// since it would completely change the meaning of the path
// eg: /var/somedir/ is not at all the same as var/somedir (relative vs absolute)
// If we aren't dealing with network paths, trim the leading slash.
searchPath = searchPath.TrimStart(Path.DirectorySeparatorChar);
}
bool searchDirectory = false;
// If the searchPath ends with \ or /, we treat searchPath as a directory,
// and will include everything under it, recursively
if (IsDirectoryPath(searchPath))
{
searchPath = searchPath + "**" + Path.DirectorySeparatorChar + "*";
searchDirectory = true;
}
basePath = NormalizeBasePath(basePath, ref searchPath);
normalizedBasePath = GetPathToEnumerateFrom(basePath, searchPath);
// Append the basePath to searchPattern and get the search regex. We need to do this because the search regex is matched from line start.
Regex searchRegex = WildcardToRegex(Path.Combine(basePath, searchPath));
// This is a hack to prevent enumerating over the entire directory tree if the only wildcard characters are the ones in the file name.
// If the path portion of the search path does not contain any wildcard characters only iterate over the TopDirectory.
SearchOption searchOption = SearchOption.AllDirectories;
// (a) Path is not recursive search
bool isRecursiveSearch = searchPath.IndexOf("**", StringComparison.OrdinalIgnoreCase) != -1;
// (b) Path does not have any wildcards.
bool isWildcardPath = Path.GetDirectoryName(searchPath).Contains('*');
if (!isRecursiveSearch && !isWildcardPath)
{
searchOption = SearchOption.TopDirectoryOnly;
}
// Starting from the base path, enumerate over all files and match it using the wildcard expression provided by the user.
// Note: We use Directory.GetFiles() instead of Directory.EnumerateFiles() here to support Mono
var matchedFiles = from file in Directory.GetFiles(normalizedBasePath, "*.*", searchOption)
where searchRegex.IsMatch(file)
select new SearchPathResult(file, isFile: true);
if (!includeEmptyDirectories)
{
return matchedFiles;
}
// retrieve empty directories
// Note: We use Directory.GetDirectories() instead of Directory.EnumerateDirectories() here to support Mono
var matchedDirectories = from directory in Directory.GetDirectories(normalizedBasePath, "*.*", searchOption)
where searchRegex.IsMatch(directory) && IsEmptyDirectory(directory)
select new SearchPathResult(directory, isFile: false);
if (searchDirectory && IsEmptyDirectory(normalizedBasePath))
{
matchedDirectories = matchedDirectories.Concat(new [] { new SearchPathResult(normalizedBasePath, isFile: false) });
}
return matchedFiles.Concat(matchedDirectories);
}
internal static string GetPathToEnumerateFrom(string basePath, string searchPath)
{
string basePathToEnumerate;
int wildcardIndex = searchPath.IndexOf('*');
if (wildcardIndex == -1)
{
// For paths without wildcard, we could either have base relative paths (such as lib\foo.dll) or paths outside the base path
// (such as basePath: C:\packages and searchPath: D:\packages\foo.dll)
// In this case, Path.Combine would pick up the right root to enumerate from.
var searchRoot = Path.GetDirectoryName(searchPath);
basePathToEnumerate = Path.Combine(basePath, searchRoot);
}
else
{
// If not, find the first directory separator and use the path to the left of it as the base path to enumerate from.
int directorySeparatoryIndex = searchPath.LastIndexOf(Path.DirectorySeparatorChar, wildcardIndex);
if (directorySeparatoryIndex == -1)
{
// We're looking at a path like "NuGet*.dll", NuGet*\bin\release\*.dll
// In this case, the basePath would continue to be the path to begin enumeration from.
basePathToEnumerate = basePath;
}
else
{
string nonWildcardPortion = searchPath.Substring(0, directorySeparatoryIndex);
basePathToEnumerate = Path.Combine(basePath, nonWildcardPortion);
}
}
return basePathToEnumerate;
}
/// <summary>
/// Determins the path of the file inside a package.
/// For recursive wildcard paths, we preserve the path portion beginning with the wildcard.
/// For non-recursive wildcard paths, we use the file name from the actual file path on disk.
/// </summary>
internal static string ResolvePackagePath(string searchDirectory, string searchPattern, string fullPath, string targetPath)
{
string packagePath;
bool isDirectorySearch = IsDirectoryPath(searchPattern);
bool isWildcardSearch = IsWildcardSearch(searchPattern);
bool isRecursiveWildcardSearch = isWildcardSearch && searchPattern.IndexOf("**", StringComparison.OrdinalIgnoreCase) != -1;
if ((isRecursiveWildcardSearch || isDirectorySearch) && fullPath.StartsWith(searchDirectory, StringComparison.OrdinalIgnoreCase))
{
// The search pattern is recursive. Preserve the non-wildcard portion of the path.
// e.g. Search: X:\foo\**\*.cs results in SearchDirectory: X:\foo and a file path of X:\foo\bar\biz\boz.cs
// Truncating X:\foo\ would result in the package path.
packagePath = fullPath.Substring(searchDirectory.Length).TrimStart(Path.DirectorySeparatorChar);
}
else if (!isWildcardSearch && Path.GetExtension(searchPattern).Equals(Path.GetExtension(targetPath), StringComparison.OrdinalIgnoreCase))
{
// If the search does not contain wild cards, and the target path shares the same extension, copy it
// e.g. <file src="ie\css\style.css" target="Content\css\ie.css" /> --> Content\css\ie.css
return targetPath;
}
else
{
packagePath = Path.GetFileName(fullPath);
}
return Path.Combine(targetPath ?? String.Empty, packagePath);
}
private static readonly string OneDotSlash = "." + Path.DirectorySeparatorChar;
private static readonly string TwoDotSlash = ".." + Path.DirectorySeparatorChar;
internal static string NormalizeBasePath(string basePath, ref string searchPath)
{
// If no base path is provided, use the current directory.
basePath = String.IsNullOrEmpty(basePath) ? OneDotSlash : basePath;
// If the search path is relative, transfer the ..\ portion to the base path.
// This needs to be done because the base path determines the root for our enumeration.
while (searchPath.StartsWith(TwoDotSlash, StringComparison.OrdinalIgnoreCase))
{
basePath = Path.Combine(basePath, TwoDotSlash);
searchPath = searchPath.Substring(TwoDotSlash.Length);
}
return Path.GetFullPath(basePath);
}
/// <summary>
/// Returns true if the path contains any wildcard characters.
/// </summary>
internal static bool IsWildcardSearch(string filter)
{
return filter.IndexOf('*') != -1;
}
internal static bool IsDirectoryPath(string path)
{
return path != null && path.Length > 1 &&
(path[path.Length - 1] == Path.DirectorySeparatorChar ||
path[path.Length - 1] == Path.AltDirectorySeparatorChar);
}
private static bool IsEmptyDirectory(string directory)
{
return !Directory.EnumerateFileSystemEntries(directory).Any();
}
private struct SearchPathResult
{
private readonly string _path;
private readonly bool _isFile;
public string Path
{
get
{
return _path;
}
}
public bool IsFile
{
get
{
return _isFile;
}
}
public SearchPathResult(string path, bool isFile)
{
_path = path;
_isFile = isFile;
}
}
}
}