Merge pull request #1347 from dotnet/pakrym/refasm

Add reference assembly support to DependencyModel
This commit is contained in:
Pavel Krymets 2016-02-10 13:55:25 -08:00
commit 23b1b077dd
6 changed files with 292 additions and 139 deletions

View file

@ -60,7 +60,7 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
private void MakeCompilationOutputRunnableForFullFramework(
string outputPath)
{
CopyAllDependencies(outputPath, _exporter);
CopyAllDependencies(outputPath, _exporter.GetAllExports());
GenerateBindingRedirects(_exporter);
}
@ -78,17 +78,14 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
contentFiles.StructuredCopyTo(outputPath);
}
private static void CopyAllDependencies(string outputPath, LibraryExporter exporter)
private static void CopyAllDependencies(string outputPath, IEnumerable<LibraryExport> libraryExports)
{
var libraryExports = exporter.GetAllExports();
libraryExports
.SelectMany(e => e.RuntimeAssemblies)
.CopyTo(outputPath);
libraryExports
.SelectMany(RuntimeAssets)
.StructuredCopyTo(outputPath);
foreach (var libraryExport in libraryExports)
{
libraryExport.RuntimeAssemblies.CopyTo(outputPath);
libraryExport.NativeLibraries.CopyTo(outputPath);
libraryExport.RuntimeAssets.StructuredCopyTo(outputPath);
}
}
private static void WriteDepsFileAndCopyProjectDependencies(
@ -104,20 +101,7 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
.Where(e => e.Library.Identity.Type == LibraryType.Project)
.ToArray();
projectExports
.SelectMany(e => e.RuntimeAssemblies)
.CopyTo(outputPath);
projectExports
.SelectMany(RuntimeAssets)
.StructuredCopyTo(outputPath);
}
private static IEnumerable<LibraryAsset> RuntimeAssets(LibraryExport export)
{
return export.NativeLibraries
.Union(export.RuntimeAssets);
CopyAllDependencies(outputPath, projectExports);
}
public void GenerateBindingRedirects(LibraryExporter exporter)

View file

@ -2,10 +2,13 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Resolution;
using Microsoft.DotNet.ProjectModel.Utilities;
using NuGet.Frameworks;
namespace Microsoft.Extensions.DependencyModel
@ -61,7 +64,7 @@ namespace Microsoft.Extensions.DependencyModel
bool runtime,
IDictionary<string, Dependency> dependencyLookup)
{
var type = export.Library.Identity.Type.Value.ToLowerInvariant();
var type = export.Library.Identity.Type;
var serviceable = (export.Library as PackageDescription)?.Library.IsServiceable ?? false;
var libraryDependencies = new List<Dependency>();
@ -82,7 +85,7 @@ namespace Microsoft.Extensions.DependencyModel
}
string[] assemblies;
if (type == "project")
if (type == LibraryType.Project)
{
var isExe = ((ProjectDescription)export.Library)
.Project
@ -94,6 +97,10 @@ namespace Microsoft.Extensions.DependencyModel
assemblies = new[] { export.Library.Identity.Name + (isExe ? ".exe" : ".dll") };
}
else if (type == LibraryType.ReferenceAssembly)
{
assemblies = ResolveReferenceAssembliesPath(libraryAssets);
}
else
{
assemblies = libraryAssets.Select(libraryAsset => libraryAsset.RelativePath).ToArray();
@ -102,7 +109,7 @@ namespace Microsoft.Extensions.DependencyModel
if (runtime)
{
return new RuntimeLibrary(
type,
type.ToString().ToLowerInvariant(),
export.Library.Identity.Name,
export.Library.Identity.Version.ToString(),
export.Library.Hash,
@ -114,7 +121,7 @@ namespace Microsoft.Extensions.DependencyModel
else
{
return new CompilationLibrary(
type,
type.ToString().ToLowerInvariant(),
export.Library.Identity.Name,
export.Library.Identity.Version.ToString(),
export.Library.Hash,
@ -124,5 +131,26 @@ namespace Microsoft.Extensions.DependencyModel
);
}
}
private static string[] ResolveReferenceAssembliesPath(IEnumerable<LibraryAsset> libraryAssets)
{
var resolvedPaths = new List<string>();
var referenceAssembliesPath =
PathUtility.EnsureTrailingSlash(FrameworkReferenceResolver.Default.ReferenceAssembliesPath);
foreach (var libraryAsset in libraryAssets)
{
// If resolved path is under ReferenceAssembliesPath store it as a relative to it
// if not, save only assembly name and try to find it somehow later
if (libraryAsset.ResolvedPath.StartsWith(referenceAssembliesPath))
{
resolvedPaths.Add(libraryAsset.ResolvedPath.Substring(referenceAssembliesPath.Length));
}
else
{
resolvedPaths.Add(Path.GetFileName(libraryAsset.ResolvedPath));
}
}
return resolvedPaths.ToArray();
}
}
}

