diff --git a/src/Microsoft.DotNet.Tools.Compiler/BindingRedirectGenerator.cs b/src/Microsoft.DotNet.Cli.Utils/BindingRedirectGenerator.cs similarity index 78% rename from src/Microsoft.DotNet.Tools.Compiler/BindingRedirectGenerator.cs rename to src/Microsoft.DotNet.Cli.Utils/BindingRedirectGenerator.cs index d00dd1197..a7236e9f3 100644 --- a/src/Microsoft.DotNet.Tools.Compiler/BindingRedirectGenerator.cs +++ b/src/Microsoft.DotNet.Cli.Utils/BindingRedirectGenerator.cs @@ -12,29 +12,29 @@ using System.Text; using System.Xml.Linq; using Microsoft.DotNet.ProjectModel.Compilation; -namespace Microsoft.DotNet.Tools.Compiler +namespace Microsoft.DotNet.Cli.Utils { - internal class BindingRedirectGenerator + internal static class BindingRedirectGenerator { private const int TokenLength = 8; private const string Namespace = "urn:schemas-microsoft-com:asm.v1"; - private static XName ConfigurationElementName = XName.Get("configuration"); - private static XName RuntimeElementName = XName.Get("runtime"); - private static XName AssemblyBindingElementName = XName.Get("assemblyBinding", Namespace); - private static XName DependentAssemblyElementName = XName.Get("dependentAssembly", Namespace); - private static XName AssemblyIdentityElementName = XName.Get("assemblyIdentity", Namespace); - private static XName BindingRedirectElementName = XName.Get("bindingRedirect", Namespace); + private static readonly XName ConfigurationElementName = XName.Get("configuration"); + private static readonly XName RuntimeElementName = XName.Get("runtime"); + private static readonly XName AssemblyBindingElementName = XName.Get("assemblyBinding", Namespace); + private static readonly XName DependentAssemblyElementName = XName.Get("dependentAssembly", Namespace); + private static readonly XName AssemblyIdentityElementName = XName.Get("assemblyIdentity", Namespace); + private static readonly XName BindingRedirectElementName = XName.Get("bindingRedirect", Namespace); - private static XName NameAttributeName = XName.Get("name"); - private static XName PublicKeyTokenAttributeName = XName.Get("publicKeyToken"); - private static XName CultureAttributeName = XName.Get("culture"); - private static XName OldVersionAttributeName = XName.Get("oldVersion"); - private static XName NewVersionAttributeName = XName.Get("newVersion"); + private static readonly XName NameAttributeName = XName.Get("name"); + private static readonly XName PublicKeyTokenAttributeName = XName.Get("publicKeyToken"); + private static readonly XName CultureAttributeName = XName.Get("culture"); + private static readonly XName OldVersionAttributeName = XName.Get("oldVersion"); + private static readonly XName NewVersionAttributeName = XName.Get("newVersion"); - private readonly SHA1 _sha1 = SHA1.Create(); + internal static SHA1 Sha1 { get; } = SHA1.Create(); - public XDocument Generate(IEnumerable dependencies, XDocument document) + internal static XDocument GenerateBindingRedirects(this IEnumerable dependencies, XDocument document) { var redirects = CollectRedirects(dependencies); @@ -57,7 +57,7 @@ namespace Microsoft.DotNet.Tools.Compiler return document; } - private void AddDependentAssembly(AssemblyRedirect redirect, XElement assemblyBindings) + private static void AddDependentAssembly(AssemblyRedirect redirect, XElement assemblyBindings) { var dependencyElement = assemblyBindings.Elements(DependentAssemblyElementName) .FirstOrDefault(element => IsSameAssembly(redirect, element)); @@ -80,16 +80,16 @@ namespace Microsoft.DotNet.Tools.Compiler )); } - private bool IsSameAssembly(AssemblyRedirect redirect, XElement dependentAssemblyElement) + private static bool IsSameAssembly(AssemblyRedirect redirect, XElement dependentAssemblyElement) { var identity = dependentAssemblyElement.Element(AssemblyIdentityElementName); if (identity == null) { return false; } - return (string) identity.Attribute(NameAttributeName) == redirect.From.Name && - (string) identity.Attribute(PublicKeyTokenAttributeName) == redirect.From.PublicKeyToken && - (string) identity.Attribute(CultureAttributeName) == redirect.From.Culture; + return (string)identity.Attribute(NameAttributeName) == redirect.From.Name && + (string)identity.Attribute(PublicKeyTokenAttributeName) == redirect.From.PublicKeyToken && + (string)identity.Attribute(CultureAttributeName) == redirect.From.Culture; } public static XElement GetOrAddElement(XContainer parent, XName elementName) @@ -107,7 +107,7 @@ namespace Microsoft.DotNet.Tools.Compiler return element; } - private AssemblyRedirect[] CollectRedirects(IEnumerable dependencies) + private static AssemblyRedirect[] CollectRedirects(IEnumerable dependencies) { var allRuntimeAssemblies = dependencies.SelectMany(d => d.RuntimeAssemblies).Select(GetAssemblyInfo).ToArray(); var assemblyLookup = allRuntimeAssemblies.ToDictionary(r => r.Identity.ToLookupKey()); @@ -136,14 +136,14 @@ namespace Microsoft.DotNet.Tools.Compiler return redirectAssemblies.ToArray(); } - private AssemblyReferenceInfo GetAssemblyInfo(LibraryAsset arg) + private static AssemblyReferenceInfo GetAssemblyInfo(LibraryAsset arg) { using (var peReader = new PEReader(File.OpenRead(arg.ResolvedPath))) { var metadataReader = peReader.GetMetadataReader(); var definition = metadataReader.GetAssemblyDefinition(); - + var identity = new AssemblyIdentity( metadataReader.GetString(definition.Name), definition.Version, @@ -168,7 +168,7 @@ namespace Microsoft.DotNet.Tools.Compiler } } - private string GetPublicKeyToken(byte[] bytes) + private static string GetPublicKeyToken(byte[] bytes) { if (bytes.Length == 0) { @@ -183,7 +183,7 @@ namespace Microsoft.DotNet.Tools.Compiler else { token = new byte[TokenLength]; - var sha1 = _sha1.ComputeHash(bytes); + var sha1 = Sha1.ComputeHash(bytes); Array.Copy(sha1, sha1.Length - TokenLength, token, 0, TokenLength); Array.Reverse(token); } @@ -215,7 +215,7 @@ namespace Microsoft.DotNet.Tools.Compiler { Name = name; Version = version; - Culture = string.IsNullOrEmpty(culture)? "neutral" : culture; + Culture = string.IsNullOrEmpty(culture) ? "neutral" : culture; PublicKeyToken = publicKeyToken; } diff --git a/src/Microsoft.DotNet.Cli.Utils/Command.cs b/src/Microsoft.DotNet.Cli.Utils/Command.cs index 7e08f7264..06be0246e 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Command.cs +++ b/src/Microsoft.DotNet.Cli.Utils/Command.cs @@ -141,7 +141,7 @@ namespace Microsoft.DotNet.Cli.Utils return fileNames.Contains(commandName + FileNameSuffixes.DotNet.Exe) && fileNames.Contains(commandName + FileNameSuffixes.DotNet.DynamicLib) && - fileNames.Contains(commandName + FileNameSuffixes.DotNet.Deps); + fileNames.Contains(commandName + FileNameSuffixes.Deps); }); if (commandPackage == null) return null; diff --git a/src/Microsoft.DotNet.Cli.Utils/CoreHost.cs b/src/Microsoft.DotNet.Cli.Utils/CoreHost.cs new file mode 100644 index 000000000..a1d027065 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CoreHost.cs @@ -0,0 +1,30 @@ +using System.IO; +using Microsoft.DotNet.ProjectModel; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal static class CoreHost + { + internal static string _path; + + public static string FileName = "corehost" + FileNameSuffixes.CurrentPlatform.Exe; + + public static string Path + { + get + { + if (_path == null) + { + _path = Env.GetCommandPath(FileName, new[] {string.Empty}); + } + + return _path; + } + } + + public static void CopyTo(string destinationPath) + { + File.Copy(Path, destinationPath, overwrite: true); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CsvFormatter.cs b/src/Microsoft.DotNet.Cli.Utils/CsvFormatter.cs new file mode 100644 index 000000000..d5dcb0731 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CsvFormatter.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal static class CsvFormatter + { + internal static string EscapeRow(IEnumerable values) + { + return values + .Select(EscapeValue) + .Aggregate((a, v) => a + "," + v); + } + + internal static string EscapeValue(string value) + { + return "\"" + value.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; + } + } +} diff --git a/src/Microsoft.DotNet.Cli.Utils/Env.cs b/src/Microsoft.DotNet.Cli.Utils/Env.cs new file mode 100644 index 000000000..f1f18eae6 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/Env.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal static class Env + { + private static IEnumerable _searchPaths; + private static IEnumerable _executableExtensions; + + public static IEnumerable ExecutableExtensions + { + get + { + if (_executableExtensions == null) + { + + _executableExtensions = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Environment.GetEnvironmentVariable("PATHEXT").Split(';').Select(e => e.ToLower()) + : new [] { string.Empty }; + } + + return _executableExtensions; + } + } + + private static IEnumerable SearchPaths + { + get + { + if (_searchPaths == null) + { + var searchPaths = new List {AppContext.BaseDirectory}; + + searchPaths.AddRange(Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator)); + + _searchPaths = searchPaths; + } + + return _searchPaths; + } + } + + public static string GetCommandPath(string commandName, params string[] extensions) + { + if (!extensions.Any()) + extensions = Env.ExecutableExtensions.ToArray(); + + var commandPath = Env.SearchPaths.Join( + extensions, + p => true, s => true, + (p, s) => Path.Combine(p, commandName + s)) + .FirstOrDefault(File.Exists); + + return commandPath; + } + } +} diff --git a/src/Microsoft.DotNet.Cli.Utils/LibraryExporterExtensions.cs b/src/Microsoft.DotNet.Cli.Utils/LibraryExporterExtensions.cs new file mode 100644 index 000000000..26cd3c6c1 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/LibraryExporterExtensions.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.IO; +using Microsoft.DotNet.ProjectModel.Compilation; +using System.Linq; +using Microsoft.DotNet.ProjectModel; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal static class LibraryExporterExtensions + { + internal static void CopyProjectDependenciesTo(this LibraryExporter exporter, string path, params ProjectDescription[] except) + { + exporter.GetAllExports() + .Where(e => !except.Contains(e.Library)) + .Where(e => e.Library is ProjectDescription) + .SelectMany(e => e.NativeLibraries.Union(e.RuntimeAssemblies)) + .CopyTo(path); + } + + internal static void WriteDepsTo(this IEnumerable exports, string path) + { + File.WriteAllLines(path, exports.SelectMany(GenerateLines)); + } + + private static IEnumerable GenerateLines(LibraryExport export) + { + return GenerateLines(export, export.RuntimeAssemblies, "runtime") + .Union(GenerateLines(export, export.NativeLibraries, "native")); + } + + private static IEnumerable GenerateLines(LibraryExport export, IEnumerable items, string type) + { + return items.Select(i => CsvFormatter.EscapeRow(new[] + { + export.Library.Identity.Type.Value, + export.Library.Identity.Name, + export.Library.Identity.Version.ToNormalizedString(), + export.Library.Hash, + type, + i.Name, + i.RelativePath + })); + } + + internal static IEnumerable RuntimeAssets(this LibraryExport export) + { + return export.RuntimeAssemblies.Union(export.NativeLibraries); + } + + internal static void CopyTo(this IEnumerable assets, string destinationPath) + { + foreach (var asset in assets) + { + File.Copy(asset.ResolvedPath, Path.Combine(destinationPath, Path.GetFileName(asset.ResolvedPath)), + overwrite: true); + } + } + } +} diff --git a/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs b/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs index c06b41e3f..4284daddb 100644 --- a/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs +++ b/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Linq; using System.Runtime.InteropServices; namespace Microsoft.DotNet.Tools.Common @@ -199,5 +198,17 @@ namespace Microsoft.DotNet.Tools.Common return GetPathWithBackSlashes(path); } } + + public static bool HasExtension(string filePath, string extension) + { + var comparison = StringComparison.Ordinal; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + comparison = StringComparison.OrdinalIgnoreCase; + } + + return Path.GetExtension(filePath).Equals(extension, comparison); + } } } \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/ProjectContextExtensions.cs b/src/Microsoft.DotNet.Cli.Utils/ProjectContextExtensions.cs index c37937e1d..43988047d 100644 --- a/src/Microsoft.DotNet.Cli.Utils/ProjectContextExtensions.cs +++ b/src/Microsoft.DotNet.Cli.Utils/ProjectContextExtensions.cs @@ -2,8 +2,14 @@ // 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 System.Xml.Linq; using Microsoft.DotNet.ProjectModel; +using Microsoft.DotNet.ProjectModel.Graph; +using Microsoft.DotNet.Tools.Common; using NuGet.Frameworks; namespace Microsoft.DotNet.Cli.Utils @@ -63,5 +69,118 @@ namespace Microsoft.DotNet.Cli.Utils return rootOutputPath; } + + internal static void MakeCompilationOutputRunnable(this ProjectContext context, string outputPath, string configuration) + { + context + .ProjectFile + .Files + .GetContentFiles() + .StructuredCopyTo(context.ProjectDirectory, outputPath) + .RemoveAttribute(FileAttributes.ReadOnly); + + var exporter = context.CreateExporter(configuration); + + if (context.TargetFramework.IsDesktop()) + { + exporter + .GetDependencies() + .SelectMany(e => e.RuntimeAssets()) + .CopyTo(outputPath); + + GenerateBindingRedirects(context, outputPath, configuration); + } + else + { + exporter + .GetDependencies(LibraryType.Package) + .WriteDepsTo(Path.Combine(outputPath, context.ProjectFile.Name + FileNameSuffixes.Deps)); + + exporter.GetDependencies(LibraryType.Project) + .SelectMany(e => e.RuntimeAssets()) + .CopyTo(outputPath); + + CoreHost.CopyTo(Path.Combine(outputPath, context.ProjectFile.Name + Constants.ExeSuffix)); + } + } + + private static IEnumerable StructuredCopyTo(this IEnumerable sourceFiles, string sourceDirectory, string targetDirectory) + { + if (sourceFiles == null) + { + throw new ArgumentNullException(nameof(sourceFiles)); + } + + var pathMap = sourceFiles + .ToDictionary(s => s, s => Path.Combine( + targetDirectory, + PathUtility.GetRelativePath(sourceDirectory, s))); + + foreach (var targetDir in pathMap.Values + .Select(Path.GetDirectoryName) + .Distinct() + .Where(t => !Directory.Exists(t))) + { + Directory.CreateDirectory(targetDir); + } + + foreach (var sourceFilePath in pathMap.Keys) + { + File.Copy( + sourceFilePath, + pathMap[sourceFilePath], + overwrite: true); + } + + return pathMap.Values; + } + + + private static IEnumerable RemoveAttribute(this IEnumerable files, FileAttributes attribute) + { + foreach (var file in files) + { + var fileAttributes = File.GetAttributes(file); + if ((fileAttributes & attribute) == attribute) + { + File.SetAttributes(file, fileAttributes & ~attribute); + } + } + + return files; + } + + + private static void GenerateBindingRedirects(this ProjectContext context, string outputPath, string configuration) + { + var existingConfig = new DirectoryInfo(context.ProjectDirectory) + .EnumerateFiles() + .FirstOrDefault(f => f.Name.Equals("app.config", StringComparison.OrdinalIgnoreCase)); + + XDocument baseAppConfig = null; + + if (existingConfig != null) + { + using (var fileStream = File.OpenRead(existingConfig.FullName)) + { + baseAppConfig = XDocument.Load(fileStream); + } + } + + var appConfig = context.CreateExporter(configuration).GetAllExports().GenerateBindingRedirects(baseAppConfig); + + if (appConfig == null) return; + + var path = Path.Combine(outputPath, context.ProjectFile.Name + ".exe.config"); + using (var stream = File.Create(path)) + { + appConfig.Save(stream); + } + } + + public static string GetDepsPath(this ProjectContext context, string buildConfiguration) + { + return Path.Combine(context.GetOutputDirectoryPath(buildConfiguration), context.ProjectFile.Name + ".deps"); + } } } diff --git a/src/Microsoft.DotNet.Cli.Utils/project.json b/src/Microsoft.DotNet.Cli.Utils/project.json index 7bafa034a..f7cf1e93c 100644 --- a/src/Microsoft.DotNet.Cli.Utils/project.json +++ b/src/Microsoft.DotNet.Cli.Utils/project.json @@ -1,14 +1,15 @@ { - "version": "1.0.0-*", + "version": "1.0.0-*", - "shared": "**/*.cs", + "shared": "**/*.cs", - "dependencies": { - "NETStandard.Library": "1.0.0-rc2-23616", - "Microsoft.DotNet.ProjectModel": "1.0.0" - }, + "dependencies": { + "NETStandard.Library": "1.0.0-rc2-23616", + "Microsoft.DotNet.ProjectModel": "1.0.0", + "System.Reflection.Metadata": "1.1.0" + }, - "frameworks": { - "dnxcore50": { } - } + "frameworks": { + "dnxcore50": { } + } } diff --git a/src/Microsoft.DotNet.ProjectModel/FileNameSuffixes.cs b/src/Microsoft.DotNet.ProjectModel/FileNameSuffixes.cs index ef535425c..8f322c748 100644 --- a/src/Microsoft.DotNet.ProjectModel/FileNameSuffixes.cs +++ b/src/Microsoft.DotNet.ProjectModel/FileNameSuffixes.cs @@ -1,16 +1,30 @@ +using System; using System.Runtime.InteropServices; namespace Microsoft.DotNet.ProjectModel { public static class FileNameSuffixes { + public const string Deps = ".deps"; + + public static PlatformFileNameSuffixes CurrentPlatform + { + get + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return Windows; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) return Linux; + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return OSX; + + throw new InvalidOperationException("Unknown Platform"); + } + } + public static PlatformFileNameSuffixes DotNet { get; } = new PlatformFileNameSuffixes { DynamicLib = ".dll", Exe = ".exe", ProgramDatabase = ".pdb", - StaticLib = ".lib", - Deps = ".deps", + StaticLib = ".lib" }; public static PlatformFileNameSuffixes Windows { get; } = new PlatformFileNameSuffixes @@ -18,8 +32,7 @@ namespace Microsoft.DotNet.ProjectModel DynamicLib = ".dll", Exe = ".exe", ProgramDatabase = ".pdb", - StaticLib = ".lib", - Deps = ".deps", + StaticLib = ".lib" }; public static PlatformFileNameSuffixes OSX { get; } = new PlatformFileNameSuffixes @@ -27,21 +40,26 @@ namespace Microsoft.DotNet.ProjectModel DynamicLib = ".dylib", Exe = "", ProgramDatabase = ".pdb", - StaticLib = ".a", - Deps = ".deps" + StaticLib = ".a" + }; + + public static PlatformFileNameSuffixes Linux { get; } = new PlatformFileNameSuffixes + { + DynamicLib = ".so", + Exe = "", + ProgramDatabase = ".pdb", + StaticLib = ".a" }; public struct PlatformFileNameSuffixes { - public string DynamicLib { get; set; } + public string DynamicLib { get; internal set; } - public string Exe { get; set; } + public string Exe { get; internal set; } - public string ProgramDatabase { get; set; } + public string ProgramDatabase { get; internal set; } - public string StaticLib { get; set; } - - public string Deps { get; set; } + public string StaticLib { get; internal set; } } } } diff --git a/src/Microsoft.DotNet.ProjectModel/Files/ProjectFilesCollection.cs b/src/Microsoft.DotNet.ProjectModel/Files/ProjectFilesCollection.cs index 941a44ca8..8444538b8 100644 --- a/src/Microsoft.DotNet.ProjectModel/Files/ProjectFilesCollection.cs +++ b/src/Microsoft.DotNet.ProjectModel/Files/ProjectFilesCollection.cs @@ -138,7 +138,7 @@ namespace Microsoft.DotNet.ProjectModel.Files get { return SharedPatternsGroup.SearchFiles(_projectDirectory).Distinct(); } } - public IEnumerable GetCopyToOutputFiles(IEnumerable additionalExcludePatterns = null) + public IEnumerable GetContentFiles(IEnumerable additionalExcludePatterns = null) { var patternGroup = new PatternGroup(ContentPatternsGroup.IncludePatterns, ContentPatternsGroup.ExcludePatterns.Concat(additionalExcludePatterns ?? new List()), diff --git a/src/Microsoft.DotNet.Tools.Compiler/Program.cs b/src/Microsoft.DotNet.Tools.Compiler/Program.cs index 9eb216245..db565fe76 100644 --- a/src/Microsoft.DotNet.Tools.Compiler/Program.cs +++ b/src/Microsoft.DotNet.Tools.Compiler/Program.cs @@ -14,6 +14,7 @@ using Microsoft.DotNet.Cli.Compiler.Common; using Microsoft.DotNet.Tools.Common; using Microsoft.DotNet.ProjectModel; using Microsoft.DotNet.ProjectModel.Compilation; +using Microsoft.DotNet.ProjectModel.Graph; using Microsoft.DotNet.ProjectModel.Utilities; using NuGet.Frameworks; using Microsoft.Extensions.DependencyModel; @@ -348,9 +349,9 @@ namespace Microsoft.DotNet.Tools.Compiler if (success && !args.NoHostValue && compilationOptions.EmitEntryPoint.GetValueOrDefault()) { - MakeRunnable(runtimeContext, - outputPath, - libraryExporter); + var projectContext = ProjectContext.Create(context.ProjectDirectory, context.TargetFramework, new[] { RuntimeIdentifier.Current }); + projectContext + .MakeCompilationOutputRunnable(outputPath, args.ConfigValue); } return PrintSummary(diagnostics, sw, success); @@ -380,135 +381,13 @@ namespace Microsoft.DotNet.Tools.Compiler return Path.Combine(outputPath, project.Name + outputExtension); } - private static void CleanOrCreateDirectory(string path) - { - if (Directory.Exists(path)) - { - try - { - Directory.Delete(path, recursive: true); - } - catch (Exception e) - { - Console.WriteLine("Unable to remove directory: " + path); - Console.WriteLine(e.Message); - } - } - - Directory.CreateDirectory(path); - } - - private static void MakeRunnable(ProjectContext runtimeContext, string outputPath, LibraryExporter exporter) - { - CopyContents(runtimeContext, outputPath); - - if (runtimeContext.TargetFramework.IsDesktop()) - { - // On desktop we need to copy dependencies since we don't own the host - foreach (var export in exporter.GetDependencies()) - { - CopyExport(outputPath, export); - } - - GenerateBindingRedirects(runtimeContext, outputPath, exporter); - } - else - { - EmitHost(runtimeContext, outputPath, exporter); - } - } - - private static void GenerateBindingRedirects(ProjectContext runtimeContext, string outputPath, LibraryExporter exporter) - { - var appConfigNames = new[] { "app.config", "App.config" }; - XDocument baseAppConfig = null; - - foreach (var appConfigName in appConfigNames) - { - var baseAppConfigPath = Path.Combine(runtimeContext.ProjectDirectory, appConfigName); - - if (File.Exists(baseAppConfigPath)) - { - using (var fileStream = File.OpenRead(baseAppConfigPath)) - { - baseAppConfig = XDocument.Load(fileStream); - break; - } - } - } - - var generator = new BindingRedirectGenerator(); - var appConfig = generator.Generate(exporter.GetAllExports(), baseAppConfig); - - if (appConfig != null) - { - var path = Path.Combine(outputPath, runtimeContext.ProjectFile.Name + ".exe.config"); - using (var stream = File.Create(path)) - { - appConfig.Save(stream); - } - } - } - + private static void CopyExport(string outputPath, LibraryExport export) { CopyFiles(export.RuntimeAssemblies, outputPath); CopyFiles(export.NativeLibraries, outputPath); } - private static void EmitHost(ProjectContext runtimeContext, string outputPath, LibraryExporter exporter) - { - // Write the Host information file (basically a simplified form of the lock file) - var lines = new List(); - foreach (var export in exporter.GetAllExports()) - { - if (export.Library == runtimeContext.RootProject) - { - continue; - } - - if (export.Library is ProjectDescription) - { - // Copy project dependencies to the output folder - CopyFiles(export.RuntimeAssemblies, outputPath); - CopyFiles(export.NativeLibraries, outputPath); - } - else - { - lines.AddRange(GenerateLines(export, export.RuntimeAssemblies, "runtime")); - lines.AddRange(GenerateLines(export, export.NativeLibraries, "native")); - } - } - - File.WriteAllLines(Path.Combine(outputPath, runtimeContext.ProjectFile.Name + ".deps"), lines); - - // Copy the host in - CopyHost(Path.Combine(outputPath, runtimeContext.ProjectFile.Name + Constants.ExeSuffix)); - } - - private static void CopyHost(string target) - { - var hostPath = Path.Combine(AppContext.BaseDirectory, Constants.HostExecutableName); - File.Copy(hostPath, target, overwrite: true); - } - - private static IEnumerable GenerateLines(LibraryExport export, IEnumerable items, string type) - { - return items.Select(item => - EscapeCsv(export.Library.Identity.Type.Value) + "," + - EscapeCsv(export.Library.Identity.Name) + "," + - EscapeCsv(export.Library.Identity.Version.ToNormalizedString()) + "," + - EscapeCsv(export.Library.Hash) + "," + - EscapeCsv(type) + "," + - EscapeCsv(item.Name) + "," + - EscapeCsv(item.RelativePath) + ","); - } - - private static string EscapeCsv(string input) - { - return "\"" + input.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; - } - private static bool PrintSummary(List diagnostics, Stopwatch sw, bool success = true) { PrintDiagnostics(diagnostics); @@ -663,49 +542,6 @@ namespace Microsoft.DotNet.Tools.Compiler } } - private static void CopyContents(ProjectContext context, string outputPath) - { - var sourceFiles = context.ProjectFile.Files.GetCopyToOutputFiles(); - Copy(sourceFiles, context.ProjectDirectory, outputPath); - } - - private static void Copy(IEnumerable sourceFiles, string sourceDirectory, string targetDirectory) - { - if (sourceFiles == null) - { - throw new ArgumentNullException(nameof(sourceFiles)); - } - - sourceDirectory = EnsureTrailingSlash(sourceDirectory); - targetDirectory = EnsureTrailingSlash(targetDirectory); - - foreach (var sourceFilePath in sourceFiles) - { - var fileName = Path.GetFileName(sourceFilePath); - - var targetFilePath = sourceFilePath.Replace(sourceDirectory, targetDirectory); - var targetFileParentFolder = Path.GetDirectoryName(targetFilePath); - - // Create directory before copying a file - if (!Directory.Exists(targetFileParentFolder)) - { - Directory.CreateDirectory(targetFileParentFolder); - } - - File.Copy( - sourceFilePath, - targetFilePath, - overwrite: true); - - // clear read-only bit if set - var fileAttributes = File.GetAttributes(targetFilePath); - if ((fileAttributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) - { - File.SetAttributes(targetFilePath, fileAttributes & ~FileAttributes.ReadOnly); - } - } - } - private static string EnsureTrailingSlash(string path) { return EnsureTrailingCharacter(path, Path.DirectorySeparatorChar);