From 1e328bdfb95698933b0bdeb6f6046a9ee1f89552 Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Thu, 21 Nov 2019 15:29:37 +0100 Subject: [PATCH] - Add a dotnet {deb|rpm|zip|tarball} install command - Do an implicit restore before running dotnet {deb|rpm|zip|tarball} - After restoring, check whether Packaging.Targets is present in the lock file. --- dotnet-deb/Program.cs | 2 +- dotnet-deb/dotnet-deb.csproj | 3 + dotnet-rpm/PackagingRunner.cs | 179 +++++++++++++++++++++------ dotnet-rpm/Program.cs | 9 +- dotnet-rpm/dotnet-rpm.csproj | 3 + dotnet-tarball/Program.cs | 4 +- dotnet-tarball/dotnet-tarball.csproj | 3 + dotnet-zip/Program.cs | 4 +- dotnet-zip/dotnet-zip.csproj | 3 + 9 files changed, 161 insertions(+), 49 deletions(-) diff --git a/dotnet-deb/Program.cs b/dotnet-deb/Program.cs index 9ba46c3..402bb22 100644 --- a/dotnet-deb/Program.cs +++ b/dotnet-deb/Program.cs @@ -6,7 +6,7 @@ namespace Dotnet.Packaging { static int Main(string[] args) { - PackagingRunner runner = new PackagingRunner("Debian/Ubuntu installer package", "CreateDeb"); + PackagingRunner runner = new PackagingRunner("Debian/Ubuntu installer package", "CreateDeb", "deb"); return runner.Run(args); } } diff --git a/dotnet-deb/dotnet-deb.csproj b/dotnet-deb/dotnet-deb.csproj index 89448fd..d6c67d9 100644 --- a/dotnet-deb/dotnet-deb.csproj +++ b/dotnet-deb/dotnet-deb.csproj @@ -14,6 +14,9 @@ See https://github.com/qmfrederik/dotnet-packaging/ for more information on how + + + diff --git a/dotnet-rpm/PackagingRunner.cs b/dotnet-rpm/PackagingRunner.cs index 7ec0b77..44570ab 100644 --- a/dotnet-rpm/PackagingRunner.cs +++ b/dotnet-rpm/PackagingRunner.cs @@ -1,7 +1,16 @@ using McMaster.Extensions.CommandLineUtils; +using Microsoft.Build.Locator; +using Newtonsoft.Json; using System; +using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Linq; using System.Text; +using ConsoleLogger = Microsoft.Build.Logging.ConsoleLogger; +using IMSBuildLogger = Microsoft.Build.Framework.ILogger; +using LoggerVerbosity = Microsoft.Build.Framework.LoggerVerbosity; +using MSBuild = Microsoft.Build.Evaluation; namespace Dotnet.Packaging { @@ -9,11 +18,15 @@ namespace Dotnet.Packaging { private readonly string outputName; private readonly string msbuildTarget; + private readonly string commandName; - public PackagingRunner(string outputName, string msbuildTarget) + public PackagingRunner(string outputName, string msbuildTarget, string commandName) { + MSBuildLocator.RegisterDefaults(); + this.outputName = outputName; this.msbuildTarget = msbuildTarget; + this.commandName = commandName; } public int Run(string[] args) @@ -36,40 +49,84 @@ namespace Dotnet.Packaging CommandOptionType.SingleValue); CommandOption versionSuffix = commandLineApplication.Option( - "---version-suffix ", + "--version-suffix ", "Defines the value for the $(VersionSuffix) property in the project.", CommandOptionType.SingleValue); + CommandOption noRestore = commandLineApplication.Option( + "--no-restore", + "Do not restore the project before building.", + CommandOptionType.NoValue); + commandLineApplication.HelpOption("-? | -h | --help"); - commandLineApplication.FullName = $"dotnet {this.outputName}"; + commandLineApplication.FullName = $"dotnet {this.commandName}"; commandLineApplication.LongVersionGetter = () => ThisAssembly.AssemblyInformationalVersion; commandLineApplication.ExtendedHelpText = $"{Environment.NewLine}See https://github.com/qmfrederik/dotnet-packaging for more information"; + var installCommand = commandLineApplication.Command( + "install", + command => command.OnExecute(() => + { + Console.WriteLine($"dotnet {this.commandName} ({ThisAssembly.AssemblyInformationalVersion})"); + + // Create/update the Directory.Build.props file in the directory of the version.json file to add the Packaging.Targets package. + string directoryBuildPropsPath = Path.Combine(Environment.CurrentDirectory, "Directory.Build.props"); + MSBuild.Project propsFile; + if (File.Exists(directoryBuildPropsPath)) + { + propsFile = new MSBuild.Project(directoryBuildPropsPath); + } + else + { + propsFile = new MSBuild.Project(); + } + + const string PackageReferenceItemType = "PackageReference"; + const string PackageId = "Packaging.Targets"; + if (!propsFile.GetItemsByEvaluatedInclude(PackageId).Any(i => i.ItemType == PackageReferenceItemType && i.EvaluatedInclude == PackageId)) + { + string packageVersion = ThisAssembly.AssemblyInformationalVersion.Replace("+", "-g"); + propsFile.AddItem( + PackageReferenceItemType, + PackageId, + new Dictionary + { + { "Version", packageVersion }, + { "PrivateAssets", "all" }, + }); + + propsFile.Save(directoryBuildPropsPath); + } + + Console.WriteLine($"Successfully installed dotnet {this.commandName}. Now run 'dotnet {this.commandName}' to package your"); + Console.WriteLine($"application as a {this.outputName}"); + })); + commandLineApplication.OnExecute(() => { - Console.WriteLine($"dotnet {this.outputName} ({ThisAssembly.AssemblyInformationalVersion})"); + Console.WriteLine($"dotnet {this.commandName} ({ThisAssembly.AssemblyInformationalVersion})"); - if (!framework.HasValue()) + if (!noRestore.HasValue()) { - Console.WriteLine("You must specify a target framework."); - commandLineApplication.ShowHint(); - - return -1; - } - - if (!runtime.HasValue()) - { - Console.WriteLine("You must specify a target runtime."); - commandLineApplication.ShowHint(); - - return -1; + if (!this.IsPackagingTargetsInstalled()) + { + return -1; + } } StringBuilder msbuildArguments = new StringBuilder(); msbuildArguments.Append($"msbuild /t:{msbuildTarget} "); - msbuildArguments.Append($"/p:RuntimeIdentifier={runtime.Value()} "); - msbuildArguments.Append($"/p:TargetFramework={framework.Value()} "); + + if (runtime.HasValue()) + { + msbuildArguments.Append($"/p:RuntimeIdentifier={runtime.Value()} "); + } + + if (framework.HasValue()) + { + msbuildArguments.Append($"/p:TargetFramework={framework.Value()} "); + } if (configuration.HasValue()) { @@ -81,25 +138,77 @@ namespace Dotnet.Packaging msbuildArguments.Append($"/p:VersionSuffix={versionSuffix.Value()} "); } - var psi = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = msbuildArguments.ToString() - }; - - var process = new Process - { - StartInfo = psi, - - }; - - process.Start(); - process.WaitForExit(); - - return process.ExitCode; + return RunDotnet(msbuildArguments); }); return commandLineApplication.Execute(args); } + + public int RunDotnet(StringBuilder msbuildArguments) + { + var psi = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = msbuildArguments.ToString() + }; + + var process = new Process + { + StartInfo = psi, + + }; + + process.Start(); + process.WaitForExit(); + + return process.ExitCode; + } + + public bool IsPackagingTargetsInstalled() + { + var projectFilePath = Directory.GetFiles(Environment.CurrentDirectory, "*.csproj").SingleOrDefault(); + + if (projectFilePath == null) + { + Console.Error.WriteLine($"Failed to find a .csproj file in '{Environment.CurrentDirectory}'. dotnet {this.commandName} only works if"); + Console.Error.WriteLine($"you have exactly one .csproj file in your directory. For advanced scenarios, please use 'dotnet msbuild /t:{this.msbuildTarget}'"); + return false; + } + + var loggers = new IMSBuildLogger[] { new ConsoleLogger(LoggerVerbosity.Quiet) }; + var project = new MSBuild.Project(projectFilePath); + + if (!project.Build("Restore", loggers)) + { + Console.Error.WriteLine($"Failed to restore '{Path.GetFileName(projectFilePath)}'. Please run dotnet restore, and try again."); + return false; + } + + var projectAssetsPath = project.GetPropertyValue("ProjectAssetsFile"); + + // NuGet has a LockFileUtilities.GetLockFile API which provides direct access to this file format, + // but loading NuGet in the same process as MSBuild creates dependency conflicts. + LockFile lockFile = null; + using (StreamReader reader = File.OpenText(projectAssetsPath)) + using (JsonReader jsonReader = new JsonTextReader(reader)) + { + JsonSerializer serializer = new JsonSerializer(); + lockFile = serializer.Deserialize(jsonReader); + } + + if (!lockFile.Libraries.Any(l => l.Key.StartsWith("Packaging.Targets/"))) + { + Console.Error.WriteLine($"The project '{Path.GetFileName(projectFilePath)}' doesn't have a PackageReference to Packaging.Targets."); + Console.Error.WriteLine($"Please run 'dotnet {this.commandName} install', and try again."); + return false; + } + + return true; + } + + class LockFile + { + public Dictionary Libraries; + } } } diff --git a/dotnet-rpm/Program.cs b/dotnet-rpm/Program.cs index e0bb756..bc0a946 100644 --- a/dotnet-rpm/Program.cs +++ b/dotnet-rpm/Program.cs @@ -1,15 +1,10 @@ -using McMaster.Extensions.CommandLineUtils; -using System; -using System.Diagnostics; -using System.Text; - -namespace Dotnet.Packaging +namespace Dotnet.Packaging { public class Program { public static int Main(string[] args) { - PackagingRunner runner = new PackagingRunner("RPM package", "CreateRpm"); + PackagingRunner runner = new PackagingRunner("RPM package", "CreateRpm", "rpm"); return runner.Run(args); } } diff --git a/dotnet-rpm/dotnet-rpm.csproj b/dotnet-rpm/dotnet-rpm.csproj index 45ecc1c..9b65e95 100644 --- a/dotnet-rpm/dotnet-rpm.csproj +++ b/dotnet-rpm/dotnet-rpm.csproj @@ -14,5 +14,8 @@ See https://github.com/qmfrederik/dotnet-packaging/ for more information on how + + + \ No newline at end of file diff --git a/dotnet-tarball/Program.cs b/dotnet-tarball/Program.cs index c3bf44d..1ace0ce 100644 --- a/dotnet-tarball/Program.cs +++ b/dotnet-tarball/Program.cs @@ -1,12 +1,10 @@ -using System.Diagnostics; - namespace Dotnet.Packaging { class Program { static int Main(string[] args) { - PackagingRunner runner = new PackagingRunner("tarball", "CreateTarball"); + PackagingRunner runner = new PackagingRunner("tarball", "CreateTarball", "tarball"); return runner.Run(args); } } diff --git a/dotnet-tarball/dotnet-tarball.csproj b/dotnet-tarball/dotnet-tarball.csproj index 99a52a7..c64712b 100644 --- a/dotnet-tarball/dotnet-tarball.csproj +++ b/dotnet-tarball/dotnet-tarball.csproj @@ -14,6 +14,9 @@ See https://github.com/qmfrederik/dotnet-packaging/ for more information on how + + + diff --git a/dotnet-zip/Program.cs b/dotnet-zip/Program.cs index 7ff0594..0e8403e 100644 --- a/dotnet-zip/Program.cs +++ b/dotnet-zip/Program.cs @@ -1,12 +1,10 @@ -using System.Diagnostics; - namespace Dotnet.Packaging { class Program { static int Main(string[] args) { - PackagingRunner runner = new PackagingRunner("zip archive", "CreateZip"); + PackagingRunner runner = new PackagingRunner("zip archive", "CreateZip", "zip"); return runner.Run(args); } } diff --git a/dotnet-zip/dotnet-zip.csproj b/dotnet-zip/dotnet-zip.csproj index 05f1874..5175f41 100644 --- a/dotnet-zip/dotnet-zip.csproj +++ b/dotnet-zip/dotnet-zip.csproj @@ -14,6 +14,9 @@ See https://github.com/qmfrederik/dotnet-packaging/ for more information on how + + +