View file

@ -11,12 +11,6 @@ namespace Microsoft.Extensions.DependencyModel
{
public class CompilationLibrary : Library
{
private static Lazy<Assembly> _entryAssembly = new Lazy<Assembly>(GetEntryAssembly);
private static string _nugetPackages = Environment.GetEnvironmentVariable("NUGET_PACKAGES") ?? GetDefaultPackageDirectory();
private static string _packageCache = Environment.GetEnvironmentVariable("DOTNET_PACKAGES_CACHE");
public CompilationLibrary(string libraryType, string packageName, string version, string hash, string[] assemblies, Dependency[] dependencies, bool serviceable)
: base(libraryType, packageName, version, hash, dependencies, serviceable)
{
@ -27,56 +21,90 @@ namespace Microsoft.Extensions.DependencyModel
public IEnumerable<string> ResolveReferencePaths()
{
var entryAssembly = _entryAssembly.Value;
var entryAssembly = Assembly.GetEntryAssembly();
string basePath;
string fullName;
var appBase = Path.GetDirectoryName(entryAssembly.Location);
var refsDir = Path.Combine(appBase, "refs");
var hasRefs = Directory.Exists(refsDir);
var isProject = string.Equals(LibraryType, "project", StringComparison.OrdinalIgnoreCase);
var isReferenceAssembly = string.Equals(LibraryType, "referenceassembly", StringComparison.OrdinalIgnoreCase);
if (!isProject && PackagePathResolver.TryResolvePackageCachePath(this, out basePath))
{
return ResolveFromPackagePath(basePath);
}
if (hasRefs || isProject)
{
foreach (var assembly in Assemblies)
var directories = new List<string>()
{
var assemblyFile = Path.GetFileName(assembly);
if (hasRefs && TryResolveAssemblyFile(refsDir, assemblyFile, out fullName))
{
yield return fullName;
}
else if (TryResolveAssemblyFile(appBase, assemblyFile, out fullName))
{
yield return fullName;
}
else
{
var errorMessage = $"Can not find assembly file {assemblyFile} at '{appBase}'";
if (hasRefs)
{
errorMessage += $", '{refsDir}'";
}
throw new InvalidOperationException(errorMessage);
}
appBase
};
if (hasRefs)
{
directories.Add(refsDir);
}
yield break;
return ResolveFromDirectories(directories.ToArray());
}
else if (TryResolvePackagePath(out basePath))
if (isReferenceAssembly)
{
foreach (var assembly in Assemblies)
{
if (!TryResolveAssemblyFile(basePath, assembly, out fullName))
{
throw new InvalidOperationException($"Can not find assembly file at '{fullName}'");
}
yield return fullName;
}
yield break;
return ResolveFromReferenceAssemblies();
}
if (PackagePathResolver.TryResolvePackagePath(this, out basePath))
{
return ResolveFromPackagePath(basePath);
}
throw new InvalidOperationException($"Can not find compilation library location for package '{PackageName}'");
}
private IEnumerable<string> ResolveFromPackagePath(string basePath)
{
foreach (var assembly in Assemblies)
{
string fullName;
if (!TryResolveAssemblyFile(basePath, assembly, out fullName))
{
throw new InvalidOperationException($"Can not find assembly file for package {PackageName} at '{fullName}'");
}
yield return fullName;
}
}
private IEnumerable<string> ResolveFromReferenceAssemblies()
{
foreach (var assembly in Assemblies)
{
string fullName;
if (!ReferenceAssemblyPathResolver.TryResolveReferenceAssembly(assembly, out fullName))
{
throw new InvalidOperationException($"Can not find refernce assembly file for package {PackageName}: '{assembly}'");
}
yield return fullName;
}
}
private IEnumerable<string> ResolveFromDirectories(string[] directories)
{
foreach (var assembly in Assemblies)
{
var assemblyFile = Path.GetFileName(assembly);
foreach (var directory in directories)
{
string fullName;
if (TryResolveAssemblyFile(directory, assemblyFile, out fullName))
{
yield return fullName;
break;
}
var errorMessage = $"Can not find assembly file {assemblyFile} at '{string.Join(",", directories)}'";
throw new InvalidOperationException(errorMessage);
}
}
}
private bool TryResolveAssemblyFile(string basePath, string assemblyPath, out string fullName)
{
fullName = Path.Combine(basePath, assemblyPath);
@ -86,76 +114,5 @@ namespace Microsoft.Extensions.DependencyModel
}
return false;
}
private bool TryResolvePackagePath(out string packagePath)
{
packagePath = null;
if (!string.IsNullOrEmpty(_packageCache))
{
var hashSplitterPos = Hash.IndexOf('-');
if (hashSplitterPos <= 0 || hashSplitterPos == Hash.Length - 1)
{
throw new InvalidOperationException($"Invalid hash entry '{Hash}' for package '{PackageName}'");
}
var hashAlgorithm = Hash.Substring(0, hashSplitterPos);
var cacheHashPath = Path.Combine(_packageCache, $"{PackageName}.{Version}.nupkg.{hashAlgorithm}");
if (File.Exists(cacheHashPath) &&
File.ReadAllText(cacheHashPath) == Hash.Substring(hashSplitterPos + 1))
{
if (TryResolvePackagePath(_nugetPackages, out packagePath))
{
return true;
}
}
}
if (!string.IsNullOrEmpty(_nugetPackages) &&
TryResolvePackagePath(_nugetPackages, out packagePath))
{
return true;
}
return false;
}
private bool TryResolvePackagePath(string basePath, out string packagePath)
{
packagePath = Path.Combine(basePath, PackageName, Version);
if (Directory.Exists(packagePath))
{
return true;
}
return false;
}
private static string GetDefaultPackageDirectory()
{
string basePath;
if (PlatformServices.Default.Runtime.OperatingSystemPlatform == Platform.Windows)
{
basePath = Environment.GetEnvironmentVariable("USERPROFILE");
}
else
{
basePath = Environment.GetEnvironmentVariable("HOME");
}
if (string.IsNullOrEmpty(basePath))
{
return null;
}
return Path.Combine(basePath, ".nuget", "packages");
}
private static Assembly GetEntryAssembly()
{
var entryAssembly = (Assembly)typeof(Assembly).GetTypeInfo().GetDeclaredMethod("GetEntryAssembly").Invoke(null, null);
if (entryAssembly == null)
{
throw new InvalidOperationException("Could not determine entry assembly");
}
return entryAssembly;
}
}
}

