Merge pull request #1766 from dotnet/pakrym/deps-json-3

Generate actual deps.json file
This commit is contained in:
Pavel Krymets 2016-03-09 15:40:58 -08:00
commit 7c6a2ad8b3
20 changed files with 211 additions and 78 deletions

View file

@ -12,6 +12,7 @@ using Microsoft.DotNet.Files;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.DependencyModel;
using NuGet.Frameworks;
namespace Microsoft.Dotnet.Cli.Compiler.Common
@ -22,19 +23,22 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
private readonly LibraryExporter _exporter;
private readonly string _configuration;
private readonly OutputPaths _outputPaths;
private readonly string _runtimeOutputPath;
private readonly string _intermediateOutputPath;
public Executable(ProjectContext context, OutputPaths outputPaths, LibraryExporter exporter)
public Executable(ProjectContext context, OutputPaths outputPaths, LibraryExporter exporter, string configuration)
{
_context = context;
_outputPaths = outputPaths;
_runtimeOutputPath = outputPaths.RuntimeOutputPath;
_intermediateOutputPath = outputPaths.IntermediateOutputDirectoryPath;
_exporter = exporter;
_configuration = configuration;
}
public void MakeCompilationOutputRunnable()
@ -101,9 +105,7 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
private void WriteDepsFileAndCopyProjectDependencies(LibraryExporter exporter)
{
exporter
.GetDependencies(LibraryType.Package)
.WriteDepsTo(Path.Combine(_runtimeOutputPath, _context.ProjectFile.Name + FileNameSuffixes.Deps));
WriteDeps(exporter);
var projectExports = exporter.GetDependencies(LibraryType.Project);
CopyAssemblies(projectExports);
@ -113,6 +115,36 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
CopyAssets(packageExports);
}
public void WriteDeps(LibraryExporter exporter)
{
var path = Path.Combine(_runtimeOutputPath, _context.ProjectFile.Name + FileNameSuffixes.Deps);
CreateDirectoryIfNotExists(path);
File.WriteAllLines(path, exporter
.GetDependencies(LibraryType.Package)
.SelectMany(GenerateLines));
var compilerOptions = _context.ResolveCompilationOptions(_configuration);
var includeCompile = compilerOptions.PreserveCompilationContext == true;
var exports = exporter.GetAllExports().ToArray();
var dependencyContext = new DependencyContextBuilder().Build(
compilerOptions: includeCompile? compilerOptions: null,
compilationExports: includeCompile ? exports : null,
runtimeExports: exports,
portable: string.IsNullOrEmpty(_context.RuntimeIdentifier),
target: _context.TargetFramework,
runtime: _context.RuntimeIdentifier ?? string.Empty);
var writer = new DependencyContextWriter();
var depsJsonFile = Path.Combine(_runtimeOutputPath, _context.ProjectFile.Name + FileNameSuffixes.DepsJson);
using (var fileStream = File.Create(depsJsonFile))
{
writer.Write(dependencyContext, fileStream);
}
}
public void GenerateBindingRedirects(LibraryExporter exporter)
{
var outputName = _outputPaths.RuntimeFiles.Assembly;
@ -141,5 +173,32 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
appConfig.Save(stream);
}
}
private static void CreateDirectoryIfNotExists(string path)
{
var depsFile = new FileInfo(path);
depsFile.Directory.Create();
}
private static IEnumerable<string> GenerateLines(LibraryExport export)
{
return GenerateLines(export, export.RuntimeAssemblies, "runtime")
.Union(GenerateLines(export, export.NativeLibraries, "native"));
}
private static IEnumerable<string> GenerateLines(LibraryExport export, IEnumerable<LibraryAsset> items, string type)
{
return items.Select(i => DepsFormatter.EscapeRow(new[]
{
export.Library.Identity.Type.Value,
export.Library.Identity.Name,
export.Library.Identity.Version.ToNormalizedString(),
export.Library.Hash,
type,
i.Name,
i.RelativePath
}));
}
}
}

View file

