Add dependency model api

This commit is contained in:
Pavel Krymets 2015-12-17 15:04:18 -08:00
parent 64946cf7d7
commit f05b208ad7
15 changed files with 499 additions and 7 deletions

View file

@ -61,6 +61,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0722D325
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MultiProjectValidator", "tools\MultiProjectValidator\MultiProjectValidator.xproj", "{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.DependencyModel", "src\Microsoft.Extensions.DependencyModel\Microsoft.Extensions.DependencyModel.xproj", "{688870C8-9843-4F9E-8576-D39290AD0F25}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -441,6 +443,22 @@ Global
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|x64.ActiveCfg = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|x64.Build.0 = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Release|Any CPU.Build.0 = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Release|x64.ActiveCfg = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Release|x64.Build.0 = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -469,5 +487,6 @@ Global
{7A75ACC4-3C2F-44E1-B492-0EC08704E9FF} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{BC765FBF-AD7A-4A99-9902-5540C5A74181} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85} = {0722D325-24C8-4E83-B5AF-0A083E7F0749}
{688870C8-9843-4F9E-8576-D39290AD0F25} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
EndGlobalSection
EndGlobal

View file

@ -29,6 +29,8 @@ namespace Microsoft.DotNet.ProjectModel
public bool? EmitEntryPoint { get; set; }
public bool? PreserveCompilationContext { get; set; }
public static CommonCompilerOptions Combine(params CommonCompilerOptions[] options)
{
var result = new CommonCompilerOptions();
@ -91,6 +93,11 @@ namespace Microsoft.DotNet.ProjectModel
{
result.EmitEntryPoint = option.EmitEntryPoint;
}
if (option.PreserveCompilationContext != null)
{
result.PreserveCompilationContext = option.PreserveCompilationContext;
}
}
return result;

View file

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
namespace Microsoft.Extensions.DependencyModel
{
public static class DependencyContextBuilder
{
public static DependencyContext FromLibraryExporter(LibraryExporter libraryExporter, string target, string runtime)
{
var dependencies = libraryExporter.GetAllExports();
return new DependencyContext(target, runtime,
GetLibraries(dependencies, export => export.CompilationAssemblies),
GetLibraries(dependencies, export => export.RuntimeAssemblies));
}
private static Library[] GetLibraries(IEnumerable<LibraryExport> dependencies, Func<LibraryExport, IEnumerable<LibraryAsset>> assemblySelector)
{
return dependencies.Select(export => GetLibrary(export, assemblySelector(export), dependencies)).ToArray();
}
private static Library GetLibrary(LibraryExport export, IEnumerable<LibraryAsset> libraryAssets, IEnumerable<LibraryExport> dependencies)
{
var serviceable = (export.Library as PackageDescription)?.Library.IsServiceable ?? false;
var version = dependencies.Where(dependency => dependency.Library.Identity == export.Library.Identity);
var libraryDependencies = export.Library.Dependencies.Select(libraryRange => GetDependency(libraryRange, dependencies)).ToArray();
return new Library(
export.Library.Identity.Type.ToString().ToLowerInvariant(),
export.Library.Identity.Name,
export.Library.Identity.Version.ToString(),
export.Library.Hash,
libraryAssets.Select(libraryAsset => libraryAsset.RelativePath).ToArray(),
libraryDependencies,
serviceable
);
}
private static Dependency GetDependency(LibraryRange libraryRange, IEnumerable<LibraryExport> dependencies)
{
var version =
dependencies.First(d => d.Library.Identity.Name == libraryRange.Name)
.Library.Identity.Version.ToString();
return new Dependency(libraryRange.Name, version);
}
}
}

View file

@ -525,7 +525,8 @@ namespace Microsoft.DotNet.ProjectModel
KeyFile = rawOptions.ValueAsString("keyFile"),
DelaySign = rawOptions.ValueAsNullableBoolean("delaySign"),
PublicSign = rawOptions.ValueAsNullableBoolean("publicSign"),
EmitEntryPoint = rawOptions.ValueAsNullableBoolean("emitEntryPoint")
EmitEntryPoint = rawOptions.ValueAsNullableBoolean("emitEntryPoint"),
PreserveCompilationContext = rawOptions.ValueAsNullableBoolean("preserveCompilationContext")
};
}

View file

@ -19,6 +19,10 @@
"Microsoft.Extensions.HashCodeCombiner.Sources": {
"type": "build",
"version": "1.0.0-*"
},
"Microsoft.Extensions.DependencyModel": {
"type": "build",
"version": "1.0.0-*"
}
},

View file