View file

@ -16,7 +16,7 @@ namespace Microsoft.Extensions.DependencyModel
{
using (var writer = new StreamWriter(stream))
{
using (var jsonWriter = new JsonTextWriter(writer))
using (var jsonWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented })
{
Write(context).WriteTo(jsonWriter);
}

View file

@ -0,0 +1,84 @@
// 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;
using Microsoft.Extensions.PlatformAbstractions;
namespace Microsoft.Extensions.DependencyModel
{
public class PackagePathResolver
{
private static string _nugetPackages = Environment.GetEnvironmentVariable("NUGET_PACKAGES") ?? GetDefaultPackageDirectory();
private static string _packageCache = Environment.GetEnvironmentVariable("DOTNET_PACKAGES_CACHE");
internal static bool TryResolvePackageCachePath(CompilationLibrary library, out string packagePath)
{
packagePath = null;
if (!string.IsNullOrEmpty(_packageCache))
{
var hashSplitterPos = library.Hash.IndexOf('-');
if (hashSplitterPos <= 0 || hashSplitterPos == library.Hash.Length - 1)
{
throw new InvalidOperationException($"Invalid hash entry '{library.Hash}' for package '{library.PackageName}'");
}
var hashAlgorithm = library.Hash.Substring(0, hashSplitterPos);
var cacheHashPath = Path.Combine(_packageCache, $"{library.PackageName}.{library.Version}.nupkg.{hashAlgorithm}");
if (File.Exists(cacheHashPath) &&
File.ReadAllText(cacheHashPath) == library.Hash.Substring(hashSplitterPos + 1))
{
if (TryResolvePackagePath(library, _nugetPackages, out packagePath))
{
return true;
}
}
}
return false;
}
internal static bool TryResolvePackagePath(CompilationLibrary library, out string packagePath)
{
packagePath = null;
if (!string.IsNullOrEmpty(_nugetPackages) &&
TryResolvePackagePath(library, _nugetPackages, out packagePath))
{
return true;
}
return false;
}
private static string GetDefaultPackageDirectory()
{
string basePath;
if (PlatformServices.Default.Runtime.OperatingSystemPlatform == Platform.Windows)
{
basePath = Environment.GetEnvironmentVariable("USERPROFILE");
}
else
{
basePath = Environment.GetEnvironmentVariable("HOME");
}
if (string.IsNullOrEmpty(basePath))
{
return null;
}
return Path.Combine(basePath, ".nuget", "packages");
}
private static bool TryResolvePackagePath(CompilationLibrary library, string basePath, out string packagePath)
{
packagePath = Path.Combine(basePath, library.PackageName, library.Version);
if (Directory.Exists(packagePath))
{
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,100 @@
// 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;
using Microsoft.Extensions.PlatformAbstractions;
namespace Microsoft.Extensions.DependencyModel
{
public class ReferenceAssemblyPathResolver
{
private static readonly Lazy<string> _defaultReferenceAssembliesPath = new Lazy<string>(GetDefaultReferenceAssembliesPath);
private static readonly Lazy<string[]> _fallbackSearchPaths = new Lazy<string[]>(GetFallbackSearchPaths);
private static string[] GetFallbackSearchPaths()
{
if (PlatformServices.Default.Runtime.OperatingSystemPlatform != Platform.Windows)
{
return new string[0];
}
var net20Dir = Path.Combine(Environment.GetEnvironmentVariable("WINDIR"), "Microsoft.NET", "Framework", "v2.0.50727");
if (!Directory.Exists(net20Dir))
{
return new string[0];
}
return new[] { net20Dir };
}
public static string GetDefaultReferenceAssembliesPath()
{
// Allow setting the reference assemblies path via an environment variable
var referenceAssembliesPath = Environment.GetEnvironmentVariable("DOTNET_REFERENCE_ASSEMBLIES_PATH");
if (!string.IsNullOrEmpty(referenceAssembliesPath))
{
return referenceAssembliesPath;
}
if (PlatformServices.Default.Runtime.OperatingSystemPlatform != Platform.Windows)
{
// There is no reference assemblies path outside of windows
// The environment variable can be used to specify one
return null;
}
// References assemblies are in %ProgramFiles(x86)% on
// 64 bit machines
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
if (string.IsNullOrEmpty(programFiles))
{
// On 32 bit machines they are in %ProgramFiles%
programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
}
if (string.IsNullOrEmpty(programFiles))
{
// Reference assemblies aren't installed
return null;
}
return Path.Combine(
programFiles,
"Reference Assemblies", "Microsoft", "Framework");
}
public static bool TryResolveReferenceAssembly(string path, out string fullPath)
{
fullPath = null;
var refereneAssembliesPath = _defaultReferenceAssembliesPath.Value;
if (refereneAssembliesPath == null)
{
return false;
}
var relativeToReferenceAssemblies = Path.Combine(refereneAssembliesPath, path);
if (File.Exists(relativeToReferenceAssemblies))
{
fullPath = relativeToReferenceAssemblies;
return true;
}
var name = Path.GetFileName(path);
foreach (var fallbackPath in _fallbackSearchPaths.Value)
{
var fallbackFile = Path.Combine(fallbackPath, name);
if (File.Exists(fallbackFile))
{
fullPath = fallbackFile;
return true;
}
}
return false;
}
}
}