@ -1,45 +1,15 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.DependencyModel;
namespace Microsoft.DotNet.Cli.Compiler.Common
{
public static class LibraryExporterExtensions
{
public static void WriteDepsTo(this IEnumerable<LibraryExport> exports, string path)
{
CreateDirectoryIfNotExists(path);
File.WriteAllLines(path, exports.SelectMany(GenerateLines));
}
private static void CreateDirectoryIfNotExists(string path)
{
var depsFile = new FileInfo(path);
depsFile.Directory.Create();
}
private static IEnumerable<string> GenerateLines(LibraryExport export)
{
return GenerateLines(export, export.RuntimeAssemblies, "runtime")
.Union(GenerateLines(export, export.NativeLibraries, "native"));
}
private static IEnumerable<string> GenerateLines(LibraryExport export, IEnumerable<LibraryAsset> items, string type)
{
return items.Select(i => DepsFormatter.EscapeRow(new[]
{
export.Library.Identity.Type.Value,
export.Library.Identity.Name,
export.Library.Identity.Version.ToNormalizedString(),
export.Library.Hash,
type,
i.Name,
i.RelativePath
}));
}
public static void CopyTo(this IEnumerable<LibraryAsset> assets, string destinationPath)
{
if (!Directory.Exists(destinationPath))

View file

@ -2,7 +2,9 @@
// 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 Microsoft.DotNet.ProjectModel;
using NuGet.Frameworks;
@ -28,5 +30,27 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
return baseOption;
}
// used in incremental compilation for the key file
public static CommonCompilerOptions ResolveCompilationOptions(this ProjectContext context, string configuration)
{
var compilationOptions = context.GetLanguageSpecificCompilerOptions(context.TargetFramework, configuration);
// Path to strong naming key in environment variable overrides path in project.json
var environmentKeyFile = Environment.GetEnvironmentVariable(EnvironmentNames.StrongNameKeyFile);
if (!string.IsNullOrWhiteSpace(environmentKeyFile))
{
compilationOptions.KeyFile = environmentKeyFile;
}
else if (!string.IsNullOrWhiteSpace(compilationOptions.KeyFile))
{
// Resolve full path to key file
compilationOptions.KeyFile =
Path.GetFullPath(Path.Combine(context.ProjectFile.ProjectDirectory, compilationOptions.KeyFile));
}
return compilationOptions;
}
}
}

View file

@ -16,8 +16,6 @@ namespace Microsoft.DotNet.ProjectModel.Compilation
private IList<LibraryAsset> _compilationAssets;
private IList<LibraryAsset> _debugAssets;
private IList<LibraryAsset> _sourceReferences;
private IList<LibraryAsset> _nativeLibraries;

View file

@ -29,6 +29,7 @@ namespace Microsoft.Extensions.DependencyModel
public DependencyContext Build(CommonCompilerOptions compilerOptions,
IEnumerable<LibraryExport> compilationExports,
IEnumerable<LibraryExport> runtimeExports,
bool portable,
NuGetFramework target,
string runtime)
{
@ -44,11 +45,14 @@ namespace Microsoft.Extensions.DependencyModel
.Select(identity => new Dependency(identity.Name, identity.Version.ToString()))
.ToDictionary(dependency => dependency.Name);
var compilationOptions = compilerOptions != null
? GetCompilationOptions(compilerOptions)
: CompilationOptions.Default;
return new DependencyContext(
target.DotNetFrameworkName,
runtime,
false,
GetCompilationOptions(compilerOptions),
portable,
compilationOptions,
GetLibraries(compilationExports, dependencyLookup, runtime: false).Cast<CompilationLibrary>(),
GetLibraries(runtimeExports, dependencyLookup, runtime: true).Cast<RuntimeLibrary>(),
new KeyValuePair<string, string[]>[0]);

View file

@ -6,6 +6,7 @@ namespace Microsoft.DotNet.ProjectModel
public static class FileNameSuffixes
{
public const string Deps = ".deps";
public const string DepsJson = ".deps.json";
public static PlatformFileNameSuffixes CurrentPlatform
{

View file

@ -167,6 +167,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
library.ResourceAssemblies = ReadObject(jobject.ValueAsJsonObject("resource"), ReadFileItem);
library.NativeLibraries = ReadObject(jobject.ValueAsJsonObject("native"), ReadFileItem);
library.ContentFiles = ReadObject(jobject.ValueAsJsonObject("contentFiles"), ReadContentFile);
library.RuntimeTargets = ReadObject(jobject.ValueAsJsonObject("runtimeTargets"), ReadRuntimeTarget);
return library;
}
@ -180,7 +181,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
return new LockFileRuntimeTarget(
path: property,
runtime: jsonObject.ValueAsString("runtime"),
runtime: jsonObject.ValueAsString("rid"),
assetType: jsonObject.ValueAsString("assetType")
);
}

View file

@ -39,6 +39,13 @@ namespace Microsoft.DotNet.ProjectModel
return Path.ChangeExtension(Assembly, FileNameSuffixes.Deps);
}
}
public string DepsJson
{
get
{
return Path.ChangeExtension(Assembly, FileNameSuffixes.DepsJson);
}
}
public string Config
{
@ -61,6 +68,11 @@ namespace Microsoft.DotNet.ProjectModel
{
yield return Deps;
}
if (File.Exists(DepsJson))
{
yield return DepsJson;
}
}
}
}

