Merge pull request #1347 from dotnet/pakrym/refasm
Add reference assembly support to DependencyModel
This commit is contained in:
commit
23b1b077dd
6 changed files with 292 additions and 139 deletions
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue