From f8d79624c06d5c157435b9ee0dba3ae07d4044d4 Mon Sep 17 00:00:00 2001 From: Bryan Date: Wed, 9 Dec 2015 12:32:27 -0800 Subject: [PATCH 1/4] basic subdirectory support --- src/Microsoft.DotNet.Tools.Publish/Program.cs | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/src/Microsoft.DotNet.Tools.Publish/Program.cs b/src/Microsoft.DotNet.Tools.Publish/Program.cs index 341291362..a8030d4ed 100644 --- a/src/Microsoft.DotNet.Tools.Publish/Program.cs +++ b/src/Microsoft.DotNet.Tools.Publish/Program.cs @@ -64,5 +64,148 @@ namespace Microsoft.DotNet.Tools.Publish return 1; } } + + private static bool CheckArg(CommandOption argument) + { + if (!argument.HasValue()) + { + Reporter.Error.WriteLine($"Missing required argument: {argument.LongName.Red().Bold()}"); + return false; + } + return true; + } + + // return the matching framework/runtime ProjectContext. + // if 'nugetframework' or 'runtime' is null or empty then it matches with any. + private static IEnumerable GetMatchingProjectContexts(IEnumerable contexts, NuGetFramework framework, string runtimeIdentifier) + { + var matchingContexts = contexts.Where(context => + { + if (context.TargetFramework == null || string.IsNullOrEmpty(context.RuntimeIdentifier)) + { + return false; + } + + if (string.IsNullOrEmpty(runtimeIdentifier) || runtimeIdentifier.Equals(context.RuntimeIdentifier)) + { + if (framework == null || framework.Equals(context.TargetFramework)) + { + return true; + } + } + + return false; + }); + + return matchingContexts; + } + + /// + /// Publish the project for given 'framework (ex - dnxcore50)' and 'runtimeID (ex - win7-x64)' + /// + /// project that is to be published + /// Location of published files + /// Debug or Release + /// Return 0 if successful else return non-zero + private static int Publish(ProjectContext context, string outputPath, string configuration) + { + Reporter.Output.WriteLine($"Publishing {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}/{context.RuntimeIdentifier.Yellow()}"); + + var options = context.ProjectFile.GetCompilerOptions(context.TargetFramework, configuration); + + // Generate the output path + if (string.IsNullOrEmpty(outputPath)) + { + outputPath = Path.Combine( + context.ProjectFile.ProjectDirectory, + Constants.BinDirectoryName, + configuration, + context.TargetFramework.GetTwoDigitShortFolderName(), + context.RuntimeIdentifier); + } + + if (!Directory.Exists(outputPath)) + { + Directory.CreateDirectory(outputPath); + } + + // Compile the project (and transitively, all it's dependencies) + var result = Command.Create("dotnet-compile", + $"--framework \"{context.TargetFramework.DotNetFrameworkName}\" " + + $"--output \"{outputPath}\" " + + $"--configuration \"{configuration}\" " + + "--no-host " + + $"\"{context.ProjectFile.ProjectDirectory}\"") + .ForwardStdErr() + .ForwardStdOut() + .Execute(); + + if (result.ExitCode != 0) + { + return result.ExitCode; + } + + // Use a library exporter to collect publish assets + var exporter = context.CreateExporter(configuration); + + foreach (var export in exporter.GetAllExports()) + { + // Skip copying project references + if (export.Library is ProjectDescription) + { + continue; + } + + Reporter.Verbose.WriteLine($"Publishing {export.Library.Identity.ToString().Green().Bold()} ..."); + + PublishFiles(export.RuntimeAssemblies, outputPath); + PublishFiles(export.NativeLibraries, outputPath); + } + + // Publish a host if this is an application + if (options.EmitEntryPoint.GetValueOrDefault()) + { + Reporter.Verbose.WriteLine($"Making {context.ProjectFile.Name.Cyan()} runnable ..."); + PublishHost(context, outputPath); + } + + Reporter.Output.WriteLine($"Published to {outputPath}".Green().Bold()); + return 0; + } + + private static int PublishHost(ProjectContext context, string outputPath) + { + if (context.TargetFramework.IsDesktop()) + { + return 0; + } + + var hostPath = Path.Combine(AppContext.BaseDirectory, Constants.HostExecutableName); + if (!File.Exists(hostPath)) + { + Reporter.Error.WriteLine($"Cannot find {Constants.HostExecutableName} in the dotnet directory.".Red()); + return 1; + } + + var outputExe = Path.Combine(outputPath, context.ProjectFile.Name + Constants.ExeSuffix); + + // Copy the host + File.Copy(hostPath, outputExe, overwrite: true); + + return 0; + } + + private static void PublishFiles(IEnumerable files, string outputPath, bool subdirectories) + { + foreach (var file in files) + { + var copyDir = subdirectories ? Path.Combine(outputPath, Path.GetDirectoryName(copyPath)) : outputPath; + if (!Directory.Exists(copyDir)) + { + Directory.CreateDirectory(copyDir); + } + File.Copy(path, Path.Combine(copyDir, Path.GetFileName(file.ResolvedPath)), overwrite: true); + } + } } } From bcf9392c40588ff8b47b12a48986b0f343a6c53b Mon Sep 17 00:00:00 2001 From: Bryan Date: Wed, 9 Dec 2015 12:37:21 -0800 Subject: [PATCH 2/4] Opt in support for including subdirectories from nuget depdencies in publish output. --- src/Microsoft.DotNet.Tools.Publish/Program.cs | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.Tools.Publish/Program.cs b/src/Microsoft.DotNet.Tools.Publish/Program.cs index a8030d4ed..3d8e5451e 100644 --- a/src/Microsoft.DotNet.Tools.Publish/Program.cs +++ b/src/Microsoft.DotNet.Tools.Publish/Program.cs @@ -26,6 +26,7 @@ namespace Microsoft.DotNet.Tools.Publish var output = app.Option("-o|--output ", "Path in which to publish the app", CommandOptionType.SingleValue); var configuration = app.Option("-c|--configuration ", "Configuration under which to build", CommandOptionType.SingleValue); var projectPath = app.Argument("", "The project to publish, defaults to the current directory. Can be a path to a project.json or a project directory"); + var subdirectories = app.Option("--subdir", "Include Subdirectories in native assets in the output", CommandOptionType.NoValue); app.OnExecute(() => { @@ -107,7 +108,7 @@ namespace Microsoft.DotNet.Tools.Publish /// Location of published files /// Debug or Release /// Return 0 if successful else return non-zero - private static int Publish(ProjectContext context, string outputPath, string configuration) + private static int Publish(ProjectContext context, string outputPath, string configuration, bool subdirectories) { Reporter.Output.WriteLine($"Publishing {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}/{context.RuntimeIdentifier.Yellow()}"); @@ -158,8 +159,8 @@ namespace Microsoft.DotNet.Tools.Publish Reporter.Verbose.WriteLine($"Publishing {export.Library.Identity.ToString().Green().Bold()} ..."); - PublishFiles(export.RuntimeAssemblies, outputPath); - PublishFiles(export.NativeLibraries, outputPath); + PublishFiles(export.RuntimeAssemblies, outputPath, false); + PublishFiles(export.NativeLibraries, outputPath, subdirectories); } // Publish a host if this is an application @@ -199,13 +200,44 @@ namespace Microsoft.DotNet.Tools.Publish { foreach (var file in files) { - var copyDir = subdirectories ? Path.Combine(outputPath, Path.GetDirectoryName(copyPath)) : outputPath; - if (!Directory.Exists(copyDir)) + var destinationDirectory = outputPath; + + if (subdirectories) { - Directory.CreateDirectory(copyDir); + destinationDirectory = Path.Combine(outputPath, GetNativeRelativeSubdirectory(file.RelativePath)); } - File.Copy(path, Path.Combine(copyDir, Path.GetFileName(file.ResolvedPath)), overwrite: true); + + if (!Directory.Exists(destinationDirectory)) + { + Directory.CreateDirectory(destinationDirectory); + } + + File.Copy(file.ResolvedPath, Path.Combine(destinationDirectory, Path.GetFileName(file.ResolvedPath)), overwrite: true); } } + + private static string GetNativeRelativeSubdirectory(string filepath) + { + string directoryPath = Path.GetDirectoryName(filepath); + + string[] parts = directoryPath.Split(new string[] { "native" }, 2, StringSplitOptions.None); + + if (parts.Length != 2) + { + throw new UnrecognizedNativeDirectoryFormat() { FailedPath = filepath }; + } + + string candidate = parts[1]; + candidate = candidate.TrimStart(new char[] { '/', '\\' }); + + return candidate; + } + + private class UnrecognizedNativeDirectoryFormat : Exception + { + public string FailedPath { get; set; } + } } + + } From 477b8737db863ef44eff33e5d7c9264ba7d3b074 Mon Sep 17 00:00:00 2001 From: Bryan Date: Thu, 10 Dec 2015 11:56:58 -0800 Subject: [PATCH 3/4] Change name of option to --native-subdirectory, Factor for less intrusive changes --- src/Microsoft.DotNet.Tools.Publish/Program.cs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.DotNet.Tools.Publish/Program.cs b/src/Microsoft.DotNet.Tools.Publish/Program.cs index 3d8e5451e..0c1ae9f59 100644 --- a/src/Microsoft.DotNet.Tools.Publish/Program.cs +++ b/src/Microsoft.DotNet.Tools.Publish/Program.cs @@ -26,7 +26,7 @@ namespace Microsoft.DotNet.Tools.Publish var output = app.Option("-o|--output ", "Path in which to publish the app", CommandOptionType.SingleValue); var configuration = app.Option("-c|--configuration ", "Configuration under which to build", CommandOptionType.SingleValue); var projectPath = app.Argument("", "The project to publish, defaults to the current directory. Can be a path to a project.json or a project directory"); - var subdirectories = app.Option("--subdir", "Include Subdirectories in native assets in the output", CommandOptionType.NoValue); + var nativeSubdirectories = app.Option("--native-subdirectory", "Include subdirectories from native assets of dependency packages in output", CommandOptionType.NoValue); app.OnExecute(() => { @@ -108,7 +108,7 @@ namespace Microsoft.DotNet.Tools.Publish /// Location of published files /// Debug or Release /// Return 0 if successful else return non-zero - private static int Publish(ProjectContext context, string outputPath, string configuration, bool subdirectories) + private static int Publish(ProjectContext context, string outputPath, string configuration, bool nativeSubdirectories) { Reporter.Output.WriteLine($"Publishing {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}/{context.RuntimeIdentifier.Yellow()}"); @@ -160,7 +160,7 @@ namespace Microsoft.DotNet.Tools.Publish Reporter.Verbose.WriteLine($"Publishing {export.Library.Identity.ToString().Green().Bold()} ..."); PublishFiles(export.RuntimeAssemblies, outputPath, false); - PublishFiles(export.NativeLibraries, outputPath, subdirectories); + PublishFiles(export.NativeLibraries, outputPath, nativeSubdirectories); } // Publish a host if this is an application @@ -196,16 +196,11 @@ namespace Microsoft.DotNet.Tools.Publish return 0; } - private static void PublishFiles(IEnumerable files, string outputPath, bool subdirectories) + private static void PublishFiles(IEnumerable files, string outputPath, bool nativeSubdirectories) { foreach (var file in files) { - var destinationDirectory = outputPath; - - if (subdirectories) - { - destinationDirectory = Path.Combine(outputPath, GetNativeRelativeSubdirectory(file.RelativePath)); - } + var destinationDirectory = DetermineFileDestinationDirectory(file, outputPath, nativeSubdirectories); if (!Directory.Exists(destinationDirectory)) { @@ -216,6 +211,18 @@ namespace Microsoft.DotNet.Tools.Publish } } + private static string DetermineFileDestinationDirectory(LibraryAsset file, string outputPath, bool nativeSubdirectories) + { + var destinationDirectory = outputPath; + + if (nativeSubdirectories) + { + destinationDirectory = Path.Combine(outputPath, GetNativeRelativeSubdirectory(file.RelativePath)); + } + + return destinationDirectory; + } + private static string GetNativeRelativeSubdirectory(string filepath) { string directoryPath = Path.GetDirectoryName(filepath); From 4580aab2e0c013f14e594385d9be145b2eeb8862 Mon Sep 17 00:00:00 2001 From: Bryan Thornbury Date: Mon, 21 Dec 2015 12:23:05 -0800 Subject: [PATCH 4/4] pipe through subdirectory support after rebase. --- src/Microsoft.DotNet.Tools.Publish/Program.cs | 186 +----------------- .../PublishCommand.cs | 49 ++++- 2 files changed, 46 insertions(+), 189 deletions(-) diff --git a/src/Microsoft.DotNet.Tools.Publish/Program.cs b/src/Microsoft.DotNet.Tools.Publish/Program.cs index 0c1ae9f59..b6dd956d1 100644 --- a/src/Microsoft.DotNet.Tools.Publish/Program.cs +++ b/src/Microsoft.DotNet.Tools.Publish/Program.cs @@ -26,7 +26,7 @@ namespace Microsoft.DotNet.Tools.Publish var output = app.Option("-o|--output ", "Path in which to publish the app", CommandOptionType.SingleValue); var configuration = app.Option("-c|--configuration ", "Configuration under which to build", CommandOptionType.SingleValue); var projectPath = app.Argument("", "The project to publish, defaults to the current directory. Can be a path to a project.json or a project directory"); - var nativeSubdirectories = app.Option("--native-subdirectory", "Include subdirectories from native assets of dependency packages in output", CommandOptionType.NoValue); + var nativeSubdirectories = app.Option("--native-subdirectory", "Temporary mechanism to include subdirectories from native assets of dependency packages in output", CommandOptionType.NoValue); app.OnExecute(() => { @@ -37,6 +37,7 @@ namespace Microsoft.DotNet.Tools.Publish publish.Runtime = runtime.Value() ?? RuntimeIdentifier.Current; publish.OutputPath = output.Value(); publish.Configuration = configuration.Value() ?? Constants.DefaultConfiguration; + publish.NativeSubdirectories = nativeSubdirectories.HasValue(); publish.ProjectPath = projectPath.Value; if (string.IsNullOrEmpty(publish.ProjectPath)) @@ -65,186 +66,5 @@ namespace Microsoft.DotNet.Tools.Publish return 1; } } - - private static bool CheckArg(CommandOption argument) - { - if (!argument.HasValue()) - { - Reporter.Error.WriteLine($"Missing required argument: {argument.LongName.Red().Bold()}"); - return false; - } - return true; - } - - // return the matching framework/runtime ProjectContext. - // if 'nugetframework' or 'runtime' is null or empty then it matches with any. - private static IEnumerable GetMatchingProjectContexts(IEnumerable contexts, NuGetFramework framework, string runtimeIdentifier) - { - var matchingContexts = contexts.Where(context => - { - if (context.TargetFramework == null || string.IsNullOrEmpty(context.RuntimeIdentifier)) - { - return false; - } - - if (string.IsNullOrEmpty(runtimeIdentifier) || runtimeIdentifier.Equals(context.RuntimeIdentifier)) - { - if (framework == null || framework.Equals(context.TargetFramework)) - { - return true; - } - } - - return false; - }); - - return matchingContexts; - } - - /// - /// Publish the project for given 'framework (ex - dnxcore50)' and 'runtimeID (ex - win7-x64)' - /// - /// project that is to be published - /// Location of published files - /// Debug or Release - /// Return 0 if successful else return non-zero - private static int Publish(ProjectContext context, string outputPath, string configuration, bool nativeSubdirectories) - { - Reporter.Output.WriteLine($"Publishing {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}/{context.RuntimeIdentifier.Yellow()}"); - - var options = context.ProjectFile.GetCompilerOptions(context.TargetFramework, configuration); - - // Generate the output path - if (string.IsNullOrEmpty(outputPath)) - { - outputPath = Path.Combine( - context.ProjectFile.ProjectDirectory, - Constants.BinDirectoryName, - configuration, - context.TargetFramework.GetTwoDigitShortFolderName(), - context.RuntimeIdentifier); - } - - if (!Directory.Exists(outputPath)) - { - Directory.CreateDirectory(outputPath); - } - - // Compile the project (and transitively, all it's dependencies) - var result = Command.Create("dotnet-compile", - $"--framework \"{context.TargetFramework.DotNetFrameworkName}\" " + - $"--output \"{outputPath}\" " + - $"--configuration \"{configuration}\" " + - "--no-host " + - $"\"{context.ProjectFile.ProjectDirectory}\"") - .ForwardStdErr() - .ForwardStdOut() - .Execute(); - - if (result.ExitCode != 0) - { - return result.ExitCode; - } - - // Use a library exporter to collect publish assets - var exporter = context.CreateExporter(configuration); - - foreach (var export in exporter.GetAllExports()) - { - // Skip copying project references - if (export.Library is ProjectDescription) - { - continue; - } - - Reporter.Verbose.WriteLine($"Publishing {export.Library.Identity.ToString().Green().Bold()} ..."); - - PublishFiles(export.RuntimeAssemblies, outputPath, false); - PublishFiles(export.NativeLibraries, outputPath, nativeSubdirectories); - } - - // Publish a host if this is an application - if (options.EmitEntryPoint.GetValueOrDefault()) - { - Reporter.Verbose.WriteLine($"Making {context.ProjectFile.Name.Cyan()} runnable ..."); - PublishHost(context, outputPath); - } - - Reporter.Output.WriteLine($"Published to {outputPath}".Green().Bold()); - return 0; - } - - private static int PublishHost(ProjectContext context, string outputPath) - { - if (context.TargetFramework.IsDesktop()) - { - return 0; - } - - var hostPath = Path.Combine(AppContext.BaseDirectory, Constants.HostExecutableName); - if (!File.Exists(hostPath)) - { - Reporter.Error.WriteLine($"Cannot find {Constants.HostExecutableName} in the dotnet directory.".Red()); - return 1; - } - - var outputExe = Path.Combine(outputPath, context.ProjectFile.Name + Constants.ExeSuffix); - - // Copy the host - File.Copy(hostPath, outputExe, overwrite: true); - - return 0; - } - - private static void PublishFiles(IEnumerable files, string outputPath, bool nativeSubdirectories) - { - foreach (var file in files) - { - var destinationDirectory = DetermineFileDestinationDirectory(file, outputPath, nativeSubdirectories); - - if (!Directory.Exists(destinationDirectory)) - { - Directory.CreateDirectory(destinationDirectory); - } - - File.Copy(file.ResolvedPath, Path.Combine(destinationDirectory, Path.GetFileName(file.ResolvedPath)), overwrite: true); - } - } - - private static string DetermineFileDestinationDirectory(LibraryAsset file, string outputPath, bool nativeSubdirectories) - { - var destinationDirectory = outputPath; - - if (nativeSubdirectories) - { - destinationDirectory = Path.Combine(outputPath, GetNativeRelativeSubdirectory(file.RelativePath)); - } - - return destinationDirectory; - } - - private static string GetNativeRelativeSubdirectory(string filepath) - { - string directoryPath = Path.GetDirectoryName(filepath); - - string[] parts = directoryPath.Split(new string[] { "native" }, 2, StringSplitOptions.None); - - if (parts.Length != 2) - { - throw new UnrecognizedNativeDirectoryFormat() { FailedPath = filepath }; - } - - string candidate = parts[1]; - candidate = candidate.TrimStart(new char[] { '/', '\\' }); - - return candidate; - } - - private class UnrecognizedNativeDirectoryFormat : Exception - { - public string FailedPath { get; set; } - } - } - - + } } diff --git a/src/Microsoft.DotNet.Tools.Publish/PublishCommand.cs b/src/Microsoft.DotNet.Tools.Publish/PublishCommand.cs index 9fd50bffb..5666b3c31 100644 --- a/src/Microsoft.DotNet.Tools.Publish/PublishCommand.cs +++ b/src/Microsoft.DotNet.Tools.Publish/PublishCommand.cs @@ -19,6 +19,7 @@ namespace Microsoft.DotNet.Tools.Publish public string OutputPath { get; set; } public string Framework { get; set; } public string Runtime { get; set; } + public bool NativeSubdirectories { get; set; } public NuGetFramework NugetFramework { get; set; } public IEnumerable ProjectContexts { get; set; } @@ -57,7 +58,7 @@ namespace Microsoft.DotNet.Tools.Publish NumberOfProjects = 0; foreach (var project in ProjectContexts) { - if (PublishProjectContext(project, OutputPath, Configuration)) + if (PublishProjectContext(project, OutputPath, Configuration, NativeSubdirectories)) { NumberOfPublishedProjects++; } @@ -96,7 +97,7 @@ namespace Microsoft.DotNet.Tools.Publish /// Location of published files /// Debug or Release /// Return 0 if successful else return non-zero - private static bool PublishProjectContext(ProjectContext context, string outputPath, string configuration) + private static bool PublishProjectContext(ProjectContext context, string outputPath, string configuration, bool nativeSubdirectories) { Reporter.Output.WriteLine($"Publishing {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}/{context.RuntimeIdentifier.Yellow()}"); @@ -147,8 +148,8 @@ namespace Microsoft.DotNet.Tools.Publish Reporter.Verbose.WriteLine($"Publishing {export.Library.Identity.ToString().Green().Bold()} ..."); - PublishFiles(export.RuntimeAssemblies, outputPath); - PublishFiles(export.NativeLibraries, outputPath); + PublishFiles(export.RuntimeAssemblies, outputPath, false); + PublishFiles(export.NativeLibraries, outputPath, nativeSubdirectories); } // Publish a host if this is an application @@ -184,12 +185,48 @@ namespace Microsoft.DotNet.Tools.Publish return 0; } - private static void PublishFiles(IEnumerable files, string outputPath) + private static void PublishFiles(IEnumerable files, string outputPath, bool nativeSubdirectories) { foreach (var file in files) { - File.Copy(file.ResolvedPath, Path.Combine(outputPath, Path.GetFileName(file.ResolvedPath)), overwrite: true); + var destinationDirectory = DetermineFileDestinationDirectory(file, outputPath, nativeSubdirectories); + + if (!Directory.Exists(destinationDirectory)) + { + Directory.CreateDirectory(destinationDirectory); + } + + File.Copy(file.ResolvedPath, Path.Combine(destinationDirectory, Path.GetFileName(file.ResolvedPath)), overwrite: true); } } + + private static string DetermineFileDestinationDirectory(LibraryAsset file, string outputPath, bool nativeSubdirectories) + { + var destinationDirectory = outputPath; + + if (nativeSubdirectories) + { + destinationDirectory = Path.Combine(outputPath, GetNativeRelativeSubdirectory(file.RelativePath)); + } + + return destinationDirectory; + } + + private static string GetNativeRelativeSubdirectory(string filepath) + { + string directoryPath = Path.GetDirectoryName(filepath); + + string[] parts = directoryPath.Split(new string[] { "native" }, 2, StringSplitOptions.None); + + if (parts.Length != 2) + { + throw new Exception("Unrecognized Native Directory Format: " + filepath); + } + + string candidate = parts[1]; + candidate = candidate.TrimStart(new char[] { '/', '\\' }); + + return candidate; + } } }