@ -16,6 +16,7 @@ using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Utilities;
using NuGet.Frameworks;
using Microsoft.Extensions.DependencyModel;
namespace Microsoft.DotNet.Tools.Compiler
{
@ -304,6 +305,8 @@ namespace Microsoft.DotNet.Tools.Compiler
compilationOptions.KeyFile = Path.GetFullPath(Path.Combine(context.ProjectFile.ProjectDirectory, compilationOptions.KeyFile));
}
var references = new List<string>();
// Add compilation options to the args
compilerArgs.AddRange(compilationOptions.SerializeToArgs());
@ -319,16 +322,49 @@ namespace Microsoft.DotNet.Tools.Compiler
if (projectDependency.Project.Files.SourceFiles.Any())
{
var projectOutputPath = GetProjectOutput(projectDependency.Project, projectDependency.Framework, configuration, outputPath);
compilerArgs.Add($"--reference:{projectOutputPath}");
references.Add(projectOutputPath);
}
}
else
{
compilerArgs.AddRange(dependency.CompilationAssemblies.Select(r => $"--reference:{r.ResolvedPath}"));
references.AddRange(dependency.CompilationAssemblies.Select(r => r.ResolvedPath));
}
compilerArgs.AddRange(dependency.SourceReferences);
}
compilerArgs.AddRange(references.Select(r => $"--reference:{r}"));
var runtimeContext = ProjectContext.Create(context.ProjectDirectory, context.TargetFramework, new[] { RuntimeIdentifier.Current });
var libraryExporter = runtimeContext.CreateExporter(configuration);
if (compilationOptions.PreserveCompilationContext == true)
{
var dependencyContext = DependencyContextBuilder.FromLibraryExporter(
libraryExporter, context.TargetFramework.DotNetFrameworkName, context.RuntimeIdentifier);
var writer = new DependencyContextWriter();
var depsJsonFile = Path.Combine(intermediateOutputPath, context.ProjectFile.Name + "dotnet-compile.deps.json");
using (var fileStream = File.Create(depsJsonFile))
{
writer.Write(dependencyContext, fileStream);
}
compilerArgs.Add($"--resource:\"{depsJsonFile}\",{context.ProjectFile.Name}.deps.json");
var refsFolder = Path.Combine(outputPath, "refs");
if (Directory.Exists(refsFolder))
{
Directory.Delete(refsFolder, true);
}
Directory.CreateDirectory(refsFolder);
foreach (var reference in references)
{
File.Copy(reference, Path.Combine(refsFolder, Path.GetFileName(reference)));
}
}
if (!AddResources(context.ProjectFile, compilerArgs, intermediateOutputPath))
{
return false;
@ -394,10 +430,9 @@ namespace Microsoft.DotNet.Tools.Compiler
if (success && !noHost && compilationOptions.EmitEntryPoint.GetValueOrDefault())
{
var runtimeContext = ProjectContext.Create(context.ProjectDirectory, context.TargetFramework, new[] { RuntimeIdentifier.Current });
MakeRunnable(runtimeContext,
outputPath,
runtimeContext.CreateExporter(configuration));
libraryExporter);
}
return PrintSummary(diagnostics, sw, success);

View file

