Merge pull request #434 from dotnet/anurse/401-loader

Add ProjectAssemblyLoadContext
This commit is contained in:
Piotr Puszkiewicz 2015-12-08 16:30:33 -08:00
commit 68216b80cf
14 changed files with 390 additions and 12 deletions

View file

@ -0,0 +1,43 @@
// 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.Collections.Generic;
using System.Reflection;
using System.Runtime.Loader;
namespace Microsoft.DotNet.ProjectModel.Loader
{
public static class LoaderProjectContextExtensions
{
public static AssemblyLoadContext CreateLoadContext(this ProjectContext context, string configuration = "Debug")
{
var exporter = context.CreateExporter(configuration);
var assemblies = new Dictionary<AssemblyName, string>();
var dllImports = new Dictionary<string, string>();
foreach (var export in exporter.GetAllExports())
{
// TODO: Handle resource assemblies
foreach (var asset in export.RuntimeAssemblies)
{
// REVIEW: Should we use the following?
// AssemblyLoadContext.GetAssemblyName(asset.ResolvedPath);
var assemblyName = new AssemblyName(asset.Name);
assemblies[assemblyName] = asset.ResolvedPath;
}
foreach (var asset in export.NativeLibraries)
{
dllImports[asset.Name] = asset.ResolvedPath;
}
}
return new ProjectLoadContext(
assemblies,
dllImports,
// Add the project's output directory path to ensure project-to-project references get located
new[] { context.GetOutputDirectoryPath(configuration) });
}
}
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0.24720" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.24720</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>c7af0290-ef0d-44dc-9edc-600803b664f8</ProjectGuid>
<RootNamespace>Microsoft.DotNet.ProjectModel.Loader</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View file

@ -0,0 +1,101 @@
// 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.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
namespace Microsoft.DotNet.ProjectModel.Loader
{
/// <summary>
/// An <see cref="AssemblyLoadContext"/> designed to allow loading from a Project.
/// </summary>
public class ProjectLoadContext : AssemblyLoadContext
{
private readonly IDictionary<AssemblyName, string> _assemblyPaths;
private readonly IDictionary<string, string> _nativeLibraries;
private readonly IEnumerable<string> _searchPaths;
private static readonly string[] NativeLibraryExtensions;
private static readonly string[] ManagedAssemblyExtensions = new[]
{
".dll",
".ni.dll",
".exe",
".ni.exe"
};
static ProjectLoadContext()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
NativeLibraryExtensions = new[] { ".dll" };
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
NativeLibraryExtensions = new[] { ".dylib" };
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
NativeLibraryExtensions = new[] { ".so" };
}
else
{
NativeLibraryExtensions = new string[0];
}
}
public ProjectLoadContext(IDictionary<AssemblyName, string> assemblyPaths,
IDictionary<string, string> nativeLibraries,
IEnumerable<string> searchPaths)
{
_assemblyPaths = assemblyPaths;
_nativeLibraries = nativeLibraries;
_searchPaths = searchPaths;
}
protected override Assembly Load(AssemblyName assemblyName)
{
string path;
if (_assemblyPaths.TryGetValue(assemblyName, out path) || SearchForLibrary(ManagedAssemblyExtensions, assemblyName.Name, out path))
{
return LoadFromAssemblyPath(path);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string path;
if (_nativeLibraries.TryGetValue(unmanagedDllName, out path) || SearchForLibrary(NativeLibraryExtensions, unmanagedDllName, out path))
{
return LoadUnmanagedDllFromPath(path);
}
return base.LoadUnmanagedDll(unmanagedDllName);
}
private bool SearchForLibrary(string[] extensions, string name, out string path)
{
foreach (var searchPath in _searchPaths)
{
foreach (var extension in extensions)
{
var candidate = Path.Combine(searchPath, name + extension);
if (File.Exists(candidate))
{
path = candidate;
return true;
}
}
}
path = null;
return false;
}
}
}

View file

@ -0,0 +1,10 @@
{
"dependencies": {
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
"System.AppContext": "4.0.1-beta-23504",
"System.Runtime.Loader": "4.0.0-beta-23504"
},
"frameworks": {
"dnxcore50": { }
}
}

View file

@ -21,22 +21,23 @@ namespace Microsoft.DotNet.ProjectModel.Workspaces
{
private Dictionary<string, AssemblyMetadata> _cache = new Dictionary<string, AssemblyMetadata>();
private readonly string[] _projectPaths;
public ProjectJsonWorkspace(ProjectContext context) : base(MefHostServices.DefaultHost, "Custom")
{
AddProject(context);
}
public ProjectJsonWorkspace(string projectPath) : this(new[] { projectPath })
{
}
public ProjectJsonWorkspace(string[] projectPaths) : base(MefHostServices.DefaultHost, "Custom")
public ProjectJsonWorkspace(IEnumerable<string> projectPaths) : base(MefHostServices.DefaultHost, "Custom")
{
_projectPaths = projectPaths;
Initialize();
Initialize(projectPaths);
}
private void Initialize()
private void Initialize(IEnumerable<string> projectPaths)
{
foreach (var projectPath in _projectPaths)
foreach (var projectPath in projectPaths)
{
AddProject(projectPath);
}

View file

@ -0,0 +1,12 @@
using Microsoft.CodeAnalysis;
namespace Microsoft.DotNet.ProjectModel.Workspaces
{
public static class WorkspaceProjectContextExtensions
{
public static Workspace CreateWorkspace(this ProjectContext context)
{
return new ProjectJsonWorkspace(context);
}
}
}

View file

@ -0,0 +1,11 @@
// 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 Microsoft.DotNet.ProjectModel
{
internal static class Constants
{
public static readonly string DefaultOutputDirectory = "bin";
public static readonly string DefaultConfiguration = "Debug";
}
}

View file

@ -130,14 +130,18 @@ namespace Microsoft.DotNet.ProjectModel
ProjectFile.Name + FileNameSuffixes.DotNet.ProgramDatabase);
}
private string GetOutputDirectoryPath(string buildConfiguration)
public string GetOutputDirectoryPath(string buildConfiguration)
{
return Path.Combine(
var outDir = Path.Combine(
ProjectDirectory,
DirectoryNames.Bin,
buildConfiguration,
TargetFramework.GetShortFolderName(),
ProjectModel.RuntimeIdentifier.Current);
TargetFramework.GetShortFolderName());
if (!string.IsNullOrEmpty(RuntimeIdentifier))
{
outDir = Path.Combine(outDir, RuntimeIdentifier);
}
return outDir;
}
}
}