diff --git a/Squirrel.sln b/Squirrel.sln index 375518f7..0d083806 100644 --- a/Squirrel.sln +++ b/Squirrel.sln @@ -32,6 +32,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Update.OSX", "src\Update.OSX\Update.OSX.csproj", "{A63B2CDA-5ECC-461C-9B1F-54CF4709ACBD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squirrel.Tool", "src\Squirrel.Tool\Squirrel.Tool.csproj", "{9E769C7E-A54C-4844-8362-727D37BB1578}" + ProjectSection(ProjectDependencies) = postProject + {6B406985-B2E1-4FED-A405-BD0694D68E93} = {6B406985-B2E1-4FED-A405-BD0694D68E93} + {611A03D4-4CDE-4DA0-B151-DE6FAFFB8B8C} = {611A03D4-4CDE-4DA0-B151-DE6FAFFB8B8C} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Squirrel.CommandLine/HelperFile.cs b/src/Squirrel.CommandLine/HelperFile.cs index ee834435..aa229e00 100644 --- a/src/Squirrel.CommandLine/HelperFile.cs +++ b/src/Squirrel.CommandLine/HelperFile.cs @@ -33,6 +33,7 @@ namespace Squirrel.CommandLine AddSearchPath(SquirrelRuntimeInfo.BaseDirectory, "..", "..", "..", "vendor", "wix"); #endif AddSearchPath(SquirrelRuntimeInfo.BaseDirectory, "bin"); + AddSearchPath(SquirrelRuntimeInfo.BaseDirectory, "wix"); } public static void AddSearchPath(params string[] pathParts) @@ -81,8 +82,7 @@ namespace Squirrel.CommandLine .Where(d => !String.IsNullOrEmpty(d)) .Distinct() .Select(d => Path.Combine(d, toFind)) - .Where(d => File.Exists(d) || (File.Exists(d + ".exe") && SquirrelRuntimeInfo.IsWindows)) - .Select(d => File.Exists(d + ".exe") ? d + ".exe" : d) + .Where(d => File.Exists(d)) .Select(Path.GetFullPath); if (predicate != null) diff --git a/src/Squirrel.CommandLine/OSX/Program.cs b/src/Squirrel.CommandLine/OSX/CommandsOSX.cs similarity index 95% rename from src/Squirrel.CommandLine/OSX/Program.cs rename to src/Squirrel.CommandLine/OSX/CommandsOSX.cs index c9042779..b398ed26 100644 --- a/src/Squirrel.CommandLine/OSX/Program.cs +++ b/src/Squirrel.CommandLine/OSX/CommandsOSX.cs @@ -1,5 +1,4 @@ -// See https://aka.ms/new-console-template for more information -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -11,21 +10,19 @@ using Squirrel.SimpleSplat; namespace Squirrel.CommandLine.OSX { [SupportedOSPlatform("osx")] - class Program + class CommandsOSX { - static IFullLogger Log => SquirrelLocator.Current.GetService().GetLogger(typeof(Program)); + static IFullLogger Log => SquirrelLocator.Current.GetService().GetLogger(typeof(CommandsOSX)); - public static int MainOSX(string[] args) + public static CommandSet GetCommands() { - var commands = new CommandSet { + return new CommandSet { "[ Package Authoring ]", { "bundle", "Convert a build directory into a OSX '.app' bundle", new BundleOptions(), Bundle }, { "pack", "Create a Squirrel release from a '.app' bundle", new PackOptions(), Pack }, }; - - return SquirrelHost.Run(args, commands); } - + private static void Pack(PackOptions options) { var targetDir = options.releaseDir ?? Path.Combine(".", "Releases"); diff --git a/src/Squirrel.CommandLine/Program.cs b/src/Squirrel.CommandLine/Program.cs deleted file mode 100644 index beae7bc2..00000000 --- a/src/Squirrel.CommandLine/Program.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Squirrel.CommandLine -{ - public class Program - { - public static int Main(string[] args) - { - if (SquirrelRuntimeInfo.IsWindows) - return Windows.Program.MainWindows(args); - - if (SquirrelRuntimeInfo.IsOSX) - return OSX.Program.MainOSX(args); - - throw new NotSupportedException("Unsupported OS: " + SquirrelRuntimeInfo.SystemOsName); - } - } -} \ No newline at end of file diff --git a/src/Squirrel.CommandLine/Squirrel.CommandLine.csproj b/src/Squirrel.CommandLine/Squirrel.CommandLine.csproj index f1105c28..7504895f 100644 --- a/src/Squirrel.CommandLine/Squirrel.CommandLine.csproj +++ b/src/Squirrel.CommandLine/Squirrel.CommandLine.csproj @@ -3,7 +3,6 @@ net6.0 true - Exe SquirrelCli $(NoWarn);CA2007 diff --git a/src/Squirrel.CommandLine/SquirrelHost.cs b/src/Squirrel.CommandLine/SquirrelHost.cs index b3f0516e..f0662be3 100644 --- a/src/Squirrel.CommandLine/SquirrelHost.cs +++ b/src/Squirrel.CommandLine/SquirrelHost.cs @@ -10,55 +10,70 @@ using Squirrel.SimpleSplat; namespace Squirrel.CommandLine { - internal class SquirrelHost + public class SquirrelHost { -#pragma warning disable CS0436 // Type conflicts with imported type - public static string DisplayVersion => ThisAssembly.AssemblyInformationalVersion + (ThisAssembly.IsPublicRelease ? "" : " (prerelease)"); - public static string FileVersion => ThisAssembly.AssemblyFileVersion; -#pragma warning restore CS0436 // Type conflicts with imported type - - public static int Run(string[] args, CommandSet packageCommands) + public static int Main(string[] args) { var logger = ConsoleLogger.RegisterLogger(); bool help = false; bool verbose = false; + string xplat = null; var globalOptions = new OptionSet() { { "h|?|help", "Ignores all other arguments and shows help text", _ => help = true }, - { "verbose", "Print extra diagnostic logging", _ => verbose = true }, + { "x|xplat=", "Select {PLATFORM} to cross-compile for (eg. win, osx)", v => xplat = v }, + { "verbose", "Print all diagnostic messages", _ => verbose = true }, }; var exeName = Path.GetFileName(SquirrelRuntimeInfo.EntryExePath); string sqUsage = - $"Squirrel {DisplayVersion}, tool for creating and deploying Squirrel releases" + Environment.NewLine + + $"Squirrel {SquirrelRuntimeInfo.SquirrelDisplayVersion}, tool for creating and deploying Squirrel releases" + Environment.NewLine + $"Usage: {exeName} [verb] [--option:value]"; - var commands = new CommandSet { - "", - sqUsage, - "", - "[ Global Options ]", - globalOptions.GetHelpText().TrimEnd(), - "", - packageCommands, - //"[ Package Authoring ]", - //{ "pack", "Creates a Squirrel release from a folder containing application files", new PackOptions(), Pack }, - //{ "releasify", "Take an existing nuget package and convert it into a Squirrel release", new ReleasifyOptions(), Releasify }, - "", - "[ Package Deployment / Syncing ]", - { "s3-down", "Download releases from S3 compatible API", new SyncS3Options(), o => Download(new S3Repository(o)) }, - { "s3-up", "Upload releases to S3 compatible API", new SyncS3Options(), o => Upload(new S3Repository(o)) }, - { "http-down", "Download releases from an HTTP source", new SyncHttpOptions(), o => Download(new SimpleWebRepository(o)) }, - { "github-down", "Download releases from GitHub", new SyncGithubOptions(), o => Download(new GitHubRepository(o)) }, - //"", - //"[ Examples ]", - //$" {exeName} pack ", - //$" ", - }; - try { globalOptions.Parse(args); + if (xplat == null) + xplat = SquirrelRuntimeInfo.SystemOsName; + + CommandSet packageCommands; + + switch (xplat.ToLower()) { + case "win": + case "windows": + if (!SquirrelRuntimeInfo.IsWindows) + logger.Write("Cross-compiling will cause some features of Squirrel to be disabled.", LogLevel.Warn); + packageCommands = Windows.CommandsWindows.GetCommands(); + break; + + case "mac": + case "osx": + case "macos": + if (!SquirrelRuntimeInfo.IsOSX) + logger.Write("Cross-compiling will cause some features of Squirrel to be disabled.", LogLevel.Warn); + packageCommands = OSX.CommandsOSX.GetCommands(); + break; + + default: + throw new NotSupportedException("Unsupported OS platform: " + xplat); + } + + var commands = new CommandSet { + "", + sqUsage, + "", + "[ Global Options ]", + globalOptions.GetHelpText().TrimEnd(), + "", + packageCommands, + "", + "[ Package Deployment / Syncing ]", + { "s3-down", "Download releases from S3 compatible API", new SyncS3Options(), o => Download(new S3Repository(o)) }, + { "s3-up", "Upload releases to S3 compatible API", new SyncS3Options(), o => Upload(new S3Repository(o)) }, + { "http-down", "Download releases from an HTTP source", new SyncHttpOptions(), o => Download(new SimpleWebRepository(o)) }, + { "github-down", "Download releases from GitHub", new SyncGithubOptions(), o => Download(new GitHubRepository(o)) }, + }; + if (verbose) { logger.Level = LogLevel.Debug; } @@ -66,20 +81,21 @@ namespace Squirrel.CommandLine if (help) { commands.WriteHelp(); return 0; - } else { - // parse cli and run command - commands.Execute(args); } - return 0; - } catch (Exception ex) when (ex is OptionValidationException || ex is OptionException) { - // if the arguments fail to validate, print argument help - Console.WriteLine(); - logger.Write(ex.Message, LogLevel.Error); - commands.WriteHelp(); - Console.WriteLine(); - logger.Write(ex.Message, LogLevel.Error); - return -1; + try { + // parse cli and run command + commands.Execute(args); + return 0; + } catch (Exception ex) when (ex is OptionValidationException || ex is OptionException) { + // if the arguments fail to validate, print argument help + Console.WriteLine(); + logger.Write(ex.Message, LogLevel.Error); + commands.WriteHelp(); + Console.WriteLine(); + logger.Write(ex.Message, LogLevel.Error); + return -1; + } } catch (Exception ex) { // for other errors, just print the error and short usage instructions Console.WriteLine(); @@ -95,4 +111,4 @@ namespace Squirrel.CommandLine static void Download(T repo) where T : IPackageRepository => repo.DownloadRecentPackages().GetAwaiter().GetResult(); } -} +} \ No newline at end of file diff --git a/src/Squirrel.CommandLine/Windows/Program.cs b/src/Squirrel.CommandLine/Windows/CommandsWindows.cs similarity index 98% rename from src/Squirrel.CommandLine/Windows/Program.cs rename to src/Squirrel.CommandLine/Windows/CommandsWindows.cs index 04f25261..66d2e592 100644 --- a/src/Squirrel.CommandLine/Windows/Program.cs +++ b/src/Squirrel.CommandLine/Windows/CommandsWindows.cs @@ -16,21 +16,19 @@ using Squirrel.SimpleSplat; namespace Squirrel.CommandLine.Windows { [SupportedOSPlatform("windows")] - class Program : IEnableLogger + class CommandsWindows : IEnableLogger { - static IFullLogger Log => SquirrelLocator.Current.GetService().GetLogger(typeof(Program)); + static IFullLogger Log => SquirrelLocator.Current.GetService().GetLogger(typeof(CommandsWindows)); - public static int MainWindows(string[] args) + public static CommandSet GetCommands() { - var commands = new CommandSet { + return new CommandSet { "[ Package Authoring ]", { "pack", "Creates a Squirrel release from a folder containing application files", new PackOptions(), Pack }, { "releasify", "Take an existing nuget package and convert it into a Squirrel release", new ReleasifyOptions(), Releasify }, }; - - return SquirrelHost.Run(args, commands); } - + static void Pack(PackOptions options) { using (Utility.GetTempDirectory(out var tmp)) { @@ -136,7 +134,7 @@ namespace Squirrel.CommandLine.Windows // warning if the installed SquirrelLib version is not the same as Squirrel.exe StringFileInfo sqLib = null; try { - var myFileVersion = new NuGetVersion(SquirrelHost.FileVersion).Version; + var myFileVersion = new NuGetVersion(SquirrelRuntimeInfo.SquirrelFileVersion).Version; sqLib = Directory.EnumerateFiles(libDir, "SquirrelLib.dll") .Select(f => { StringFileInfo.ReadVersionInfo(f, out var fi); return fi; }) .FirstOrDefault(fi => fi.FileVersion != myFileVersion); @@ -146,7 +144,7 @@ namespace Squirrel.CommandLine.Windows if (sqLib != null) { Log.Warn( $"SquirrelLib.dll {sqLib.FileVersion} is installed in provided package, " + - $"but current Squirrel.exe version is {SquirrelHost.DisplayVersion} ({SquirrelHost.FileVersion}). " + + $"but current Squirrel.exe version is {SquirrelRuntimeInfo.SquirrelDisplayVersion} ({SquirrelRuntimeInfo.SquirrelFileVersion}). " + $"The LIB version and CLI tool version must be the same to build releases " + $"or the application may fail to update properly."); } diff --git a/src/Squirrel.Tool/Program.cs b/src/Squirrel.Tool/Program.cs index 02655c17..f4b0b094 100644 --- a/src/Squirrel.Tool/Program.cs +++ b/src/Squirrel.Tool/Program.cs @@ -1,75 +1,79 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Text.RegularExpressions; -using System.Threading; +using Microsoft.Build.Construction; +using Squirrel.CommandLine; namespace Squirrel.Tool { class Program { - static void Main(string[] args) + static int Main(string[] args) { - var packageName = "Clowd.Squirrel"; - // var dependencies = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.sln", SearchOption.TopDirectoryOnly) - // .SelectMany(GetProjectsFromSln) - // .Distinct() - // .SelectMany(proj => GetSquirrelVersionsFromProject(packageName, proj)) - // .Distinct() - // .ToArray(); - - var dependencies = GetSquirrelVersionsFromProject(packageName); - - if (dependencies.Length == 0) - throw new Exception("Clowd.Squirrel package was not found to be installed in the current solution."); - - if (dependencies.Length > 1) - throw new Exception("Found multiple versions of Clowd.Squirrel installed in current solution. " + - "Please consolidate to a single version: " + string.Join(", ", dependencies)); - - var toolExecutable = SquirrelRuntimeInfo.SystemOsName switch { - "windows" => "Squirrel.exe", - "osx" => "SquirrelMac", - _ => throw new NotSupportedException("OS not supported: " + SquirrelRuntimeInfo.SystemOsName), - }; + if (args.Contains("--csq-embedded-only")) { + return SquirrelHost.Main(args); + } - var packages = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); - var toolPath = Path.Combine(packages, packageName.ToLower(), dependencies.First(), "tools", toolExecutable); + Console.WriteLine($"Squirrel Locator {SquirrelRuntimeInfo.SquirrelDisplayVersion}"); - Process.Start(toolPath, args); + var packageName = "Clowd.Squirrel"; + var dependencies = GetPackageVersionsFromCurrentDir(packageName).Distinct().ToArray(); + + if (dependencies.Length == 0) { + Console.WriteLine("Clowd.Squirrel package was not found to be installed in the current working dir/project."); + Console.WriteLine($"Using bundled Squirrel {SquirrelRuntimeInfo.SquirrelDisplayVersion}"); + return SquirrelHost.Main(args); + } + + if (dependencies.Length > 1) { + throw new Exception("Found multiple versions of Clowd.Squirrel installed in current working dir/project. " + + "Please consolidate to a single version: " + string.Join(", ", dependencies)); + } + + var packages = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); + + var targetVersion = dependencies.First(); + Console.WriteLine("Attempting to locate Squirrel " + targetVersion + " (installed in current working dir)"); + + var dllName = "csq.dll"; + var exeName = "Squirrel.exe"; + var toolDllPath = Path.Combine(packages, packageName.ToLower(), targetVersion, "tools", dllName); + var toolExePath = Path.Combine(packages, packageName.ToLower(), targetVersion, "tools", exeName); + + Process p; + + if (File.Exists(toolDllPath)) { + Console.WriteLine("Running at: " + toolDllPath); + p = Process.Start("dotnet", new[] { dllName, "--csq-embedded-only" }.Concat(args)); + } else if (File.Exists(toolExePath)) { + if (!SquirrelRuntimeInfo.IsWindows) + throw new NotSupportedException($"The installed version {targetVersion} does not support this operating system. Please update."); + Console.WriteLine("Running at: " + toolExePath); + p = Process.Start(toolExePath, args); + } else { + throw new Exception("Unable to locate Squirrel " + targetVersion); + } + + p.WaitForExit(); + return p.ExitCode; } - // static string[] GetProjectsFromSln(string solutionFile) - // { - // var result = ProcessUtil.InvokeProcess("dotnet", new[] { "sln", solutionFile, "list" }, null, CancellationToken.None); - // var proj = result.StdOutput - // .Split('\r', '\n') - // .Select(s => s.TrimEnd()) - // .Where(s => s.EndsWith(".csproj", StringComparison.InvariantCultureIgnoreCase)) - // .Select(s => s.Trim()) - // .ToArray(); - // - // return proj; - // } - - static string[] GetSquirrelVersionsFromProject(string packageName) + static IEnumerable GetPackageVersionsFromCurrentDir(string packageName) { - //dotnet list "$PSScriptRoot\src\Clowd\Clowd.csproj" package - var result = ProcessUtil.InvokeProcess("dotnet", new[] { "list", "package" }, null, CancellationToken.None); - Console.WriteLine(result.StdOutput); - - var escapedName = Regex.Escape(packageName); - var matches = Regex.Matches(result.StdOutput, $@"(?m){escapedName}.*\s(\d{{1,3}}\.\d{{1,3}}\.\d.*?)$"); + foreach (var projFile in Directory.EnumerateFiles(Environment.CurrentDirectory, "*.csproj", SearchOption.AllDirectories)) { + var proj = ProjectRootElement.Open(projFile); + if (proj == null) continue; - if (matches.Count == 0) - return new string[0]; + ProjectItemElement item = proj.Items.FirstOrDefault(i => i.ItemType == "PackageReference" && i.Include == packageName); + if (item == null) continue; - var outp = matches.Select(m => m.Groups[1].Value.Trim()).Distinct().ToArray(); - Console.WriteLine(String.Join(", ", outp)); - Console.WriteLine(String.Join(", ", outp)); - Console.WriteLine(String.Join(", ", outp)); - return outp; + var version = item.Children.FirstOrDefault(x => x.ElementName == "Version") as ProjectMetadataElement; + if (version == null) continue; + + yield return version.Value; + } } } } \ No newline at end of file diff --git a/src/Squirrel.Tool/Squirrel.Tool.csproj b/src/Squirrel.Tool/Squirrel.Tool.csproj index f5f212b1..202d0507 100644 --- a/src/Squirrel.Tool/Squirrel.Tool.csproj +++ b/src/Squirrel.Tool/Squirrel.Tool.csproj @@ -2,7 +2,7 @@ Exe - net5.0;net6.0 + net6.0 true csq csq @@ -19,9 +19,19 @@ MIT A .NET Core Tool that uses the Squirrel framework to create installers and update packages for dotnet applications. + + + + + + - + + + + + diff --git a/src/Squirrel/SquirrelRuntimeInfo.cs b/src/Squirrel/SquirrelRuntimeInfo.cs index 657158c2..a9593b1a 100644 --- a/src/Squirrel/SquirrelRuntimeInfo.cs +++ b/src/Squirrel/SquirrelRuntimeInfo.cs @@ -66,6 +66,12 @@ namespace Squirrel /// public static class SquirrelRuntimeInfo { + /// The current compiled Squirrel display version. + public static string SquirrelDisplayVersion => ThisAssembly.AssemblyInformationalVersion + (ThisAssembly.IsPublicRelease ? "" : " (prerelease)"); + + /// The current compiled Squirrel assembly file version. + public static string SquirrelFileVersion => ThisAssembly.AssemblyFileVersion; + /// The path on disk of the entry assembly. public static string EntryExePath { get; }