diff --git a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/Validator.cs b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/Validator.cs index 81d057a29..0cfe83ce7 100644 --- a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/Validator.cs +++ b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/Validator.cs @@ -17,13 +17,13 @@ namespace Microsoft.Extensions.DependencyModel private static void CheckMetadata(Library library) { - if (string.Equals(library.LibraryType, "package", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(library.Type, "package", StringComparison.OrdinalIgnoreCase)) { - if (string.IsNullOrWhiteSpace(library.PackageName) || + if (string.IsNullOrWhiteSpace(library.Name) || string.IsNullOrWhiteSpace(library.Hash) || string.IsNullOrWhiteSpace(library.Version)) { - Error($"Empty metadata for {library.GetType().ToString()} {library.PackageName}"); + Error($"Empty metadata for {library.GetType().ToString()} {library.Name}"); } } } diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs index 2ab423241..2679750f4 100644 --- a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs @@ -215,7 +215,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation var compileAsset = new LibraryAsset( project.Project.Name, - null, + Path.GetFileName(assemblyPath), assemblyPath); builder.AddCompilationAssembly(compileAsset); @@ -334,7 +334,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation var assemblyPath = Path.Combine(package.Path, analyzer); - // $/analyzers/{Framework Name}{Version}/{Supported Architecture}/{Supported Programming Language}/{Analyzer}.dll + // $/analyzers/{Framework Name}{Version}/{Supported Architecture}/{Supported Programming Language}/{Analyzer}.dll switch (specifiers.Length) { // $/analyzers/{analyzer}.dll @@ -396,7 +396,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation private static bool LibraryIsOfType(LibraryType type, LibraryDescription library) { - return type.Equals(LibraryType.Unspecified) || // No type filter was requested + return type.Equals(LibraryType.Unspecified) || // No type filter was requested library.Identity.Type.Equals(type); // OR, library type matches requested type } } diff --git a/src/Microsoft.DotNet.ProjectModel/DependencyContextBuilder.cs b/src/Microsoft.DotNet.ProjectModel/DependencyContextBuilder.cs index 2411ce72b..da9ad8444 100644 --- a/src/Microsoft.DotNet.ProjectModel/DependencyContextBuilder.cs +++ b/src/Microsoft.DotNet.ProjectModel/DependencyContextBuilder.cs @@ -13,25 +13,45 @@ using NuGet.Frameworks; namespace Microsoft.Extensions.DependencyModel { - public static class DependencyContextBuilder + public class DependencyContextBuilder { - public static DependencyContext Build(CommonCompilerOptions compilerOptions, LibraryExporter libraryExporter, string configuration, NuGetFramework target, string runtime) - { - var dependencies = libraryExporter.GetAllExports(); + private readonly string _referenceAssembliesPath; - // Sometimes we have package and reference assembly with the same name (System.Runtime for example) thats why we - // deduplicating them prefering reference assembly - var dependencyLookup = dependencies - .OrderBy(export => export.Library.Identity.Type == LibraryType.ReferenceAssembly) - .GroupBy(export => export.Library.Identity.Name) - .Select(exports => exports.First()) - .Select(export => new Dependency(export.Library.Identity.Name, export.Library.Identity.Version.ToString())) + public DependencyContextBuilder() : this(FrameworkReferenceResolver.Default.ReferenceAssembliesPath) + { + } + + public DependencyContextBuilder(string referenceAssembliesPath) + { + _referenceAssembliesPath = referenceAssembliesPath; + } + + public DependencyContext Build(CommonCompilerOptions compilerOptions, + IEnumerable compilationExports, + IEnumerable runtimeExports, + NuGetFramework target, + string runtime) + { + if (compilationExports == null) + { + compilationExports = Enumerable.Empty(); + } + + var dependencyLookup = compilationExports + .Concat(runtimeExports) + .Select(export => export.Library.Identity) + .Distinct() + .Select(identity => new Dependency(identity.Name, identity.Version.ToString())) .ToDictionary(dependency => dependency.Name); - return new DependencyContext(target.DotNetFrameworkName, runtime, + return new DependencyContext( + target.DotNetFrameworkName, + runtime, + false, GetCompilationOptions(compilerOptions), - GetLibraries(dependencies, dependencyLookup, target, configuration, runtime: false).Cast().ToArray(), - GetLibraries(dependencies, dependencyLookup, target, configuration, runtime: true).Cast().ToArray()); + GetLibraries(compilationExports, dependencyLookup, runtime: false).Cast().ToArray(), + GetLibraries(runtimeExports, dependencyLookup, runtime: true).Cast().ToArray(), + new KeyValuePair[0]); } private static CompilationOptions GetCompilationOptions(CommonCompilerOptions compilerOptions) @@ -50,18 +70,14 @@ namespace Microsoft.Extensions.DependencyModel compilerOptions.GenerateXmlDocumentation); } - private static IEnumerable GetLibraries(IEnumerable dependencies, + private IEnumerable GetLibraries(IEnumerable exports, IDictionary dependencyLookup, - NuGetFramework target, - string configuration, bool runtime) { - return dependencies.Select(export => GetLibrary(export, target, configuration, runtime, dependencyLookup)); + return exports.Select(export => GetLibrary(export, runtime, dependencyLookup)); } - private static Library GetLibrary(LibraryExport export, - NuGetFramework target, - string configuration, + private Library GetLibrary(LibraryExport export, bool runtime, IDictionary dependencyLookup) { @@ -72,11 +88,17 @@ namespace Microsoft.Extensions.DependencyModel var libraryAssets = runtime ? export.RuntimeAssemblies : export.CompilationAssemblies; - foreach (var libraryDependenciesGroup in export.Library.Dependencies.GroupBy(d => d.Name)) + foreach (var libraryDependency in export.Library.Dependencies) { - LibraryRange libraryDependency = libraryDependenciesGroup - .OrderByDescending(d => d.Target == LibraryType.ReferenceAssembly) - .First(); + // skip build time dependencies + if (!libraryDependency.Type.HasFlag( + LibraryDependencyTypeFlag.MainReference | + LibraryDependencyTypeFlag.MainExport | + LibraryDependencyTypeFlag.RuntimeComponent | + LibraryDependencyTypeFlag.BecomesNupkgDependency)) + { + continue; + } Dependency dependency; if (dependencyLookup.TryGetValue(libraryDependency.Name, out dependency)) @@ -86,19 +108,7 @@ namespace Microsoft.Extensions.DependencyModel } string[] assemblies; - if (type == LibraryType.Project) - { - var isExe = ((ProjectDescription)export.Library) - .Project - .GetCompilerOptions(target, configuration) - .EmitEntryPoint - .GetValueOrDefault(false); - - isExe &= target.IsDesktop(); - - assemblies = new[] { export.Library.Identity.Name + (isExe ? ".exe" : ".dll") }; - } - else if (type == LibraryType.ReferenceAssembly) + if (type == LibraryType.ReferenceAssembly) { assemblies = ResolveReferenceAssembliesPath(libraryAssets); } @@ -114,7 +124,8 @@ namespace Microsoft.Extensions.DependencyModel export.Library.Identity.Name, export.Library.Identity.Version.ToString(), export.Library.Hash, - assemblies, + assemblies.Select(RuntimeAssembly.Create).ToArray(), + new RuntimeTarget[0], libraryDependencies.ToArray(), serviceable ); @@ -133,11 +144,11 @@ namespace Microsoft.Extensions.DependencyModel } } - private static string[] ResolveReferenceAssembliesPath(IEnumerable libraryAssets) + private string[] ResolveReferenceAssembliesPath(IEnumerable libraryAssets) { var resolvedPaths = new List(); var referenceAssembliesPath = - PathUtility.EnsureTrailingSlash(FrameworkReferenceResolver.Default.ReferenceAssembliesPath); + PathUtility.EnsureTrailingSlash(_referenceAssembliesPath); foreach (var libraryAsset in libraryAssets) { // If resolved path is under ReferenceAssembliesPath store it as a relative to it diff --git a/src/Microsoft.Extensions.DependencyModel/CompilationLibrary.cs b/src/Microsoft.Extensions.DependencyModel/CompilationLibrary.cs index 73ad8187b..1d5490f0f 100644 --- a/src/Microsoft.Extensions.DependencyModel/CompilationLibrary.cs +++ b/src/Microsoft.Extensions.DependencyModel/CompilationLibrary.cs @@ -9,8 +9,8 @@ namespace Microsoft.Extensions.DependencyModel { public class CompilationLibrary : Library { - public CompilationLibrary(string libraryType, string packageName, string version, string hash, string[] assemblies, Dependency[] dependencies, bool serviceable) - : base(libraryType, packageName, version, hash, dependencies, serviceable) + public CompilationLibrary(string type, string name, string version, string hash, string[] assemblies, Dependency[] dependencies, bool serviceable) + : base(type, name, version, hash, dependencies, serviceable) { Assemblies = assemblies; } @@ -30,7 +30,7 @@ namespace Microsoft.Extensions.DependencyModel var assemblies = new List(); if (!DefaultResolver.TryResolveAssemblyPaths(this, assemblies)) { - throw new InvalidOperationException($"Can not find compilation library location for package '{PackageName}'"); + throw new InvalidOperationException($"Can not find compilation library location for package '{Name}'"); } return assemblies; } diff --git a/src/Microsoft.Extensions.DependencyModel/Dependency.cs b/src/Microsoft.Extensions.DependencyModel/Dependency.cs index 7950220af..8060cb4b1 100644 --- a/src/Microsoft.Extensions.DependencyModel/Dependency.cs +++ b/src/Microsoft.Extensions.DependencyModel/Dependency.cs @@ -1,6 +1,8 @@ // 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 Microsoft.Extensions.Internal; + namespace Microsoft.Extensions.DependencyModel { public struct Dependency @@ -13,5 +15,24 @@ namespace Microsoft.Extensions.DependencyModel public string Name { get; } public string Version { get; } + + public bool Equals(Dependency other) + { + return string.Equals(Name, other.Name) && string.Equals(Version, other.Version); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is Dependency && Equals((Dependency) obj); + } + + public override int GetHashCode() + { + var combiner = HashCodeCombiner.Start(); + combiner.Add(Name); + combiner.Add(Version); + return combiner.CombinedHash; + } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContext.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContext.cs index b1cb8ccf7..229a090b9 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContext.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContext.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Reflection; using System.Collections.Generic; +using System.Linq; namespace Microsoft.Extensions.DependencyModel { @@ -15,27 +16,64 @@ namespace Microsoft.Extensions.DependencyModel private static readonly Lazy _defaultContext = new Lazy(LoadDefault); - public DependencyContext(string target, string runtime, CompilationOptions compilationOptions, CompilationLibrary[] compileLibraries, RuntimeLibrary[] runtimeLibraries) + public DependencyContext(string targetFramework, + string runtime, + bool isPortable, + CompilationOptions compilationOptions, + IEnumerable compileLibraries, + IEnumerable runtimeLibraries, + IEnumerable> runtimeGraph) { - Target = target; + if (targetFramework == null) + { + throw new ArgumentNullException(nameof(targetFramework)); + } + if (runtime == null) + { + throw new ArgumentNullException(nameof(runtime)); + } + if (compilationOptions == null) + { + throw new ArgumentNullException(nameof(compilationOptions)); + } + if (compileLibraries == null) + { + throw new ArgumentNullException(nameof(compileLibraries)); + } + if (runtimeLibraries == null) + { + throw new ArgumentNullException(nameof(runtimeLibraries)); + } + if (runtimeGraph == null) + { + throw new ArgumentNullException(nameof(runtimeGraph)); + } + + TargetFramework = targetFramework; Runtime = runtime; + IsPortable = isPortable; CompilationOptions = compilationOptions; - CompileLibraries = compileLibraries; - RuntimeLibraries = runtimeLibraries; + CompileLibraries = compileLibraries.ToArray(); + RuntimeLibraries = runtimeLibraries.ToArray(); + RuntimeGraph = runtimeGraph.ToArray(); } public static DependencyContext Default => _defaultContext.Value; - public string Target { get; } + public string TargetFramework { get; } public string Runtime { get; } + public bool IsPortable { get; } + public CompilationOptions CompilationOptions { get; } public IReadOnlyList CompileLibraries { get; } public IReadOnlyList RuntimeLibraries { get; } + public IReadOnlyList> RuntimeGraph { get; } + private static DependencyContext LoadDefault() { var entryAssembly = Assembly.GetEntryAssembly(); diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextCsvReader.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextCsvReader.cs index d466bd15c..8008fe065 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextCsvReader.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextCsvReader.cs @@ -42,22 +42,25 @@ namespace Microsoft.Extensions.DependencyModel { var identity = packageGroup.Key; runtimeLibraries.Add(new RuntimeLibrary( - libraryType: identity.Item1, - packageName: identity.Item2, + type: identity.Item1, + name: identity.Item2, version: identity.Item3, hash: identity.Item4, - assemblies: packageGroup.Select(l => l.AssetPath).ToArray(), + assemblies: packageGroup.Select(l => RuntimeAssembly.Create(l.AssetPath)).ToArray(), + subTargets: new RuntimeTarget[0], dependencies: new Dependency[] { }, serviceable: false )); } return new DependencyContext( - target: string.Empty, + targetFramework: string.Empty, runtime: string.Empty, + isPortable: false, compilationOptions: CompilationOptions.Default, compileLibraries: new CompilationLibrary[] {}, - runtimeLibraries: runtimeLibraries.ToArray()); + runtimeLibraries: runtimeLibraries.ToArray(), + runtimeGraph: new KeyValuePair[0]); } private Tuple PackageIdentity(DepsFileLine line) diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs index 558878b35..e16498313 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs @@ -28,23 +28,79 @@ namespace Microsoft.Extensions.DependencyModel private DependencyContext Read(JObject root) { - var libraryStubs = ReadLibraryStubs((JObject) root[DependencyContextStrings.LibrariesPropertyName]); - var targetsObject = (IEnumerable>) root[DependencyContextStrings.TargetsPropertyName]; + var runtime = string.Empty; + var target = string.Empty; + var isPortable = true; - var runtimeTargetProperty = targetsObject.First(target => IsRuntimeTarget(target.Key)); - var compileTargetProperty = targetsObject.First(target => !IsRuntimeTarget(target.Key)); + var runtimeTargetName = root[DependencyContextStrings.RuntimeTargetPropertyName]?.Value(); + + var libraryStubs = ReadLibraryStubs((JObject) root[DependencyContextStrings.LibrariesPropertyName]); + var targetsObject = (JObject) root[DependencyContextStrings.TargetsPropertyName]; + + JObject runtimeTarget = null; + JObject compileTarget = null; + if (targetsObject != null) + { + var compileTargetProperty = targetsObject.Properties() + .FirstOrDefault(p => !IsRuntimeTarget(p.Name)); + + compileTarget = (JObject)compileTargetProperty.Value; + target = compileTargetProperty.Name; + + if (!string.IsNullOrEmpty(runtimeTargetName)) + { + runtimeTarget = (JObject) targetsObject[runtimeTargetName]; + if (runtimeTarget == null) + { + throw new FormatException($"Target with name {runtimeTargetName} not found"); + } + + var seperatorIndex = runtimeTargetName.IndexOf(DependencyContextStrings.VersionSeperator); + if (seperatorIndex > -1 && seperatorIndex < runtimeTargetName.Length) + { + runtime = runtimeTargetName.Substring(seperatorIndex + 1); + isPortable = false; + } + } + else + { + runtimeTarget = compileTarget; + } + } return new DependencyContext( - compileTargetProperty.Key, - runtimeTargetProperty.Key.Substring(compileTargetProperty.Key.Length + 1), + target, + runtime, + isPortable, ReadCompilationOptions((JObject)root[DependencyContextStrings.CompilationOptionsPropertName]), - ReadLibraries((JObject)compileTargetProperty.Value, false, libraryStubs).Cast().ToArray(), - ReadLibraries((JObject)runtimeTargetProperty.Value, true, libraryStubs).Cast().ToArray() + ReadLibraries(compileTarget, false, libraryStubs).Cast().ToArray(), + ReadLibraries(runtimeTarget, true, libraryStubs).Cast().ToArray(), + ReadRuntimeGraph((JObject)root[DependencyContextStrings.RuntimesPropertyName]).ToArray() ); } + private IEnumerable> ReadRuntimeGraph(JObject runtimes) + { + if (runtimes == null) + { + yield break; + } + + var targets = runtimes.Children(); + var runtime = (JProperty)targets.Single(); + foreach (var pair in (JObject)runtime.Value) + { + yield return new KeyValuePair(pair.Key, pair.Value.Values().ToArray()); + } + } + private CompilationOptions ReadCompilationOptions(JObject compilationOptionsObject) { + if (compilationOptionsObject == null) + { + return CompilationOptions.Default; + } + return new CompilationOptions( compilationOptionsObject[DependencyContextStrings.DefinesPropertyName]?.Values(), compilationOptionsObject[DependencyContextStrings.LanguageVersionPropertyName]?.Value(), @@ -63,6 +119,10 @@ namespace Microsoft.Extensions.DependencyModel private IEnumerable ReadLibraries(JObject librariesObject, bool runtime, Dictionary libraryStubs) { + if (librariesObject == null) + { + return Enumerable.Empty(); + } return librariesObject.Properties().Select(property => ReadLibrary(property, runtime, libraryStubs)); } @@ -84,21 +144,65 @@ namespace Microsoft.Extensions.DependencyModel var libraryObject = (JObject) property.Value; var dependencies = ReadDependencies(libraryObject); - var assemblies = ReadAssemblies(libraryObject, runtime); if (runtime) { - return new RuntimeLibrary(stub.Type, name, version, stub.Hash, assemblies, dependencies, stub.Serviceable); + var runtimeTargets = new List(); + var runtimeTargetsObject = (JObject)libraryObject[DependencyContextStrings.RuntimeTargetsPropertyName]; + + var entries = ReadRuntimeTargetEntries(runtimeTargetsObject).ToArray(); + + foreach (var ridGroup in entries.GroupBy(e => e.Rid)) + { + var runtimeAssets = entries.Where(e => e.Type == DependencyContextStrings.RuntimeAssetType) + .Select(e => RuntimeAssembly.Create(e.Path)) + .ToArray(); + + var nativeAssets = entries.Where(e => e.Type == DependencyContextStrings.NativeAssetType) + .Select(e => e.Path) + .ToArray(); + + runtimeTargets.Add(new RuntimeTarget( + ridGroup.Key, + runtimeAssets, + nativeAssets + )); + } + + var assemblies = ReadAssemblies(libraryObject, DependencyContextStrings.RuntimeAssembliesKey) + .Select(RuntimeAssembly.Create) + .ToArray(); + + return new RuntimeLibrary(stub.Type, name, version, stub.Hash, assemblies, runtimeTargets.ToArray(), dependencies, stub.Serviceable); } else { + var assemblies = ReadAssemblies(libraryObject, DependencyContextStrings.CompileTimeAssembliesKey); return new CompilationLibrary(stub.Type, name, version, stub.Hash, assemblies, dependencies, stub.Serviceable); } } - private static string[] ReadAssemblies(JObject libraryObject, bool runtime) + private static IEnumerable ReadRuntimeTargetEntries(JObject runtimeTargetObject) { - var assembliesObject = (JObject) libraryObject[runtime ? DependencyContextStrings.RunTimeAssembliesKey : DependencyContextStrings.CompileTimeAssembliesKey]; + if (runtimeTargetObject == null) + { + yield break; + } + foreach (var libraryProperty in runtimeTargetObject) + { + var libraryObject = (JObject)libraryProperty.Value; + yield return new RuntimeTargetEntryStub() + { + Path = libraryProperty.Key, + Rid = libraryObject[DependencyContextStrings.RidPropertyName].Value(), + Type = libraryObject[DependencyContextStrings.AssetTypePropertyName].Value() + }; + } + } + + private static string[] ReadAssemblies(JObject libraryObject, string name) + { + var assembliesObject = (JObject) libraryObject[name]; if (assembliesObject == null) { @@ -110,7 +214,7 @@ namespace Microsoft.Extensions.DependencyModel private static Dependency[] ReadDependencies(JObject libraryObject) { - var dependenciesObject = ((JObject) libraryObject[DependencyContextStrings.DependenciesPropertyName]); + var dependenciesObject = (JObject) libraryObject[DependencyContextStrings.DependenciesPropertyName]; if (dependenciesObject == null) { @@ -124,21 +228,31 @@ namespace Microsoft.Extensions.DependencyModel private Dictionary ReadLibraryStubs(JObject librariesObject) { var libraries = new Dictionary(); - foreach (var libraryProperty in librariesObject) + if (librariesObject != null) { - var value = (JObject) libraryProperty.Value; - var stub = new LibraryStub + foreach (var libraryProperty in librariesObject) { - Name = libraryProperty.Key, - Hash = value[DependencyContextStrings.Sha512PropertyName]?.Value(), - Type = value[DependencyContextStrings.TypePropertyName].Value(), - Serviceable = value[DependencyContextStrings.ServiceablePropertyName]?.Value() == true - }; - libraries.Add(stub.Name, stub); + var value = (JObject) libraryProperty.Value; + var stub = new LibraryStub + { + Name = libraryProperty.Key, + Hash = value[DependencyContextStrings.Sha512PropertyName]?.Value(), + Type = value[DependencyContextStrings.TypePropertyName].Value(), + Serviceable = value[DependencyContextStrings.ServiceablePropertyName]?.Value() == true + }; + libraries.Add(stub.Name, stub); + } } return libraries; } + private struct RuntimeTargetEntryStub + { + public string Type; + public string Path; + public string Rid; + } + private struct LibraryStub { public string Name; diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextStrings.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextStrings.cs index a3831d0be..e42a850b5 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextStrings.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextStrings.cs @@ -9,7 +9,9 @@ namespace Microsoft.Extensions.DependencyModel internal const string CompileTimeAssembliesKey = "compile"; - internal const string RunTimeAssembliesKey = "runtime"; + internal const string RuntimeAssembliesKey = "runtime"; + + internal const string RuntimeTargetPropertyName = "runtimeTarget"; internal const string LibrariesPropertyName = "libraries"; @@ -48,5 +50,21 @@ namespace Microsoft.Extensions.DependencyModel internal const string EmitEntryPointPropertyName = "emitEntryPoint"; internal const string GenerateXmlDocumentationPropertyName = "xmlDoc"; + + internal const string PortablePropertyName = "portable"; + + internal const string RuntimeTargetNamePropertyName = "name"; + + internal const string RuntimesPropertyName = "runtimes"; + + internal const string RuntimeTargetsPropertyName = "runtimeTargets"; + + internal const string RidPropertyName = "rid"; + + internal const string AssetTypePropertyName = "assetType"; + + internal const string RuntimeAssetType = "runtime"; + + internal const string NativeAssetType = "native"; } -} +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs index dca0e08b0..a63f622d4 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextWriter.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; +using Microsoft.Extensions.PlatformAbstractions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -26,9 +28,29 @@ namespace Microsoft.Extensions.DependencyModel private JObject Write(DependencyContext context) { return new JObject( + new JProperty(DependencyContextStrings.RuntimeTargetPropertyName, WriteRuntimeTargetInfo(context)), new JProperty(DependencyContextStrings.CompilationOptionsPropertName, WriteCompilationOptions(context.CompilationOptions)), new JProperty(DependencyContextStrings.TargetsPropertyName, WriteTargets(context)), - new JProperty(DependencyContextStrings.LibrariesPropertyName, WriteLibraries(context)) + new JProperty(DependencyContextStrings.LibrariesPropertyName, WriteLibraries(context)), + new JProperty(DependencyContextStrings.RuntimesPropertyName, WriteRuntimeGraph(context)) + ); + } + + private string WriteRuntimeTargetInfo(DependencyContext context) + { + return context.IsPortable? + context.TargetFramework : + context.TargetFramework + DependencyContextStrings.VersionSeperator + context.Runtime; + } + + private JObject WriteRuntimeGraph(DependencyContext context) + { + return new JObject( + new JProperty(context.TargetFramework, + new JObject( + context.RuntimeGraph.Select(g => new JProperty(g.Key, new JArray(g.Value))) + ) + ) ); } @@ -39,54 +61,40 @@ namespace Microsoft.Extensions.DependencyModel { o[DependencyContextStrings.DefinesPropertyName] = new JArray(compilationOptions.Defines); } - if (compilationOptions.LanguageVersion != null) - { - o[DependencyContextStrings.LanguageVersionPropertyName] = compilationOptions.LanguageVersion; - } - if (compilationOptions.Platform != null) - { - o[DependencyContextStrings.PlatformPropertyName] = compilationOptions.Platform; - } - if (compilationOptions.AllowUnsafe != null) - { - o[DependencyContextStrings.AllowUnsafePropertyName] = compilationOptions.AllowUnsafe; - } - if (compilationOptions.WarningsAsErrors != null) - { - o[DependencyContextStrings.WarningsAsErrorsPropertyName] = compilationOptions.WarningsAsErrors; - } - if (compilationOptions.Optimize != null) - { - o[DependencyContextStrings.OptimizePropertyName] = compilationOptions.Optimize; - } - if (compilationOptions.KeyFile != null) - { - o[DependencyContextStrings.KeyFilePropertyName] = compilationOptions.KeyFile; - } - if (compilationOptions.DelaySign != null) - { - o[DependencyContextStrings.DelaySignPropertyName] = compilationOptions.DelaySign; - } - if (compilationOptions.PublicSign != null) - { - o[DependencyContextStrings.PublicSignPropertyName] = compilationOptions.PublicSign; - } - if (compilationOptions.DebugType != null) - { - o[DependencyContextStrings.DebugTypePropertyName] = compilationOptions.DebugType; - } - if (compilationOptions.EmitEntryPoint != null) - { - o[DependencyContextStrings.EmitEntryPointPropertyName] = compilationOptions.EmitEntryPoint; - } + AddPropertyIfNotNull(o, DependencyContextStrings.LanguageVersionPropertyName, compilationOptions.LanguageVersion); + AddPropertyIfNotNull(o, DependencyContextStrings.PlatformPropertyName, compilationOptions.Platform); + AddPropertyIfNotNull(o, DependencyContextStrings.AllowUnsafePropertyName, compilationOptions.AllowUnsafe); + AddPropertyIfNotNull(o, DependencyContextStrings.WarningsAsErrorsPropertyName, compilationOptions.WarningsAsErrors); + AddPropertyIfNotNull(o, DependencyContextStrings.OptimizePropertyName, compilationOptions.Optimize); + AddPropertyIfNotNull(o, DependencyContextStrings.KeyFilePropertyName, compilationOptions.KeyFile); + AddPropertyIfNotNull(o, DependencyContextStrings.DelaySignPropertyName, compilationOptions.DelaySign); + AddPropertyIfNotNull(o, DependencyContextStrings.PublicSignPropertyName, compilationOptions.PublicSign); + AddPropertyIfNotNull(o, DependencyContextStrings.EmitEntryPointPropertyName, compilationOptions.EmitEntryPoint); + AddPropertyIfNotNull(o, DependencyContextStrings.GenerateXmlDocumentationPropertyName, compilationOptions.GenerateXmlDocumentation); + AddPropertyIfNotNull(o, DependencyContextStrings.DebugTypePropertyName, compilationOptions.DebugType); return o; - } + } + + private void AddPropertyIfNotNull(JObject o, string name, T value) + { + if (value != null) + { + o[name] = value.ToString(); + } + } private JObject WriteTargets(DependencyContext context) { + if (context.IsPortable) + { + return new JObject( + new JProperty(context.TargetFramework, WritePortableTarget(context.RuntimeLibraries, context.CompileLibraries)) + ); + } + return new JObject( - new JProperty(context.Target, WriteTarget(context.CompileLibraries)), - new JProperty(context.Target + DependencyContextStrings.VersionSeperator + context.Runtime, + new JProperty(context.TargetFramework, WriteTarget(context.CompileLibraries)), + new JProperty(context.TargetFramework + DependencyContextStrings.VersionSeperator + context.Runtime, WriteTarget(context.RuntimeLibraries)) ); } @@ -95,7 +103,41 @@ namespace Microsoft.Extensions.DependencyModel { return new JObject( libraries.Select(library => - new JProperty(library.PackageName + DependencyContextStrings.VersionSeperator + library.Version, WriteTargetLibrary(library)))); + new JProperty(library.Name + DependencyContextStrings.VersionSeperator + library.Version, WriteTargetLibrary(library)))); + } + + private JObject WritePortableTarget(IReadOnlyList runtimeLibraries, IReadOnlyList compilationLibraries) + { + var runtimeLookup = runtimeLibraries.ToDictionary(l => l.Name); + var compileLookup = compilationLibraries.ToDictionary(l => l.Name); + + var targetObject = new JObject(); + + foreach (var packageName in runtimeLookup.Keys.Concat(compileLookup.Keys).Distinct()) + { + RuntimeLibrary runtimeLibrary; + runtimeLookup.TryGetValue(packageName, out runtimeLibrary); + + CompilationLibrary compilationLibrary; + compileLookup.TryGetValue(packageName, out compilationLibrary); + + if (compilationLibrary != null && runtimeLibrary != null) + { + Debug.Assert(compilationLibrary.Serviceable == runtimeLibrary.Serviceable); + Debug.Assert(compilationLibrary.Version == runtimeLibrary.Version); + Debug.Assert(compilationLibrary.Hash == runtimeLibrary.Hash); + Debug.Assert(compilationLibrary.Type == runtimeLibrary.Type); + } + + var library = (Library)compilationLibrary ?? (Library)runtimeLibrary; + targetObject.Add( + new JProperty(library.Name + DependencyContextStrings.VersionSeperator + library.Version, + WritePortableTargetLibrary(runtimeLibrary, compilationLibrary) + ) + ); + + } + return targetObject; } private JObject WriteTargetLibrary(Library library) @@ -106,7 +148,7 @@ namespace Microsoft.Extensions.DependencyModel var runtimeLibrary = library as RuntimeLibrary; if (runtimeLibrary != null) { - propertyName = DependencyContextStrings.RunTimeAssembliesKey; + propertyName = DependencyContextStrings.RuntimeAssembliesKey; assemblies = runtimeLibrary.Assemblies.Select(assembly => assembly.Path).ToArray(); } else @@ -131,12 +173,75 @@ namespace Microsoft.Extensions.DependencyModel ); } - private JObject WriteAssemblies(IReadOnlyList assemblies) + private JObject WritePortableTargetLibrary(RuntimeLibrary runtimeLibrary, CompilationLibrary compilationLibrary) + { + var libraryObject = new JObject(); + var dependencies = new HashSet(); + + if (runtimeLibrary != null) + { + libraryObject.Add(new JProperty(DependencyContextStrings.RuntimeAssembliesKey, + WriteAssemblies(runtimeLibrary.Assemblies.Select(a => a.Path))) + ); + if (runtimeLibrary.RuntimeTargets.Any()) + { + libraryObject.Add(new JProperty( + DependencyContextStrings.RuntimeTargetsPropertyName, + new JObject(runtimeLibrary.RuntimeTargets.SelectMany(WriteRuntimeTarget))) + ); + } + + dependencies.UnionWith(runtimeLibrary.Dependencies); + } + + if (compilationLibrary != null) + { + libraryObject.Add(new JProperty(DependencyContextStrings.CompileTimeAssembliesKey, + WriteAssemblies(compilationLibrary.Assemblies)) + ); + dependencies.UnionWith(compilationLibrary.Dependencies); + } + + libraryObject.Add( + new JProperty(DependencyContextStrings.DependenciesPropertyName, WriteDependencies(dependencies))); + + return libraryObject; + } + + private IEnumerable WriteRuntimeTarget(RuntimeTarget target) + { + var runtime = WriteRuntimeTargetAssemblies( + target.Assemblies.Select(a => a.Path), + target.Runtime, + DependencyContextStrings.RuntimeAssetType); + + var native = WriteRuntimeTargetAssemblies( + target.NativeLibraries, + target.Runtime, + DependencyContextStrings.NativeAssetType); + + return runtime.Concat(native); + } + + private IEnumerable WriteRuntimeTargetAssemblies(IEnumerable assemblies, string runtime, string assetType) + { + foreach (var assembly in assemblies) + { + yield return new JProperty(assembly, + new JObject( + new JProperty(DependencyContextStrings.RidPropertyName, runtime), + new JProperty(DependencyContextStrings.AssetTypePropertyName, assetType) + ) + ); + } + } + + private JObject WriteAssemblies(IEnumerable assemblies) { return new JObject(assemblies.Select(assembly => new JProperty(assembly, new JObject()))); } - private JObject WriteDependencies(IReadOnlyList dependencies) + private JObject WriteDependencies(IEnumerable dependencies) { return new JObject( dependencies.Select(dependency => new JProperty(dependency.Name, dependency.Version)) @@ -147,7 +252,7 @@ namespace Microsoft.Extensions.DependencyModel { var allLibraries = context.RuntimeLibraries.Cast().Concat(context.CompileLibraries) - .GroupBy(library => library.PackageName + DependencyContextStrings.VersionSeperator + library.Version); + .GroupBy(library => library.Name + DependencyContextStrings.VersionSeperator + library.Version); return new JObject(allLibraries.Select(libraries=> new JProperty(libraries.Key, WriteLibrary(libraries.First())))); } @@ -155,7 +260,7 @@ namespace Microsoft.Extensions.DependencyModel private JObject WriteLibrary(Library library) { return new JObject( - new JProperty(DependencyContextStrings.TypePropertyName, library.LibraryType), + new JProperty(DependencyContextStrings.TypePropertyName, library.Type), new JProperty(DependencyContextStrings.ServiceablePropertyName, library.Serviceable), new JProperty(DependencyContextStrings.Sha512PropertyName, library.Hash) ); diff --git a/src/Microsoft.Extensions.DependencyModel/Library.cs b/src/Microsoft.Extensions.DependencyModel/Library.cs index 1dcd2b169..74c5cc2ef 100644 --- a/src/Microsoft.Extensions.DependencyModel/Library.cs +++ b/src/Microsoft.Extensions.DependencyModel/Library.cs @@ -7,19 +7,19 @@ namespace Microsoft.Extensions.DependencyModel { public class Library { - public Library(string libraryType, string packageName, string version, string hash, Dependency[] dependencies, bool serviceable) + public Library(string type, string name, string version, string hash, Dependency[] dependencies, bool serviceable) { - LibraryType = libraryType; - PackageName = packageName; + Type = type; + Name = name; Version = version; Hash = hash; Dependencies = dependencies; Serviceable = serviceable; } - public string LibraryType { get; } + public string Type { get; } - public string PackageName { get; } + public string Name { get; } public string Version { get; } diff --git a/src/Microsoft.Extensions.DependencyModel/Resolution/AppBaseCompilationAssemblyResolver.cs b/src/Microsoft.Extensions.DependencyModel/Resolution/AppBaseCompilationAssemblyResolver.cs index f5f681f86..9c047365f 100644 --- a/src/Microsoft.Extensions.DependencyModel/Resolution/AppBaseCompilationAssemblyResolver.cs +++ b/src/Microsoft.Extensions.DependencyModel/Resolution/AppBaseCompilationAssemblyResolver.cs @@ -37,11 +37,11 @@ namespace Microsoft.Extensions.DependencyModel.Resolution public bool TryResolveAssemblyPaths(CompilationLibrary library, List assemblies) { - var isProject = string.Equals(library.LibraryType, "project", StringComparison.OrdinalIgnoreCase); + var isProject = string.Equals(library.Type, "project", StringComparison.OrdinalIgnoreCase); if (!isProject && - !string.Equals(library.LibraryType, "package", StringComparison.OrdinalIgnoreCase) && - !string.Equals(library.LibraryType, "referenceassembly", StringComparison.OrdinalIgnoreCase)) + !string.Equals(library.Type, "package", StringComparison.OrdinalIgnoreCase) && + !string.Equals(library.Type, "referenceassembly", StringComparison.OrdinalIgnoreCase)) { return false; } diff --git a/src/Microsoft.Extensions.DependencyModel/Resolution/PackageCacheCompilationAssemblyResolver.cs b/src/Microsoft.Extensions.DependencyModel/Resolution/PackageCacheCompilationAssemblyResolver.cs index 2b955b0c1..156ea0622 100644 --- a/src/Microsoft.Extensions.DependencyModel/Resolution/PackageCacheCompilationAssemblyResolver.cs +++ b/src/Microsoft.Extensions.DependencyModel/Resolution/PackageCacheCompilationAssemblyResolver.cs @@ -36,7 +36,7 @@ namespace Microsoft.Extensions.DependencyModel.Resolution public bool TryResolveAssemblyPaths(CompilationLibrary library, List assemblies) { - if (!string.Equals(library.LibraryType, "package", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(library.Type, "package", StringComparison.OrdinalIgnoreCase)) { return false; } @@ -46,14 +46,14 @@ namespace Microsoft.Extensions.DependencyModel.Resolution 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}'"); + throw new InvalidOperationException($"Invalid hash entry '{library.Hash}' for package '{library.Name}'"); } string packagePath; if (ResolverUtils.TryResolvePackagePath(_fileSystem, library, _packageCacheDirectory, out packagePath)) { var hashAlgorithm = library.Hash.Substring(0, hashSplitterPos); - var cacheHashPath = Path.Combine(packagePath, $"{library.PackageName}.{library.Version}.nupkg.{hashAlgorithm}"); + var cacheHashPath = Path.Combine(packagePath, $"{library.Name}.{library.Version}.nupkg.{hashAlgorithm}"); if (_fileSystem.File.Exists(cacheHashPath) && _fileSystem.File.ReadAllText(cacheHashPath) == library.Hash.Substring(hashSplitterPos + 1)) diff --git a/src/Microsoft.Extensions.DependencyModel/Resolution/PackageCompilationAssemblyResolver.cs b/src/Microsoft.Extensions.DependencyModel/Resolution/PackageCompilationAssemblyResolver.cs index 4f4928473..680d24e99 100644 --- a/src/Microsoft.Extensions.DependencyModel/Resolution/PackageCompilationAssemblyResolver.cs +++ b/src/Microsoft.Extensions.DependencyModel/Resolution/PackageCompilationAssemblyResolver.cs @@ -65,7 +65,7 @@ namespace Microsoft.Extensions.DependencyModel.Resolution public bool TryResolveAssemblyPaths(CompilationLibrary library, List assemblies) { if (string.IsNullOrEmpty(_nugetPackageDirectory) || - !string.Equals(library.LibraryType, "package", StringComparison.OrdinalIgnoreCase)) + !string.Equals(library.Type, "package", StringComparison.OrdinalIgnoreCase)) { return false; } diff --git a/src/Microsoft.Extensions.DependencyModel/Resolution/ReferenceAssemblyPathResolver.cs b/src/Microsoft.Extensions.DependencyModel/Resolution/ReferenceAssemblyPathResolver.cs index 53a36abdb..523b20eca 100644 --- a/src/Microsoft.Extensions.DependencyModel/Resolution/ReferenceAssemblyPathResolver.cs +++ b/src/Microsoft.Extensions.DependencyModel/Resolution/ReferenceAssemblyPathResolver.cs @@ -41,7 +41,7 @@ namespace Microsoft.Extensions.DependencyModel.Resolution public bool TryResolveAssemblyPaths(CompilationLibrary library, List assemblies) { - if (!string.Equals(library.LibraryType, "referenceassembly", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(library.Type, "referenceassembly", StringComparison.OrdinalIgnoreCase)) { return false; } @@ -50,7 +50,7 @@ namespace Microsoft.Extensions.DependencyModel.Resolution string fullName; if (!TryResolveReferenceAssembly(assembly, out fullName)) { - throw new InvalidOperationException($"Can not find reference assembly '{assembly}' file for package {library.PackageName}"); + throw new InvalidOperationException($"Can not find reference assembly '{assembly}' file for package {library.Name}"); } assemblies.Add(fullName); } diff --git a/src/Microsoft.Extensions.DependencyModel/Resolution/ResolverUtils.cs b/src/Microsoft.Extensions.DependencyModel/Resolution/ResolverUtils.cs index bfcf7f44f..b9b93d276 100644 --- a/src/Microsoft.Extensions.DependencyModel/Resolution/ResolverUtils.cs +++ b/src/Microsoft.Extensions.DependencyModel/Resolution/ResolverUtils.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.DependencyModel.Resolution { internal static bool TryResolvePackagePath(IFileSystem fileSystem, CompilationLibrary library, string basePath, out string packagePath) { - packagePath = Path.Combine(basePath, library.PackageName, library.Version); + packagePath = Path.Combine(basePath, library.Name, library.Version); if (fileSystem.Directory.Exists(packagePath)) { return true; @@ -27,7 +27,7 @@ namespace Microsoft.Extensions.DependencyModel.Resolution string fullName; if (!TryResolveAssemblyFile(fileSystem, basePath, assembly, out fullName)) { - throw new InvalidOperationException($"Can not find assembly file for package {library.PackageName} at '{fullName}'"); + throw new InvalidOperationException($"Can not find assembly file for package {library.Name} at '{fullName}'"); } yield return fullName; } diff --git a/src/Microsoft.Extensions.DependencyModel/RuntimeAssembly.cs b/src/Microsoft.Extensions.DependencyModel/RuntimeAssembly.cs index fa1bc066d..dcbee3a14 100644 --- a/src/Microsoft.Extensions.DependencyModel/RuntimeAssembly.cs +++ b/src/Microsoft.Extensions.DependencyModel/RuntimeAssembly.cs @@ -10,11 +10,6 @@ namespace Microsoft.Extensions.DependencyModel { private readonly string _assemblyName; - public RuntimeAssembly(string path) - : this(System.IO.Path.GetFileNameWithoutExtension(path), path) - { - } - public RuntimeAssembly(string assemblyName, string path) { _assemblyName = assemblyName; @@ -24,5 +19,10 @@ namespace Microsoft.Extensions.DependencyModel public AssemblyName Name => new AssemblyName(_assemblyName); public string Path { get; } + + public static RuntimeAssembly Create(string path) + { + return new RuntimeAssembly(System.IO.Path.GetFileNameWithoutExtension(path), path); + } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.DependencyModel/RuntimeLibrary.cs b/src/Microsoft.Extensions.DependencyModel/RuntimeLibrary.cs index 39369c6d5..4519e8590 100644 --- a/src/Microsoft.Extensions.DependencyModel/RuntimeLibrary.cs +++ b/src/Microsoft.Extensions.DependencyModel/RuntimeLibrary.cs @@ -8,12 +8,23 @@ namespace Microsoft.Extensions.DependencyModel { public class RuntimeLibrary : Library { - public RuntimeLibrary(string libraryType, string packageName, string version, string hash, string[] assemblies, Dependency[] dependencies, bool serviceable) - : base(libraryType, packageName, version, hash, dependencies, serviceable) + public RuntimeLibrary( + string type, + string name, + string version, + string hash, + RuntimeAssembly[] assemblies, + RuntimeTarget[] subTargets, + Dependency[] dependencies, + bool serviceable) + : base(type, name, version, hash, dependencies, serviceable) { - Assemblies = assemblies.Select(path => new RuntimeAssembly(path)).ToArray(); + Assemblies = assemblies; + RuntimeTargets = subTargets; } public IReadOnlyList Assemblies { get; } + + public IReadOnlyList RuntimeTargets { get; } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.DependencyModel/RuntimeTarget.cs b/src/Microsoft.Extensions.DependencyModel/RuntimeTarget.cs new file mode 100644 index 000000000..15b5d9d7f --- /dev/null +++ b/src/Microsoft.Extensions.DependencyModel/RuntimeTarget.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Microsoft.Extensions.DependencyModel +{ + public class RuntimeTarget + { + public RuntimeTarget(string runtime, IReadOnlyList assemblies, IReadOnlyList nativeLibraries) + { + Runtime = runtime; + Assemblies = assemblies; + NativeLibraries = nativeLibraries; + } + + public string Runtime { get; } + + public IReadOnlyList Assemblies { get; } + + public IReadOnlyList NativeLibraries { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.DependencyModel/project.json b/src/Microsoft.Extensions.DependencyModel/project.json index c439b0a1b..1ad395b56 100644 --- a/src/Microsoft.Extensions.DependencyModel/project.json +++ b/src/Microsoft.Extensions.DependencyModel/project.json @@ -10,6 +10,10 @@ "keyFile": "../../tools/Key.snk" }, "dependencies": { + "Microsoft.Extensions.HashCodeCombiner.Sources": { + "type": "build", + "version": "1.0.0-rc2-16054" + }, "Microsoft.DotNet.InternalAbstractions": { "target": "project", "version": "1.0.0-*" diff --git a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs index 674542dca..aeb692979 100644 --- a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs +++ b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs @@ -111,11 +111,12 @@ namespace Microsoft.DotNet.Tools.Compiler if (compilationOptions.PreserveCompilationContext == true) { - var dependencyContext = DependencyContextBuilder.Build(compilationOptions, - exporter, - args.ConfigValue, + var allExports = exporter.GetAllExports().ToList(); + var dependencyContext = new DependencyContextBuilder().Build(compilationOptions, + allExports, + allExports, context.TargetFramework, - context.RuntimeIdentifier); + context.RuntimeIdentifier ?? string.Empty); var writer = new DependencyContextWriter(); var depsJsonFile = Path.Combine(intermediateOutputPath, context.ProjectFile.Name + "dotnet-compile.deps.json"); diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextBuilderTests.cs b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextBuilderTests.cs new file mode 100644 index 000000000..13eaefaa0 --- /dev/null +++ b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextBuilderTests.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using FluentAssertions; +using Microsoft.DotNet.ProjectModel; +using Microsoft.DotNet.ProjectModel.Compilation; +using Microsoft.DotNet.ProjectModel.Graph; +using NuGet.Frameworks; +using NuGet.Versioning; +using Xunit; + +namespace Microsoft.Extensions.DependencyModel.Tests +{ + public class DependencyContextBuilderTests + { + private string _referenceAssembliesPath = Path.Combine("reference", "assemblies"); + private NuGetFramework _defaultFramework; + private string _defaultName = "Library.Name"; + private string _defaultHash = "Hash"; + private NuGetVersion _defaultVersion = new NuGetVersion(1, 2, 3, new []{"dev"}, string.Empty); + + public DependencyContext Build(CommonCompilerOptions compilerOptions = null, + IEnumerable compilationExports = null, + IEnumerable runtimeExports = null, + NuGetFramework target = null, + string runtime = null) + { + _defaultFramework = NuGetFramework.Parse("net451"); + return new DependencyContextBuilder(_referenceAssembliesPath).Build( + compilerOptions ?? new CommonCompilerOptions(), + compilationExports ?? new LibraryExport[] { }, + runtimeExports ?? new LibraryExport[] {}, + target ?? _defaultFramework, + runtime ?? string.Empty); + } + + [Fact] + public void PreservesCompilationOptions() + { + var context = Build(new CommonCompilerOptions() + { + AllowUnsafe = true, + Defines = new[] {"Define", "D"}, + DelaySign = true, + EmitEntryPoint = true, + GenerateXmlDocumentation = true, + KeyFile = "Key.snk", + LanguageVersion = "C#8", + Optimize = true, + Platform = "Platform", + PublicSign = true, + WarningsAsErrors = true + }); + + context.CompilationOptions.AllowUnsafe.Should().Be(true); + context.CompilationOptions.DelaySign.Should().Be(true); + context.CompilationOptions.EmitEntryPoint.Should().Be(true); + context.CompilationOptions.GenerateXmlDocumentation.Should().Be(true); + context.CompilationOptions.Optimize.Should().Be(true); + context.CompilationOptions.PublicSign.Should().Be(true); + context.CompilationOptions.WarningsAsErrors.Should().Be(true); + + context.CompilationOptions.Defines.Should().BeEquivalentTo(new[] { "Define", "D" }); + context.CompilationOptions.KeyFile.Should().Be("Key.snk"); + context.CompilationOptions.LanguageVersion.Should().Be("C#8"); + context.CompilationOptions.Platform.Should().Be("Platform"); + } + + [Fact] + public void FillsRuntimeAndTarget() + { + var context = Build(target: new NuGetFramework("SomeFramework",new Version(1,2)), runtime: "win8-x86"); + context.Runtime.Should().Be("win8-x86"); + context.TargetFramework.Should().Be("SomeFramework,Version=v1.2"); + } + + [Fact] + public void TakesServicableFromPackageDescription() + { + var context = Build(runtimeExports: new[] + { + Export(PackageDescription("Pack.Age", servicable: true)) + }); + + var lib = context.RuntimeLibraries.Single(); + lib.Serviceable.Should().BeTrue(); + } + + [Fact] + public void FillsRuntimeLibraryProperties() + { + var context = Build(runtimeExports: new[] + { + Export(PackageDescription("Pack.Age", + servicable: true, + hash: "Hash", + version: new NuGetVersion(1, 2, 3), + dependencies: new[] + { + new LibraryRange("System.Collections", + new VersionRange(new NuGetVersion(2, 1, 2)), + LibraryType.ReferenceAssembly, + LibraryDependencyType.Default) + }), + runtimeAssemblies: new[] + { + new LibraryAsset("Dll", "lib/Pack.Age.dll", ""), + } + ), + Export(ReferenceAssemblyDescription("System.Collections", + version: new NuGetVersion(3, 3, 3)), + runtimeAssemblies: new[] + { + new LibraryAsset("Dll", "", "System.Collections.dll"), + }) + }); + + context.RuntimeLibraries.Should().HaveCount(2); + + var lib = context.RuntimeLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject; + lib.Type.Should().Be("package"); + lib.Serviceable.Should().BeTrue(); + lib.Hash.Should().Be("sha512-Hash"); + lib.Version.Should().Be("1.2.3"); + lib.Dependencies.Should().OnlyContain(l => l.Name == "System.Collections" && l.Version == "3.3.3"); + lib.Assemblies.Should().OnlyContain(l => l.Path == "lib/Pack.Age.dll"); + + var asm = context.RuntimeLibraries.Should().Contain(l => l.Name == "System.Collections").Subject; + asm.Type.Should().Be("referenceassembly"); + asm.Version.Should().Be("3.3.3"); + asm.Hash.Should().BeEmpty(); + asm.Dependencies.Should().BeEmpty(); + asm.Assemblies.Should().OnlyContain(l => l.Path == "System.Collections.dll"); + } + + + [Fact] + public void FillsCompileLibraryProperties() + { + var context = Build(compilationExports: new[] + { + Export(PackageDescription("Pack.Age", + servicable: true, + hash: "Hash", + version: new NuGetVersion(1, 2, 3), + dependencies: new[] + { + new LibraryRange("System.Collections", + new VersionRange(new NuGetVersion(2, 1, 2)), + LibraryType.ReferenceAssembly, + LibraryDependencyType.Default) + }), + compilationAssemblies: new[] + { + new LibraryAsset("Dll", "lib/Pack.Age.dll", ""), + } + ), + Export(ReferenceAssemblyDescription("System.Collections", + version: new NuGetVersion(3, 3, 3)), + compilationAssemblies: new[] + { + new LibraryAsset("Dll", "", "System.Collections.dll"), + }) + }); + + context.CompileLibraries.Should().HaveCount(2); + + var lib = context.CompileLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject; + lib.Type.Should().Be("package"); + lib.Serviceable.Should().BeTrue(); + lib.Hash.Should().Be("sha512-Hash"); + lib.Version.Should().Be("1.2.3"); + lib.Dependencies.Should().OnlyContain(l => l.Name == "System.Collections" && l.Version == "3.3.3"); + lib.Assemblies.Should().OnlyContain(a => a == "lib/Pack.Age.dll"); + + var asm = context.CompileLibraries.Should().Contain(l => l.Name == "System.Collections").Subject; + asm.Type.Should().Be("referenceassembly"); + asm.Version.Should().Be("3.3.3"); + asm.Hash.Should().BeEmpty(); + asm.Dependencies.Should().BeEmpty(); + asm.Assemblies.Should().OnlyContain(a => a == "System.Collections.dll"); + } + + [Fact] + public void ReferenceAssembliesPathRelativeToDefaultRoot() + { + var context = Build(compilationExports: new[] + { + Export(ReferenceAssemblyDescription("System.Collections", + version: new NuGetVersion(3, 3, 3)), + compilationAssemblies: new[] + { + new LibraryAsset("Dll", "", Path.Combine(_referenceAssembliesPath, "sub", "System.Collections.dll")) + }) + }); + + var asm = context.CompileLibraries.Should().Contain(l => l.Name == "System.Collections").Subject; + asm.Assemblies.Should().OnlyContain(a => a == Path.Combine("sub", "System.Collections.dll")); + } + + [Fact] + public void SkipsBuildDependencies() + { + var context = Build(compilationExports: new[] + { + Export(PackageDescription("Pack.Age", + dependencies: new[] + { + new LibraryRange("System.Collections", + new VersionRange(new NuGetVersion(2, 1, 2)), + LibraryType.ReferenceAssembly, + LibraryDependencyType.Build) + }) + ), + Export(ReferenceAssemblyDescription("System.Collections", + version: new NuGetVersion(3, 3, 3))) + }); + + var lib = context.CompileLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject; + lib.Dependencies.Should().BeEmpty(); + } + + private LibraryExport Export( + LibraryDescription description, + IEnumerable compilationAssemblies = null, + IEnumerable runtimeAssemblies = null) + { + return new LibraryExport( + description, + compilationAssemblies ?? Enumerable.Empty(), + Enumerable.Empty(), + runtimeAssemblies ?? Enumerable.Empty(), + Enumerable.Empty(), + Enumerable.Empty(), + Enumerable.Empty(), + Enumerable.Empty() + ); + } + + private PackageDescription PackageDescription( + string name = null, + NuGetVersion version = null, + string hash = null, + IEnumerable dependencies = null, + bool? servicable = null) + { + return new PackageDescription( + "PATH", + new LockFilePackageLibrary() + { + Files = new string[] { }, + IsServiceable = servicable ?? false, + Name = name ?? _defaultName, + Version = version ?? _defaultVersion, + Sha512 = hash ?? _defaultHash + }, + new LockFileTargetLibrary(), + dependencies ?? Enumerable.Empty(), + true, + true); + } + + private ProjectDescription ProjectDescription( + string name = null, + NuGetVersion version = null, + IEnumerable dependencies = null) + { + return new ProjectDescription( + new LibraryRange( + name ?? _defaultName, + new VersionRange(version ?? _defaultVersion), + LibraryType.Project, + LibraryDependencyType.Default + ), + new Project(), + dependencies ?? Enumerable.Empty(), + new TargetFrameworkInformation(), + true); + } + + private LibraryDescription ReferenceAssemblyDescription( + string name = null, + NuGetVersion version = null) + { + return new LibraryDescription( + new LibraryIdentity( + name ?? _defaultName, + version ?? _defaultVersion, + LibraryType.ReferenceAssembly), + string.Empty, // Framework assemblies don't have hashes + "PATH", + Enumerable.Empty(), + _defaultFramework, + true, + true); + } + + + } +} diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextCsvReaderTests.cs b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextCsvReaderTests.cs index e5ebe1a3c..7c19c1c69 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextCsvReaderTests.cs +++ b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextCsvReaderTests.cs @@ -32,8 +32,8 @@ namespace Microsoft.Extensions.DependencyModel.Tests "); context.RuntimeLibraries.Should().HaveCount(1); var library = context.RuntimeLibraries.Single(); - library.LibraryType.Should().Be("Package"); - library.PackageName.Should().Be("runtime.any.System.AppContext"); + library.Type.Should().Be("Package"); + library.Name.Should().Be("runtime.any.System.AppContext"); library.Version.Should().Be("4.1.0-rc2-23811"); library.Hash.Should().Be("sha512-1"); library.Assemblies.Should().HaveCount(2).And diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs new file mode 100644 index 000000000..b5fb29a10 --- /dev/null +++ b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonReaderTest.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; + +namespace Microsoft.Extensions.DependencyModel.Tests +{ + public class DependencyContextJsonReaderTest + { + private DependencyContext Read(string text) + { + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(text))) + { + return new DependencyContextJsonReader().Read(stream); + } + } + + [Fact] + public void ReadsRuntimeTargetInfo() + { + var context = Read( +@"{ + ""runtimeTarget"": "".NETStandardApp,Version=v1.5/osx.10.10-x64"", + ""targets"": { + "".NETStandardApp,Version=v1.5"": {}, + "".NETStandardApp,Version=v1.5/osx.10.10-x64"": {}, + } +}"); + context.IsPortable.Should().BeFalse(); + context.TargetFramework.Should().Be(".NETStandardApp,Version=v1.5"); + context.Runtime.Should().Be("osx.10.10-x64"); + } + + [Fact] + public void DefaultsToPortable() + { + var context = Read( +@"{ +}"); + context.IsPortable.Should().BeTrue(); + } + + [Fact] + public void SetsPortableIfRuntimeTargetHasNoRid() + { + var context = Read( +@"{ + ""runtimeTarget"": "".NETStandardApp,Version=v1.5"", + ""targets"": { + "".NETStandardApp,Version=v1.5"": {} + } +}"); + context.IsPortable.Should().BeTrue(); + } + + [Fact] + public void SetsNotPortableIfRuntimeTargetHasRid() + { + var context = Read( +@"{ + ""runtimeTarget"": "".NETStandardApp,Version=v1.5/osx.10.10-x64"", + ""targets"": { + "".NETStandardApp,Version=v1.5"": {}, + "".NETStandardApp,Version=v1.5/osx.10.10-x64"": {} + } +}"); + context.IsPortable.Should().BeFalse(); + } + + [Fact] + public void ReadsMainTarget() + { + var context = Read( +@"{ + ""targets"": { + "".NETStandardApp,Version=v1.5"": {} + } +}"); + context.TargetFramework.Should().Be(".NETStandardApp,Version=v1.5"); + } + + [Fact] + public void ReadsRuntimeGraph() + { + var context = Read( +@"{ + ""runtimes"": { + "".NETStandardApp,Version=v1.5"": { + ""osx.10.10-x64"": [ ], + ""osx.10.11-x64"": [ ""osx"" ], + ""rhel.7-x64"": [ ""linux-x64"", ""unix"" ] + } + } +}"); + context.RuntimeGraph.Should().Contain(p => p.Key == "osx.10.10-x64").Which + .Value.Should().BeEquivalentTo(); + + context.RuntimeGraph.Should().Contain(p => p.Key == "osx.10.11-x64").Which + .Value.Should().BeEquivalentTo("osx"); + + context.RuntimeGraph.Should().Contain(p => p.Key == "rhel.7-x64").Which + .Value.Should().BeEquivalentTo("linux-x64", "unix"); + } + + [Fact] + public void ReadsCompilationTarget() + { + var context = Read( +@"{ + ""targets"": { + "".NETStandardApp,Version=v1.5"": { + ""MyApp/1.0.1"": { + ""dependencies"": { + ""AspNet.Mvc"": ""1.0.0"" + }, + ""compile"": { + ""MyApp.dll"": { } + } + }, + ""System.Banana/1.0.0"": { + ""dependencies"": { + ""System.Foo"": ""1.0.0"" + }, + ""compile"": { + ""ref/dotnet5.4/System.Banana.dll"": { } + } + } + } + }, + ""libraries"":{ + ""MyApp/1.0.1"": { + ""type"": ""project"" + }, + ""System.Banana/1.0.0"": { + ""type"": ""package"", + ""serviceable"": false, + ""sha512"": ""HASH-System.Banana"" + }, + } +}"); + context.CompileLibraries.Should().HaveCount(2); + var project = context.CompileLibraries.Should().Contain(l => l.Name == "MyApp").Subject; + project.Version.Should().Be("1.0.1"); + project.Assemblies.Should().BeEquivalentTo("MyApp.dll"); + project.Type.Should().Be("project"); + + var package = context.CompileLibraries.Should().Contain(l => l.Name == "System.Banana").Subject; + package.Version.Should().Be("1.0.0"); + package.Assemblies.Should().BeEquivalentTo("ref/dotnet5.4/System.Banana.dll"); + package.Hash.Should().Be("HASH-System.Banana"); + package.Type.Should().Be("package"); + package.Serviceable.Should().Be(false); + } + + + [Fact] + public void ReadsRuntimeLibrariesWithSubtargetsFromMainTargetForPortable() + { + var context = Read( +@"{ + ""runtimeTarget"": "".NETStandardApp,Version=v1.5"", + ""targets"": { + "".NETStandardApp,Version=v1.5"": { + ""MyApp/1.0.1"": { + ""dependencies"": { + ""AspNet.Mvc"": ""1.0.0"" + }, + ""runtime"": { + ""MyApp.dll"": { } + } + }, + ""System.Banana/1.0.0"": { + ""dependencies"": { + ""System.Foo"": ""1.0.0"" + }, + ""runtime"": { + ""lib/dotnet5.4/System.Banana.dll"": { } + }, + ""runtimeTargets"": { + ""lib/win7/System.Banana.dll"": { ""assetType"": ""runtime"", ""rid"": ""win7-x64""}, + ""lib/win7/Banana.dll"": { ""assetType"": ""native"", ""rid"": ""win7-x64""} + } + } + } + }, + ""libraries"":{ + ""MyApp/1.0.1"": { + ""type"": ""project"", + }, + ""System.Banana/1.0.0"": { + ""type"": ""package"", + ""serviceable"": false, + ""sha512"": ""HASH-System.Banana"" + }, + } +}"); + context.CompileLibraries.Should().HaveCount(2); + var project = context.RuntimeLibraries.Should().Contain(l => l.Name == "MyApp").Subject; + project.Version.Should().Be("1.0.1"); + project.Assemblies.Should().Contain(a => a.Path == "MyApp.dll"); + project.Type.Should().Be("project"); + + + var package = context.RuntimeLibraries.Should().Contain(l => l.Name == "System.Banana").Subject; + package.Version.Should().Be("1.0.0"); + package.Hash.Should().Be("HASH-System.Banana"); + package.Type.Should().Be("package"); + package.Serviceable.Should().Be(false); + package.Assemblies.Should().Contain(a => a.Path == "lib/dotnet5.4/System.Banana.dll"); + + var target = package.RuntimeTargets.Should().Contain(t => t.Runtime == "win7-x64").Subject; + target.Assemblies.Should().Contain(a => a.Path == "lib/win7/System.Banana.dll"); + target.NativeLibraries.Should().Contain("lib/win7/Banana.dll"); + } + + [Fact] + public void ReadsCompilationOptions() + { + var context = Read( +@"{ + ""compilationOptions"": { + ""allowUnsafe"": true, + ""defines"": [""MY"", ""DEFINES""], + ""delaySign"": true, + ""emitEntryPoint"": true, + ""xmlDoc"": true, + ""keyFile"": ""Key.snk"", + ""languageVersion"": ""C#8"", + ""platform"": ""Platform"", + ""publicSign"": true, + ""warningsAsErrors"": true, + ""optimize"": true + } +}"); + context.CompilationOptions.AllowUnsafe.Should().Be(true); + context.CompilationOptions.Defines.Should().BeEquivalentTo(new [] {"MY", "DEFINES"}); + context.CompilationOptions.DelaySign.Should().Be(true); + context.CompilationOptions.EmitEntryPoint.Should().Be(true); + context.CompilationOptions.GenerateXmlDocumentation.Should().Be(true); + context.CompilationOptions.KeyFile.Should().Be("Key.snk"); + context.CompilationOptions.LanguageVersion.Should().Be("C#8"); + context.CompilationOptions.Optimize.Should().Be(true); + context.CompilationOptions.Platform.Should().Be("Platform"); + context.CompilationOptions.PublicSign.Should().Be(true); + context.CompilationOptions.WarningsAsErrors.Should().Be(true); + } + } +} diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonWriterTests.cs b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonWriterTests.cs new file mode 100644 index 000000000..0e1639fde --- /dev/null +++ b/test/Microsoft.Extensions.DependencyModel.Tests/DependencyContextJsonWriterTests.cs @@ -0,0 +1,343 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; +using FluentAssertions; + +namespace Microsoft.Extensions.DependencyModel.Tests +{ + public class DependencyContextJsonWriterTests + { + public JObject Save(DependencyContext dependencyContext) + { + using (var memoryStream = new MemoryStream()) + { + new DependencyContextWriter().Write(dependencyContext, memoryStream); + using (var readStream = new MemoryStream(memoryStream.ToArray())) + { + using (var textReader = new StreamReader(readStream)) + { + using (var reader = new JsonTextReader(textReader)) + { + return JObject.Load(reader); + } + } + } + } + } + + public DependencyContext Create( + string target = null, + string runtime = null, + bool? isPortable = null, + CompilationOptions compilationOptions = null, + CompilationLibrary[] compileLibraries = null, + RuntimeLibrary[] runtimeLibraries = null, + IReadOnlyList> runtimeGraph = null) + { + return new DependencyContext( + target ?? string.Empty, + runtime ?? string.Empty, + isPortable ?? false, + compilationOptions ?? CompilationOptions.Default, + compileLibraries ?? new CompilationLibrary[0], + runtimeLibraries ?? new RuntimeLibrary[0], + runtimeGraph ?? new KeyValuePair[0] + ); + } + + [Fact] + public void SavesRuntimeGraph() + { + var result = Save(Create( + "Target", + "Target/runtime", + runtimeGraph: new[] + { + new KeyValuePair("win7-x64", new [] { "win6", "win5"}), + new KeyValuePair("win8-x64", new [] { "win7-x64"}), + })); + + var runtimes = result.Should().HaveProperty("runtimes") + .Subject.Should().BeOfType().Subject; + + var rids = runtimes.Should().HaveProperty("Target") + .Subject.Should().BeOfType().Subject; + + rids.Should().HaveProperty("win7-x64") + .Subject.Should().BeOfType() + .Which.Values().ShouldBeEquivalentTo(new[] { "win6", "win5" }); + + rids.Should().HaveProperty("win8-x64") + .Subject.Should().BeOfType() + .Which.Values().ShouldBeEquivalentTo(new[] { "win7-x64" }); + } + + [Fact] + public void WritesRuntimeTargetPropertyIfNotPortable() + { + var result = Save(Create( + "Target", + "runtime", + false) + ); + + result.Should().HavePropertyValue("runtimeTarget", "Target/runtime"); + } + + [Fact] + public void WritesMainTargetNameToRuntimeTargetIfPortable() + { + var result = Save(Create( + "Target", + "runtime", + true) + ); + result.Should().HavePropertyValue("runtimeTarget", "Target"); + } + + [Fact] + public void WritesCompilationLibraries() + { + var result = Save(Create( + "Target", + "runtime", + true, + compileLibraries: new[] + { + new CompilationLibrary( + "package", + "PackageName", + "1.2.3", + "HASH", + new [] {"Banana.dll"}, + new [] { + new Dependency("Fruits.Abstract.dll","2.0.0") + }, + true + ) + })); + + // targets + var targets = result.Should().HavePropertyAsObject("targets").Subject; + var target = targets.Should().HavePropertyAsObject("Target").Subject; + var library = target.Should().HavePropertyAsObject("PackageName/1.2.3").Subject; + var dependencies = library.Should().HavePropertyAsObject("dependencies").Subject; + dependencies.Should().HavePropertyValue("Fruits.Abstract.dll", "2.0.0"); + library.Should().HavePropertyAsObject("compile") + .Subject.Should().HaveProperty("Banana.dll"); + + //libraries + var libraries = result.Should().HavePropertyAsObject("libraries").Subject; + library = libraries.Should().HavePropertyAsObject("PackageName/1.2.3").Subject; + library.Should().HavePropertyValue("sha512", "HASH"); + library.Should().HavePropertyValue("type", "package"); + library.Should().HavePropertyValue("serviceable", true); + } + + [Fact] + public void WritesRuntimeLibrariesToRuntimeTarget() + { + var result = Save(Create( + "Target", + "runtime", + true, + runtimeLibraries: new[] + { + new RuntimeLibrary( + "package", + "PackageName", + "1.2.3", + "HASH", + new [] { RuntimeAssembly.Create("Banana.dll")}, + new [] + { + new RuntimeTarget("win7-x64", + new [] { RuntimeAssembly.Create("Banana.Win7-x64.dll") }, + new [] { "Banana.Win7-x64.so" } + ) + }, + new [] { + new Dependency("Fruits.Abstract.dll","2.0.0") + }, + true + ), + })); + + // targets + var targets = result.Should().HavePropertyAsObject("targets").Subject; + var target = targets.Should().HavePropertyAsObject("Target").Subject; + var library = target.Should().HavePropertyAsObject("PackageName/1.2.3").Subject; + var dependencies = library.Should().HavePropertyAsObject("dependencies").Subject; + dependencies.Should().HavePropertyValue("Fruits.Abstract.dll", "2.0.0"); + library.Should().HavePropertyAsObject("runtime") + .Subject.Should().HaveProperty("Banana.dll"); + + var runtimeTargets = library.Should().HavePropertyAsObject("runtimeTargets").Subject; + + var runtimeAssembly = runtimeTargets.Should().HavePropertyAsObject("Banana.Win7-x64.dll").Subject; + runtimeAssembly.Should().HavePropertyValue("rid", "win7-x64"); + runtimeAssembly.Should().HavePropertyValue("assetType", "runtime"); + + var nativeLibrary = runtimeTargets.Should().HavePropertyAsObject("Banana.Win7-x64.so").Subject; + nativeLibrary.Should().HavePropertyValue("rid", "win7-x64"); + nativeLibrary.Should().HavePropertyValue("assetType", "native"); + + //libraries + var libraries = result.Should().HavePropertyAsObject("libraries").Subject; + library = libraries.Should().HavePropertyAsObject("PackageName/1.2.3").Subject; + library.Should().HavePropertyValue("sha512", "HASH"); + library.Should().HavePropertyValue("type", "package"); + library.Should().HavePropertyValue("serviceable", true); + } + + [Fact] + public void MergesRuntimeAndCompileLibrariesForPortable() + { + var result = Save(Create( + "Target", + "runtime", + true, + compileLibraries: new[] + { + new CompilationLibrary( + "package", + "PackageName", + "1.2.3", + "HASH", + new [] { "ref/Banana.dll" }, + new [] { + new Dependency("Fruits.Abstract.dll","2.0.0") + }, + true + ) + }, + runtimeLibraries: new[] + { + new RuntimeLibrary( + "package", + "PackageName", + "1.2.3", + "HASH", + new [] { RuntimeAssembly.Create("Banana.dll")}, + new [] + { + new RuntimeTarget("win7-x64", + new [] { RuntimeAssembly.Create("Banana.Win7-x64.dll") }, + new [] { "Banana.Win7-x64.so" } + ) + }, + new [] { + new Dependency("Fruits.Abstract.dll","2.0.0") + }, + true + ), + })); + + // targets + var targets = result.Should().HavePropertyAsObject("targets").Subject; + var target = targets.Should().HavePropertyAsObject("Target").Subject; + var library = target.Should().HavePropertyAsObject("PackageName/1.2.3").Subject; + var dependencies = library.Should().HavePropertyAsObject("dependencies").Subject; + dependencies.Should().HavePropertyValue("Fruits.Abstract.dll", "2.0.0"); + library.Should().HavePropertyAsObject("runtime") + .Subject.Should().HaveProperty("Banana.dll"); + + library.Should().HavePropertyAsObject("compile") + .Subject.Should().HaveProperty("ref/Banana.dll"); + + var runtimeTargets = library.Should().HavePropertyAsObject("runtimeTargets").Subject; + + var runtimeAssembly = runtimeTargets.Should().HavePropertyAsObject("Banana.Win7-x64.dll").Subject; + runtimeAssembly.Should().HavePropertyValue("rid", "win7-x64"); + runtimeAssembly.Should().HavePropertyValue("assetType", "runtime"); + + var nativeLibrary = runtimeTargets.Should().HavePropertyAsObject("Banana.Win7-x64.so").Subject; + nativeLibrary.Should().HavePropertyValue("rid", "win7-x64"); + nativeLibrary.Should().HavePropertyValue("assetType", "native"); + + //libraries + var libraries = result.Should().HavePropertyAsObject("libraries").Subject; + library = libraries.Should().HavePropertyAsObject("PackageName/1.2.3").Subject; + library.Should().HavePropertyValue("sha512", "HASH"); + library.Should().HavePropertyValue("type", "package"); + library.Should().HavePropertyValue("serviceable", true); + } + + [Fact] + public void WritesRuntimeTargetForNonPortable() + { + var result = Save(Create( + "Target", + "runtime", + false, + runtimeLibraries: new[] + { + new RuntimeLibrary( + "package", + "PackageName", + "1.2.3", + "HASH", + new [] { RuntimeAssembly.Create("Banana.dll")}, + new RuntimeTarget[] {}, + new [] { + new Dependency("Fruits.Abstract.dll","2.0.0") + }, + true + ), + })); + + // targets + var targets = result.Should().HavePropertyAsObject("targets").Subject; + var target = targets.Should().HavePropertyAsObject("Target/runtime").Subject; + var library = target.Should().HavePropertyAsObject("PackageName/1.2.3").Subject; + var dependencies = library.Should().HavePropertyAsObject("dependencies").Subject; + dependencies.Should().HavePropertyValue("Fruits.Abstract.dll", "2.0.0"); + library.Should().HavePropertyAsObject("runtime") + .Subject.Should().HaveProperty("Banana.dll"); + + //libraries + var libraries = result.Should().HavePropertyAsObject("libraries").Subject; + library = libraries.Should().HavePropertyAsObject("PackageName/1.2.3").Subject; + library.Should().HavePropertyValue("sha512", "HASH"); + library.Should().HavePropertyValue("type", "package"); + library.Should().HavePropertyValue("serviceable", true); + } + + [Fact] + public void WritesCompilationOptions() + { + var result = Save(Create(compilationOptions: new CompilationOptions( + defines: new[] {"MY", "DEFINES"}, + languageVersion: "C#8", + platform: "Platform", + allowUnsafe: true, + warningsAsErrors: true, + optimize: true, + keyFile: "Key.snk", + delaySign: true, + debugType: null, + publicSign: true, + emitEntryPoint: true, + generateXmlDocumentation: true))); + + var options = result.Should().HavePropertyAsObject("compilationOptions").Subject; + options.Should().HavePropertyValue("allowUnsafe", true); + options.Should().HavePropertyValue("delaySign", true); + options.Should().HavePropertyValue("emitEntryPoint", true); + options.Should().HavePropertyValue("xmlDoc", true); + options.Should().HavePropertyValue("publicSign", true); + options.Should().HavePropertyValue("optimize", true); + options.Should().HavePropertyValue("warningsAsErrors", true); + options.Should().HavePropertyValue("allowUnsafe", true); + options.Should().HavePropertyValue("languageVersion", "C#8"); + options.Should().HavePropertyValue("keyFile", "Key.snk"); + options.Should().HaveProperty("defines") + .Subject.Values().Should().BeEquivalentTo(new [] {"MY", "DEFINES" }); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/FileSystemMockBuilder.cs b/test/Microsoft.Extensions.DependencyModel.Tests/FileSystemMockBuilder.cs deleted file mode 100644 index 48886c69e..000000000 --- a/test/Microsoft.Extensions.DependencyModel.Tests/FileSystemMockBuilder.cs +++ /dev/null @@ -1,94 +0,0 @@ -// 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.Linq; -using System.Collections.Generic; -using System.IO; -using Microsoft.Extensions.EnvironmentAbstractions; - -namespace Microsoft.Extensions.DependencyModel.Tests -{ - class FileSystemMockBuilder - { - private Dictionary _files = new Dictionary(); - - public static IFileSystem Empty { get; } = Create().Build(); - - public static FileSystemMockBuilder Create() - { - return new FileSystemMockBuilder(); - } - - public FileSystemMockBuilder AddFile(string name, string content = "") - { - _files.Add(name, content); - return this; - } - - public FileSystemMockBuilder AddFiles(string basePath, params string[] files) - { - foreach (var file in files) - { - AddFile(Path.Combine(basePath, file)); - } - return this; - } - - public IFileSystem Build() - { - return new FileSystemMock(_files); - } - - private class FileSystemMock : IFileSystem - { - public FileSystemMock(Dictionary files) - { - File = new FileMock(files); - Directory = new DirectoryMock(files); - } - - public IFile File { get; } - - public IDirectory Directory { get; } - } - - private class FileMock : IFile - { - private Dictionary _files; - public FileMock(Dictionary files) - { - _files = files; - } - - public bool Exists(string path) - { - return _files.ContainsKey(path); - } - - public string ReadAllText(string path) - { - string text; - if (!_files.TryGetValue(path, out text)) - { - throw new FileNotFoundException(path); - } - return text; - } - } - - private class DirectoryMock : IDirectory - { - private Dictionary _files; - public DirectoryMock(Dictionary files) - { - _files = files; - } - - public bool Exists(string path) - { - return _files.Keys.Any(k => k.StartsWith(path)); - } - } - } - -} diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/JsonAssetions.cs b/test/Microsoft.Extensions.DependencyModel.Tests/JsonAssetions.cs new file mode 100644 index 000000000..214d0c2dc --- /dev/null +++ b/test/Microsoft.Extensions.DependencyModel.Tests/JsonAssetions.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using FluentAssertions.Execution; +using FluentAssertions.Primitives; +using Newtonsoft.Json.Linq; + +namespace Microsoft.Extensions.DependencyModel.Tests +{ + public static class JsonAssertionExtensions + { + public static JsonAssetions Should(this JToken jToken) + { + return new JsonAssetions(jToken); + } + } + + public class JsonAssetions: ReferenceTypeAssertions + { + public JsonAssetions(JToken token) + { + Subject = token; + } + + protected override string Context => nameof(JToken); + + public AndWhichConstraint HaveProperty(string expected) + { + var token = Subject[expected]; + Execute.Assertion + .ForCondition(token != null) + .FailWith($"Expected {Subject} to have property '" + expected + "'"); + + return new AndWhichConstraint(this, token); + } + + public AndConstraint NotHaveProperty(string expected) + { + var token = Subject[expected]; + Execute.Assertion + .ForCondition(token == null) + .FailWith($"Expected {Subject} not to have property '" + expected + "'"); + + return new AndConstraint(this); + } + + public AndWhichConstraint HavePropertyAsObject(string expected) + { + return HaveProperty(expected).Subject.Should().BeOfType(); + } + + public AndConstraint HavePropertyValue(string expected, T value) + { + return HaveProperty(expected).Subject.Value().Should().Be(value); + } + } +} diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/PackageCacheResolverTest.cs b/test/Microsoft.Extensions.DependencyModel.Tests/PackageCacheResolverTest.cs index c3879fcf3..7ea550b98 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/PackageCacheResolverTest.cs +++ b/test/Microsoft.Extensions.DependencyModel.Tests/PackageCacheResolverTest.cs @@ -49,7 +49,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests var exception = Assert.Throws(() => resolver.TryResolveAssemblyPaths(library, null)); exception.Message.Should() .Contain(library.Hash) - .And.Contain(library.PackageName); + .And.Contain(library.Name); } [Fact] @@ -114,7 +114,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests var exception = Assert.Throws(() => resolver.TryResolveAssemblyPaths(library, assemblies)); exception.Message.Should() .Contain(F.SecondAssemblyPath) - .And.Contain(library.PackageName); + .And.Contain(library.Name); } private IEnvironment GetDefaultEnviroment() diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/PackageResolverTest.cs b/test/Microsoft.Extensions.DependencyModel.Tests/PackageResolverTest.cs index 35869d7fc..2eb0e095f 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/PackageResolverTest.cs +++ b/test/Microsoft.Extensions.DependencyModel.Tests/PackageResolverTest.cs @@ -95,7 +95,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests var exception = Assert.Throws(() => resolver.TryResolveAssemblyPaths(library, assemblies)); exception.Message.Should() .Contain(F.SecondAssemblyPath) - .And.Contain(library.PackageName); + .And.Contain(library.Name); } } } diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/ReferenceAssemblyResolverTests.cs b/test/Microsoft.Extensions.DependencyModel.Tests/ReferenceAssemblyResolverTests.cs index 9af3e0abf..7561c77dc 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/ReferenceAssemblyResolverTests.cs +++ b/test/Microsoft.Extensions.DependencyModel.Tests/ReferenceAssemblyResolverTests.cs @@ -153,7 +153,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests exception.Message.Should() .Contain(F.SecondAssemblyPath) - .And.Contain(library.PackageName); + .And.Contain(library.Name); } } }