2016-01-29 18:21:37 -08:00
|
|
|
|
// 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;
|
2016-02-09 12:01:52 -08:00
|
|
|
|
using System.Collections.Generic;
|
2016-01-29 18:21:37 -08:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Xml.Linq;
|
|
|
|
|
using Microsoft.DotNet.Cli.Compiler.Common;
|
|
|
|
|
using Microsoft.DotNet.Cli.Utils;
|
2016-02-02 10:50:59 -08:00
|
|
|
|
using Microsoft.DotNet.Files;
|
2016-01-29 18:21:37 -08:00
|
|
|
|
using Microsoft.DotNet.ProjectModel;
|
|
|
|
|
using Microsoft.DotNet.ProjectModel.Compilation;
|
|
|
|
|
using Microsoft.DotNet.ProjectModel.Graph;
|
2016-03-08 16:46:50 -08:00
|
|
|
|
using Microsoft.Extensions.DependencyModel;
|
2016-01-29 18:21:37 -08:00
|
|
|
|
using NuGet.Frameworks;
|
2016-03-09 11:36:16 -08:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using Newtonsoft.Json;
|
2016-04-11 15:56:44 -07:00
|
|
|
|
using System.Reflection.PortableExecutable;
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
2016-04-17 20:53:49 -07:00
|
|
|
|
namespace Microsoft.DotNet.Cli.Compiler.Common
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
|
|
|
|
public class Executable
|
|
|
|
|
{
|
|
|
|
|
private readonly ProjectContext _context;
|
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
private readonly LibraryExporter _exporter;
|
|
|
|
|
|
2016-04-11 11:01:41 -07:00
|
|
|
|
private readonly string _configuration;
|
|
|
|
|
|
2016-02-03 10:57:25 -08:00
|
|
|
|
private readonly OutputPaths _outputPaths;
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
private readonly string _runtimeOutputPath;
|
|
|
|
|
|
|
|
|
|
private readonly string _intermediateOutputPath;
|
2016-02-03 10:57:25 -08:00
|
|
|
|
|
2016-03-16 16:39:59 -07:00
|
|
|
|
private readonly CommonCompilerOptions _compilerOptions;
|
|
|
|
|
|
2016-03-08 16:46:50 -08:00
|
|
|
|
public Executable(ProjectContext context, OutputPaths outputPaths, LibraryExporter exporter, string configuration)
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
|
|
|
|
_context = context;
|
2016-02-03 10:57:25 -08:00
|
|
|
|
_outputPaths = outputPaths;
|
2016-02-17 10:08:27 -08:00
|
|
|
|
_runtimeOutputPath = outputPaths.RuntimeOutputPath;
|
|
|
|
|
_intermediateOutputPath = outputPaths.IntermediateOutputDirectoryPath;
|
2016-02-03 10:57:25 -08:00
|
|
|
|
_exporter = exporter;
|
2016-04-11 11:01:41 -07:00
|
|
|
|
_configuration = configuration;
|
2016-03-16 16:39:59 -07:00
|
|
|
|
_compilerOptions = _context.ProjectFile.GetCompilerOptions(_context.TargetFramework, configuration);
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-03 10:57:25 -08:00
|
|
|
|
public void MakeCompilationOutputRunnable()
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
2016-02-17 10:08:27 -08:00
|
|
|
|
CopyContentFiles();
|
|
|
|
|
ExportRuntimeAssets();
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
private void ExportRuntimeAssets()
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
|
|
|
|
if (_context.TargetFramework.IsDesktop())
|
|
|
|
|
{
|
2016-02-17 10:08:27 -08:00
|
|
|
|
MakeCompilationOutputRunnableForFullFramework();
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-02-17 10:08:27 -08:00
|
|
|
|
MakeCompilationOutputRunnableForCoreCLR();
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
2016-02-03 10:57:25 -08:00
|
|
|
|
}
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
private void MakeCompilationOutputRunnableForFullFramework()
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
2016-02-17 10:08:27 -08:00
|
|
|
|
var dependencies = _exporter.GetDependencies();
|
|
|
|
|
CopyAssemblies(dependencies);
|
|
|
|
|
CopyAssets(dependencies);
|
2016-02-03 10:57:25 -08:00
|
|
|
|
GenerateBindingRedirects(_exporter);
|
|
|
|
|
}
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
private void MakeCompilationOutputRunnableForCoreCLR()
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
2016-02-17 10:08:27 -08:00
|
|
|
|
WriteDepsFileAndCopyProjectDependencies(_exporter);
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
2016-03-16 16:39:59 -07:00
|
|
|
|
var emitEntryPoint = _compilerOptions.EmitEntryPoint ?? false;
|
2016-03-09 11:36:16 -08:00
|
|
|
|
if (emitEntryPoint && !string.IsNullOrEmpty(_context.RuntimeIdentifier))
|
2016-03-01 17:42:44 -08:00
|
|
|
|
{
|
|
|
|
|
// TODO: Pick a host based on the RID
|
2016-03-17 16:52:50 -07:00
|
|
|
|
CoreHost.CopyTo(_runtimeOutputPath, _compilerOptions.OutputName + Constants.ExeSuffix);
|
2016-03-01 17:42:44 -08:00
|
|
|
|
}
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
private void CopyContentFiles()
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
|
|
|
|
var contentFiles = new ContentFiles(_context);
|
2016-02-17 10:08:27 -08:00
|
|
|
|
contentFiles.StructuredCopyTo(_runtimeOutputPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CopyAssemblies(IEnumerable<LibraryExport> libraryExports)
|
|
|
|
|
{
|
|
|
|
|
foreach (var libraryExport in libraryExports)
|
|
|
|
|
{
|
2016-03-17 11:56:57 -07:00
|
|
|
|
libraryExport.RuntimeAssemblyGroups.GetDefaultAssets().CopyTo(_runtimeOutputPath);
|
|
|
|
|
libraryExport.NativeLibraryGroups.GetDefaultAssets().CopyTo(_runtimeOutputPath);
|
2016-04-15 14:45:51 -07:00
|
|
|
|
|
2016-04-19 13:26:46 -07:00
|
|
|
|
foreach (var group in libraryExport.ResourceAssemblies.GroupBy(r => r.Locale))
|
2016-04-15 14:45:51 -07:00
|
|
|
|
{
|
|
|
|
|
var localeSpecificDir = Path.Combine(_runtimeOutputPath, group.Key);
|
2016-04-19 13:26:46 -07:00
|
|
|
|
if (!Directory.Exists(localeSpecificDir))
|
2016-04-15 14:45:51 -07:00
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(localeSpecificDir);
|
|
|
|
|
}
|
|
|
|
|
group.Select(r => r.Asset).CopyTo(localeSpecificDir);
|
|
|
|
|
}
|
2016-02-17 10:08:27 -08:00
|
|
|
|
}
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
private void CopyAssets(IEnumerable<LibraryExport> libraryExports)
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
2016-02-10 10:07:22 -08:00
|
|
|
|
foreach (var libraryExport in libraryExports)
|
|
|
|
|
{
|
2016-02-17 10:08:27 -08:00
|
|
|
|
libraryExport.RuntimeAssets.StructuredCopyTo(
|
|
|
|
|
_runtimeOutputPath,
|
|
|
|
|
_intermediateOutputPath);
|
2016-02-10 10:07:22 -08:00
|
|
|
|
}
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
private void WriteDepsFileAndCopyProjectDependencies(LibraryExporter exporter)
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
2016-03-08 16:46:50 -08:00
|
|
|
|
WriteDeps(exporter);
|
2016-04-11 11:01:41 -07:00
|
|
|
|
if (_context.ProjectFile.HasRuntimeOutput(_configuration))
|
|
|
|
|
{
|
|
|
|
|
WriteRuntimeConfig(exporter);
|
|
|
|
|
WriteDevRuntimeConfig(exporter);
|
|
|
|
|
}
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
2016-02-16 15:30:39 -08:00
|
|
|
|
var projectExports = exporter.GetDependencies(LibraryType.Project);
|
2016-02-17 10:08:27 -08:00
|
|
|
|
CopyAssemblies(projectExports);
|
|
|
|
|
CopyAssets(projectExports);
|
2016-02-09 12:01:52 -08:00
|
|
|
|
|
2016-02-17 10:08:27 -08:00
|
|
|
|
var packageExports = exporter.GetDependencies(LibraryType.Package);
|
|
|
|
|
CopyAssets(packageExports);
|
2016-02-03 10:57:25 -08:00
|
|
|
|
}
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
2016-03-09 11:36:16 -08:00
|
|
|
|
private void WriteRuntimeConfig(LibraryExporter exporter)
|
|
|
|
|
{
|
|
|
|
|
if (!_context.TargetFramework.IsDesktop())
|
|
|
|
|
{
|
|
|
|
|
// TODO: Suppress this file if there's nothing to write? RuntimeOutputFiles would have to be updated
|
|
|
|
|
// in order to prevent breaking incremental compilation...
|
|
|
|
|
|
|
|
|
|
var json = new JObject();
|
|
|
|
|
var runtimeOptions = new JObject();
|
|
|
|
|
json.Add("runtimeOptions", runtimeOptions);
|
|
|
|
|
|
2016-03-31 16:25:52 -07:00
|
|
|
|
WriteFramework(runtimeOptions, exporter);
|
|
|
|
|
WriteRuntimeOptions(runtimeOptions);
|
2016-03-09 11:36:16 -08:00
|
|
|
|
|
2016-03-16 16:39:59 -07:00
|
|
|
|
var runtimeConfigJsonFile =
|
2016-03-17 16:52:50 -07:00
|
|
|
|
Path.Combine(_runtimeOutputPath, _compilerOptions.OutputName + FileNameSuffixes.RuntimeConfigJson);
|
2016-03-16 16:39:59 -07:00
|
|
|
|
|
2016-03-09 11:36:16 -08:00
|
|
|
|
using (var writer = new JsonTextWriter(new StreamWriter(File.Create(runtimeConfigJsonFile))))
|
|
|
|
|
{
|
|
|
|
|
writer.Formatting = Formatting.Indented;
|
|
|
|
|
json.WriteTo(writer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 16:25:52 -07:00
|
|
|
|
private void WriteFramework(JObject runtimeOptions, LibraryExporter exporter)
|
|
|
|
|
{
|
2016-04-08 08:51:51 -07:00
|
|
|
|
var redistPackage = _context.PlatformLibrary;
|
|
|
|
|
if (redistPackage != null)
|
2016-03-31 16:25:52 -07:00
|
|
|
|
{
|
2016-04-08 08:51:51 -07:00
|
|
|
|
var packageName = redistPackage.Identity.Name;
|
2016-03-31 16:25:52 -07:00
|
|
|
|
|
|
|
|
|
var redistExport = exporter.GetAllExports()
|
|
|
|
|
.FirstOrDefault(e => e.Library.Identity.Name.Equals(packageName));
|
|
|
|
|
if (redistExport == null)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException($"Platform package '{packageName}' was not present in the graph.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var framework = new JObject(
|
|
|
|
|
new JProperty("name", redistExport.Library.Identity.Name),
|
|
|
|
|
new JProperty("version", redistExport.Library.Identity.Version.ToNormalizedString()));
|
|
|
|
|
runtimeOptions.Add("framework", framework);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteRuntimeOptions(JObject runtimeOptions)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(_context.ProjectFile.RawRuntimeOptions))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var runtimeOptionsFromProjectJson = JObject.Parse(_context.ProjectFile.RawRuntimeOptions);
|
|
|
|
|
foreach (var runtimeOption in runtimeOptionsFromProjectJson)
|
|
|
|
|
{
|
|
|
|
|
runtimeOptions.Add(runtimeOption.Key, runtimeOption.Value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-29 16:06:10 -07:00
|
|
|
|
private void WriteDevRuntimeConfig(LibraryExporter exporter)
|
|
|
|
|
{
|
|
|
|
|
if (_context.TargetFramework.IsDesktop())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var json = new JObject();
|
|
|
|
|
var runtimeOptions = new JObject();
|
|
|
|
|
json.Add("runtimeOptions", runtimeOptions);
|
|
|
|
|
|
|
|
|
|
AddAdditionalProbingPaths(runtimeOptions);
|
|
|
|
|
|
|
|
|
|
var runtimeConfigDevJsonFile =
|
|
|
|
|
Path.Combine(_runtimeOutputPath, _compilerOptions.OutputName + FileNameSuffixes.RuntimeConfigDevJson);
|
|
|
|
|
|
|
|
|
|
using (var writer = new JsonTextWriter(new StreamWriter(File.Create(runtimeConfigDevJsonFile))))
|
|
|
|
|
{
|
|
|
|
|
writer.Formatting = Formatting.Indented;
|
|
|
|
|
json.WriteTo(writer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AddAdditionalProbingPaths(JObject runtimeOptions)
|
|
|
|
|
{
|
|
|
|
|
var additionalProbingPaths = new JArray(_context.PackagesDirectory);
|
|
|
|
|
runtimeOptions.Add("additionalProbingPaths", additionalProbingPaths);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-08 16:46:50 -08:00
|
|
|
|
public void WriteDeps(LibraryExporter exporter)
|
|
|
|
|
{
|
2016-03-16 16:39:59 -07:00
|
|
|
|
Directory.CreateDirectory(_runtimeOutputPath);
|
|
|
|
|
|
|
|
|
|
var includeCompile = _compilerOptions.PreserveCompilationContext == true;
|
2016-03-08 16:46:50 -08:00
|
|
|
|
|
|
|
|
|
var exports = exporter.GetAllExports().ToArray();
|
|
|
|
|
var dependencyContext = new DependencyContextBuilder().Build(
|
2016-03-16 16:39:59 -07:00
|
|
|
|
compilerOptions: includeCompile ? _compilerOptions : null,
|
2016-03-08 16:46:50 -08:00
|
|
|
|
compilationExports: includeCompile ? exports : null,
|
|
|
|
|
runtimeExports: exports,
|
|
|
|
|
portable: string.IsNullOrEmpty(_context.RuntimeIdentifier),
|
|
|
|
|
target: _context.TargetFramework,
|
|
|
|
|
runtime: _context.RuntimeIdentifier ?? string.Empty);
|
|
|
|
|
|
|
|
|
|
var writer = new DependencyContextWriter();
|
2016-03-17 16:52:50 -07:00
|
|
|
|
var depsJsonFilePath = Path.Combine(_runtimeOutputPath, _compilerOptions.OutputName + FileNameSuffixes.DepsJson);
|
2016-03-16 16:39:59 -07:00
|
|
|
|
using (var fileStream = File.Create(depsJsonFilePath))
|
2016-03-08 16:46:50 -08:00
|
|
|
|
{
|
|
|
|
|
writer.Write(dependencyContext, fileStream);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-03 10:57:25 -08:00
|
|
|
|
public void GenerateBindingRedirects(LibraryExporter exporter)
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
2016-02-03 10:57:25 -08:00
|
|
|
|
var outputName = _outputPaths.RuntimeFiles.Assembly;
|
2016-04-19 13:26:46 -07:00
|
|
|
|
var configFile = outputName + ".config";
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
|
|
|
|
var existingConfig = new DirectoryInfo(_context.ProjectDirectory)
|
|
|
|
|
.EnumerateFiles()
|
|
|
|
|
.FirstOrDefault(f => f.Name.Equals("app.config", StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
if (existingConfig != null)
|
|
|
|
|
{
|
2016-04-19 13:26:46 -07:00
|
|
|
|
File.Copy(existingConfig.FullName, configFile, true);
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-19 13:26:46 -07:00
|
|
|
|
List<string> configFiles = new List<string>();
|
|
|
|
|
configFiles.Add(configFile);
|
2016-01-29 18:21:37 -08:00
|
|
|
|
|
2016-04-19 13:26:46 -07:00
|
|
|
|
foreach (var export in exporter.GetDependencies())
|
2016-01-29 18:21:37 -08:00
|
|
|
|
{
|
2016-04-19 13:26:46 -07:00
|
|
|
|
var dependencyExecutables = export.RuntimeAssemblyGroups.GetDefaultAssets()
|
|
|
|
|
.Where(asset => asset.FileName.ToLower().EndsWith(".exe"))
|
|
|
|
|
.Select(asset => Path.Combine(_runtimeOutputPath, asset.FileName));
|
|
|
|
|
|
|
|
|
|
foreach (var executable in dependencyExecutables)
|
|
|
|
|
{
|
|
|
|
|
configFile = executable + ".config";
|
|
|
|
|
configFiles.Add(configFile);
|
|
|
|
|
}
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
2016-04-19 13:26:46 -07:00
|
|
|
|
|
|
|
|
|
exporter.GetAllExports().GenerateBindingRedirects(configFiles);
|
2016-01-29 18:21:37 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|