@ -16,6 +16,10 @@
"Microsoft.Extensions.CommandLineUtils.Sources": {
"type": "build",
"version": "1.0.0-*"
},
"Microsoft.Extensions.DependencyModel": {
"type": "build",
"version": "1.0.0-*"
}
},
"frameworks": {

View file

@ -0,0 +1,17 @@
// 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.Extensions.DependencyModel
{
public struct Dependency
{
public Dependency(string name, string version)
{
Name = name;
Version = version;
}
public string Name { get; }
public string Version { get; }
}
}

View file

@ -0,0 +1,52 @@
// 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 System.Reflection;
using System.Collections.Generic;
namespace Microsoft.Extensions.DependencyModel
{
public class DependencyContext
{
private const string DepsResourceSufix = ".deps.json";
public DependencyContext(string target, string runtime, Library[] compileLibraries, Library[] runtimeLibraries)
{
Target = target;
Runtime = runtime;
CompileLibraries = compileLibraries;
RuntimeLibraries = runtimeLibraries;
}
public string Target { get; set; }
public string Runtime { get; set; }
public IReadOnlyList<Library> CompileLibraries { get; }
public IReadOnlyList<Library> RuntimeLibraries { get; }
public static DependencyContext Load()
{
var entryAssembly = (Assembly)typeof(Assembly).GetTypeInfo().GetDeclaredMethod("GetEntryAssembly").Invoke(null, null);
var stream = entryAssembly.GetManifestResourceStream(entryAssembly.GetName().Name + DepsResourceSufix);
if (stream == null)
{
throw new InvalidOperationException("Entry assembly was compiled without `preserveCompilationContext` enabled");
}
using (stream)
{
return Load(stream);
}
}
public static DependencyContext Load(Stream stream)
{
return new DependencyContextReader().Read(stream);
}
}
}

View file

@ -0,0 +1,127 @@
// 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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Extensions.DependencyModel
{
public class DependencyContextReader
{
public DependencyContext Read(Stream stream)
{
using (var streamReader = new StreamReader(stream))
{
using (var reader = new JsonTextReader(streamReader))
{
var root = JObject.Load(reader);
return Read(root);
}
}
}
private bool IsRuntimeTarget(string name) => name.Contains(DependencyContextStrings.VersionSeperator);
private DependencyContext Read(JObject root)
{
var libraryStubs = ReadLibraryStubs((JObject) root[DependencyContextStrings.LibrariesPropertyName]);
var targetsObject = (IEnumerable<KeyValuePair<string, JToken>>) root[DependencyContextStrings.TargetsPropertyName];
var runtimeTargetProperty = targetsObject.First(target => IsRuntimeTarget(target.Key));
var compileTargetProperty = targetsObject.First(target => !IsRuntimeTarget(target.Key));
return new DependencyContext(
compileTargetProperty.Key,
runtimeTargetProperty.Key.Substring(compileTargetProperty.Key.Length + 1),
ReadLibraries((JObject)runtimeTargetProperty.Value, true, libraryStubs),
ReadLibraries((JObject)compileTargetProperty.Value, false, libraryStubs)
);
}
private Library[] ReadLibraries(JObject librariesObject, bool runtime, Dictionary<string, DependencyContextReader.LibraryStub> libraryStubs)
{
return librariesObject.Properties().Select(property => ReadLibrary(property, runtime, libraryStubs)).ToArray();
}
private Library ReadLibrary(JProperty property, bool runtime, Dictionary<string, DependencyContextReader.LibraryStub> libraryStubs)
{
var nameWithVersion = property.Name;
DependencyContextReader.LibraryStub stub;
if (!libraryStubs.TryGetValue(nameWithVersion, out stub))
{
throw new InvalidOperationException($"Cannot find library information for {nameWithVersion}");
}
var seperatorPosition = nameWithVersion.IndexOf(DependencyContextStrings.VersionSeperator);
var name = nameWithVersion.Substring(0, seperatorPosition);
var version = nameWithVersion.Substring(seperatorPosition + 1);
var libraryObject = (JObject) property.Value;
var dependencies = ReadDependencies(libraryObject);
var assemblies = ReadAssemblies(libraryObject, runtime);
return new Library(stub.Type, name, version, stub.Hash, assemblies, dependencies, stub.Serviceable);
}
private static string[] ReadAssemblies(JObject libraryObject, bool runtime)
{
var assembliesObject = (JObject) libraryObject[runtime ? DependencyContextStrings.RunTimeAssembliesKey : DependencyContextStrings.CompileTimeAssembliesKey];
if (assembliesObject == null)
{
return new string[] {};
}
return assembliesObject.Properties().Select(property => property.Name).ToArray();
}
private static Dependency[] ReadDependencies(JObject libraryObject)
{
var dependenciesObject = ((JObject) libraryObject[DependencyContextStrings.DependenciesPropertyName]);
if (dependenciesObject == null)
{
return new Dependency[]{ };
}
return dependenciesObject.Properties()
.Select(property => new Dependency(property.Name, (string) property.Value)).ToArray();
}
private Dictionary<string, LibraryStub> ReadLibraryStubs(JObject librariesObject)
{
var libraries = new Dictionary<string, LibraryStub>();
foreach (var libraryProperty in librariesObject)
{
var value = (JObject) libraryProperty.Value;
var stub = new LibraryStub
{
Name = libraryProperty.Key,
Hash = value[DependencyContextStrings.Sha512PropertyName]?.Value<string>(),
Type = value[DependencyContextStrings.TypePropertyName].Value<string>(),
Serviceable = value[DependencyContextStrings.ServiceablePropertyName]?.Value<bool>() == true
};
libraries.Add(stub.Name, stub);
}
return libraries;
}
private struct LibraryStub
{
public string Name;
public string Hash;
public string Type;
public bool Serviceable;
}
}
}

View file

@ -0,0 +1,23 @@
namespace Microsoft.Extensions.DependencyModel
{
internal class DependencyContextStrings
{
internal const char VersionSeperator = '/';
internal const string CompileTimeAssembliesKey = "compile";
internal const string RunTimeAssembliesKey = "runtime";
internal const string LibrariesPropertyName = "libraries";
internal const string TargetsPropertyName = "targets";
internal const string DependenciesPropertyName = "dependencies";
internal const string Sha512PropertyName = "sha512";
internal const string TypePropertyName = "type";
internal const string ServiceablePropertyName = "serviceable";
}
}

View file

@ -0,0 +1,86 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Extensions.DependencyModel
{
public class DependencyContextWriter
{
public void Write(DependencyContext context, Stream stream)
{
using (var writer = new StreamWriter(stream))
{
using (var jsonWriter = new JsonTextWriter(writer))
{
Write(context).WriteTo(jsonWriter);
}
}
}
private JObject Write(DependencyContext context)
{
return new JObject(
new JProperty(DependencyContextStrings.TargetsPropertyName, WriteTargets(context)),
new JProperty(DependencyContextStrings.LibrariesPropertyName, WriteLibraries(context))
);
}
private JObject WriteTargets(DependencyContext context)
{
return new JObject(
new JProperty(context.Target, WriteTarget(context.CompileLibraries, false)),
new JProperty(context.Target + DependencyContextStrings.VersionSeperator + context.Runtime,
WriteTarget(context.RuntimeLibraries, true))
);
}
private JObject WriteTarget(IReadOnlyList<Library> libraries, bool runtime)
{
return new JObject(
libraries.Select(library =>
new JProperty(library.PackageName + DependencyContextStrings.VersionSeperator + library.Version, WriteTargetLibrary(library, runtime))));
}
private JObject WriteTargetLibrary(Library library, bool runtime)
{
return new JObject(
new JProperty(DependencyContextStrings.DependenciesPropertyName, WriteDependencies(library.Dependencies)),
new JProperty(runtime ? DependencyContextStrings.RunTimeAssembliesKey : DependencyContextStrings.CompileTimeAssembliesKey,
WriteAssemblies(library.Assemblies))
);
}
private JObject WriteAssemblies(IReadOnlyList<string> assemblies)
{
return new JObject(assemblies.Select(assembly => new JProperty(assembly, new JObject())));
}
private JObject WriteDependencies(IReadOnlyList<Dependency> dependencies)
{
return new JObject(
dependencies.Select(dependency => new JProperty(dependency.Name, dependency.Version))
);
}
private JObject WriteLibraries(DependencyContext context)
{
var allLibraries =
context.RuntimeLibraries.Concat(context.CompileLibraries)
.GroupBy(library => library.PackageName + DependencyContextStrings.VersionSeperator + library.Version);
return new JObject(allLibraries.Select(libraries=> new JProperty(libraries.Key, WriteLibrary(libraries.First()))));
}
private JObject WriteLibrary(Library library)
{
return new JObject(
new JProperty(DependencyContextStrings.TypePropertyName, library.LibraryType),
new JProperty(DependencyContextStrings.ServiceablePropertyName, library.Serviceable),
new JProperty(DependencyContextStrings.Sha512PropertyName, library.Hash)
);
}
}
}

View file

@ -0,0 +1,36 @@
// 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;
namespace Microsoft.Extensions.DependencyModel
{
public struct Library
{
public Library(string libraryType, string packageName, string version, string hash, string[] assemblies, Dependency[] dependencies, bool serviceable)
{
LibraryType = libraryType;
PackageName = packageName;
Version = version;
Hash = hash;
Assemblies = assemblies;
Dependencies = dependencies;
Serviceable = serviceable;
}
public string LibraryType { get; }
public string PackageName { get; }
public string Version { get; }
public string Hash { get; }
public IReadOnlyList<string> Assemblies { get; }
public IReadOnlyList<Dependency> Dependencies { get; }
public bool Serviceable { get; }
}
}

View file

@ -6,12 +6,11 @@
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>482b1045-a1fa-4063-a0d9-a8107a91a016</ProjectGuid>
<ProjectGuid>688870c8-9843-4f9e-8576-d39290ad0f25</ProjectGuid>
<RootNamespace>Microsoft.Extensions.DependencyModel</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>

View file

@ -0,0 +1,29 @@
{
"description": "Abstractions for reading `.deps` files.",
"version": "1.0.0-*",
"repository": {
"type": "git",
"url": "git://github.com/dotnet/cli"
},
"compilationOptions": {
"warningsAsErrors": true
},
"dependencies": {
"Newtonsoft.Json": "7.0.1"
},
"frameworks": {
"dnx451": {},
"dnxcore50": {
"dependencies": {
"System.Runtime": "4.0.21-rc2-23618",
"System.Dynamic.Runtime": "4.0.11-rc2-23616"
}
}
},
"scripts": {
"postcompile": [
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.dll\"",
"../../scripts/build/place-binary \"%compile:OutputDir%/%project:Name%.pdb\""
]
}
}