View file

@ -11,7 +11,7 @@ namespace Microsoft.Extensions.DependencyModel
{
public class DependencyContext
{
private const string DepsResourceSufix = ".deps.json";
private const string DepsJsonExtension = ".deps.json";
private const string DepsFileExtension = ".deps";
private static readonly Lazy<DependencyContext> _defaultContext = new Lazy<DependencyContext>(LoadDefault);
@ -32,10 +32,6 @@ namespace Microsoft.Extensions.DependencyModel
{
throw new ArgumentNullException(nameof(runtime));
}
if (compilationOptions == null)
{
throw new ArgumentNullException(nameof(compilationOptions));
}
if (compileLibraries == null)
{
throw new ArgumentNullException(nameof(compileLibraries));
@ -87,7 +83,7 @@ namespace Microsoft.Extensions.DependencyModel
throw new ArgumentNullException(nameof(assembly));
}
using (var stream = assembly.GetManifestResourceStream(assembly.GetName().Name + DepsResourceSufix))
using (var stream = assembly.GetManifestResourceStream(assembly.GetName().Name + DepsJsonExtension))
{
if (stream != null)
{
@ -95,6 +91,15 @@ namespace Microsoft.Extensions.DependencyModel
}
}
var depsJsonFile = Path.ChangeExtension(assembly.Location, DepsJsonExtension);
if (File.Exists(depsJsonFile))
{
using (var stream = File.OpenRead(depsJsonFile))
{
return new DependencyContextJsonReader().Read(stream);
}
}
var depsFile = Path.ChangeExtension(assembly.Location, DepsFileExtension);
if (File.Exists(depsFile))
{

View file

@ -57,7 +57,7 @@ namespace Microsoft.Extensions.DependencyModel
private JObject WriteCompilationOptions(CompilationOptions compilationOptions)
{
var o = new JObject();
if (compilationOptions.Defines != null)
if (compilationOptions.Defines?.Any() == true)
{
o[DependencyContextStrings.DefinesPropertyName] = new JArray(compilationOptions.Defines);
}
@ -73,7 +73,7 @@ namespace Microsoft.Extensions.DependencyModel
AddPropertyIfNotNull(o, DependencyContextStrings.GenerateXmlDocumentationPropertyName, compilationOptions.GenerateXmlDocumentation);
AddPropertyIfNotNull(o, DependencyContextStrings.DebugTypePropertyName, compilationOptions.DebugType);
return o;
}
}
private void AddPropertyIfNotNull<T>(JObject o, string name, T value)
{
@ -142,6 +142,10 @@ namespace Microsoft.Extensions.DependencyModel
private void AddCompilationAssemblies(JObject libraryObject, IEnumerable<string> compilationAssemblies)
{
if (!compilationAssemblies.Any())
{
return;
}
libraryObject.Add(new JProperty(DependencyContextStrings.CompileTimeAssembliesKey,
WriteAssemblies(compilationAssemblies))
);
@ -149,6 +153,10 @@ namespace Microsoft.Extensions.DependencyModel
private void AddRuntimeAssemblies(JObject libraryObject, IEnumerable<RuntimeAssembly> runtimeAssemblies)
{
if (!runtimeAssemblies.Any())
{
return;
}
libraryObject.Add(new JProperty(DependencyContextStrings.RuntimeAssembliesKey,
WriteAssemblies(runtimeAssemblies.Select(a => a.Path)))
);
@ -156,6 +164,10 @@ namespace Microsoft.Extensions.DependencyModel
private void AddDependencies(JObject libraryObject, IEnumerable<Dependency> dependencies)
{
if (!dependencies.Any())
{
return;
}
libraryObject.Add(
new JProperty(DependencyContextStrings.DependenciesPropertyName,
new JObject(
@ -165,6 +177,10 @@ namespace Microsoft.Extensions.DependencyModel
private void AddResourceAssemblies(JObject libraryObject, IEnumerable<ResourceAssembly> resourceAssemblies)
{
if (!resourceAssemblies.Any())
{
return;
}
libraryObject.Add(DependencyContextStrings.ResourceAssembliesPropertyName,
new JObject(resourceAssemblies.Select(a =>
new JProperty(a.Path, new JObject(new JProperty(DependencyContextStrings.LocalePropertyName, a.Locale))))

View file

@ -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 System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
@ -8,6 +10,7 @@ namespace Microsoft.Extensions.DependencyModel
{
public class RuntimeAssembly
{
private const string NativeImageSufix = ".ni";
private readonly string _assemblyName;
public RuntimeAssembly(string assemblyName, string path)
@ -22,7 +25,17 @@ namespace Microsoft.Extensions.DependencyModel
public static RuntimeAssembly Create(string path)
{
return new RuntimeAssembly(System.IO.Path.GetFileNameWithoutExtension(path), path);
var assemblyName = System.IO.Path.GetFileNameWithoutExtension(path);
if (assemblyName == null)
{
throw new ArgumentException($"Provided path has empty file name '{path}'", nameof(path));
}
if (assemblyName.EndsWith(NativeImageSufix))
{
assemblyName = assemblyName.Substring(0, assemblyName.Length - NativeImageSufix.Length);
}
return new RuntimeAssembly(assemblyName, path);
}
}
}

View file

@ -436,7 +436,7 @@ namespace Microsoft.DotNet.Tools.Build
CopyCompilationOutput(outputPaths);
}
var executable = new Executable(runtimeContext, outputPaths, libraryExporter);
var executable = new Executable(runtimeContext, outputPaths, libraryExporter, _args.ConfigValue);
executable.MakeCompilationOutputRunnable();
PatchMscorlibNextToCoreClr(runtimeContext, _args.ConfigValue);
@ -579,7 +579,7 @@ namespace Microsoft.DotNet.Tools.Build
private static void AddCompilationOptions(ProjectContext project, string config, CompilerIO compilerIO)
{
var compilerOptions = CompilerUtil.ResolveCompilationOptions(project, config);
var compilerOptions = project.ResolveCompilationOptions(config);
// input: key file
if (compilerOptions.KeyFile != null)

View file

@ -119,27 +119,6 @@ namespace Microsoft.DotNet.Tools.Compiler
// used in incremental compilation
public static IEnumerable<string> GetCompilationSources(ProjectContext project) => project.ProjectFile.Files.SourceFiles;
// used in incremental compilation for the key file
public static CommonCompilerOptions ResolveCompilationOptions(ProjectContext context, string configuration)
{
var compilationOptions = context.GetLanguageSpecificCompilerOptions(context.TargetFramework, configuration);
// Path to strong naming key in environment variable overrides path in project.json
var environmentKeyFile = Environment.GetEnvironmentVariable(EnvironmentNames.StrongNameKeyFile);
if (!string.IsNullOrWhiteSpace(environmentKeyFile))
{
compilationOptions.KeyFile = environmentKeyFile;
}
else if (!string.IsNullOrWhiteSpace(compilationOptions.KeyFile))
{
// Resolve full path to key file
compilationOptions.KeyFile =
Path.GetFullPath(Path.Combine(context.ProjectFile.ProjectDirectory, compilationOptions.KeyFile));
}
return compilationOptions;
}
//used in incremental precondition checks
public static IEnumerable<string> GetCommandsInvokedByCompile(ProjectContext project)
{

View file

@ -76,7 +76,7 @@ namespace Microsoft.DotNet.Tools.Compiler
$"--out:{outputName}"
};
var compilationOptions = CompilerUtil.ResolveCompilationOptions(context, args.ConfigValue);
var compilationOptions = context.ResolveCompilationOptions(args.ConfigValue);
var languageId = CompilerUtil.ResolveLanguageId(context);
var references = new List<string>();
@ -114,6 +114,7 @@ namespace Microsoft.DotNet.Tools.Compiler
var dependencyContext = new DependencyContextBuilder().Build(compilationOptions,
allExports,
allExports,
string.IsNullOrEmpty(context.RuntimeIdentifier),
context.TargetFramework,
context.RuntimeIdentifier ?? string.Empty);

View file

@ -163,7 +163,7 @@ namespace Microsoft.DotNet.Tools.Restore
toolDescription.Identity.Name + FileNameSuffixes.Deps);
var calculator = context.GetOutputPaths(Constants.DefaultConfiguration, buidBasePath: null, outputPath: context.ProjectDirectory);
var executable = new Executable(context, calculator, context.CreateExporter(Constants.DefaultConfiguration));
var executable = new Executable(context, calculator, context.CreateExporter(Constants.DefaultConfiguration), null);
executable.MakeCompilationOutputRunnable();

View file

@ -24,14 +24,16 @@ namespace Microsoft.Extensions.DependencyModel.Tests
public DependencyContext Build(CommonCompilerOptions compilerOptions = null,
IEnumerable<LibraryExport> compilationExports = null,
IEnumerable<LibraryExport> runtimeExports = null,
bool portable = false,
NuGetFramework target = null,
string runtime = null)
{
_defaultFramework = NuGetFramework.Parse("net451");
return new DependencyContextBuilder(_referenceAssembliesPath).Build(
compilerOptions ?? new CommonCompilerOptions(),
compilerOptions,
compilationExports ?? new LibraryExport[] { },
runtimeExports ?? new LibraryExport[] {},
portable,
target ?? _defaultFramework,
runtime ?? string.Empty);
}
@ -42,7 +44,7 @@ namespace Microsoft.Extensions.DependencyModel.Tests
var context = Build(new CommonCompilerOptions()
{
AllowUnsafe = true,
Defines = new[] {"Define", "D"},
Defines = new[] { "Define", "D" },
DelaySign = true,
EmitEntryPoint = true,
GenerateXmlDocumentation = true,
@ -68,6 +70,23 @@ namespace Microsoft.Extensions.DependencyModel.Tests
context.CompilationOptions.Platform.Should().Be("Platform");
}
[Fact]
public void AlowsNullCompilationOptions()
{
var context = Build(compilerOptions: null);
context.CompilationOptions.Should().Be(CompilationOptions.Default);
}
[Fact]
public void SetsPortableFlag()
{
var context = Build(portable: true);
context.IsPortable.Should().BeTrue();
}
[Fact]
public void FillsRuntimeAndTarget()
{

View file

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
namespace Microsoft.Extensions.DependencyModel.Tests
{
public class RuntimeAssemblyTests
{
[Fact]
public void UsesFileNameAsAssemblyNameInCreate()
{
var assembly = RuntimeAssembly.Create("path/to/System.Collections.dll");
assembly.Name.Name.Should().Be("System.Collections");
}
[Fact]
public void TrimsDotNiFromDllNames()
{
var assembly = RuntimeAssembly.Create("path/to/System.Collections.ni.dll");
assembly.Name.Name.Should().Be("System.Collections");
}
}
}

View file

@ -27,6 +27,7 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
"TestApp" + FileNameSuffixes.DotNet.ProgramDatabase,
"TestApp" + FileNameSuffixes.CurrentPlatform.Exe,
"TestApp" + FileNameSuffixes.Deps,
"TestApp" + FileNameSuffixes.DepsJson,
"TestLibrary" + FileNameSuffixes.DotNet.DynamicLib,
"TestLibrary" + FileNameSuffixes.DotNet.ProgramDatabase
};

View file

@ -28,6 +28,7 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
.HaveFiles(new[]
{
"BuildTestPortableProject.deps",
"BuildTestPortableProject.deps.json",
"BuildTestPortableProject.dll",
"BuildTestPortableProject.pdb"
});

View file

@ -126,6 +126,7 @@ namespace Microsoft.DotNet.Tools.Publish.Tests
publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryLesser.pdb");
publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryLesser.dll.config");
publishCommand.GetOutputDirectory().Should().NotHaveFile("TestLibraryLesser.deps");
publishCommand.GetOutputDirectory().Should().NotHaveFile("TestLibraryLesser.deps.json");
// dependencies should also be copied
publishCommand.GetOutputDirectory().Should().HaveFile("Newtonsoft.Json.dll");
@ -138,6 +139,7 @@ namespace Microsoft.DotNet.Tools.Publish.Tests
publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryLesser.pdb");
publishCommand.GetOutputDirectory().Should().NotHaveFile("TestLibraryLesser.dll.config");
publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryLesser.deps");
publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryLesser.deps.json");
// dependencies should also be copied
publishCommand.GetOutputDirectory().Should().HaveFile("Newtonsoft.Json.dll");