From bfb697156460c2fc2c535c4f2cbad90dedad270f Mon Sep 17 00:00:00 2001 From: Holger Boerchers Date: Thu, 16 Aug 2018 21:36:25 +0200 Subject: [PATCH] Added another command line parser. Has a nice parameter verification framework --- Creator/App.config | 4 + Creator/ApplicationArguments.cs | 54 ------- Creator/AssemblyFileInfo.cs | 10 +- Creator/Helper/Minimatcher.cs | 1 - Creator/Helper/SimpleCommandLineParser.cs | 167 ---------------------- Creator/KattekerCreator.csproj | 13 +- Creator/Program.Arguments.cs | 55 +++++++ Creator/Program.cs | 83 ++++------- Creator/packages.config | 2 + Katteker.Test/Katteker.Test.csproj | 1 + Katteker.Test/app.config | 11 ++ 11 files changed, 118 insertions(+), 283 deletions(-) delete mode 100644 Creator/ApplicationArguments.cs delete mode 100644 Creator/Helper/SimpleCommandLineParser.cs create mode 100644 Creator/Program.Arguments.cs create mode 100644 Katteker.Test/app.config diff --git a/Creator/App.config b/Creator/App.config index ba6b45f..b34a1ea 100644 --- a/Creator/App.config +++ b/Creator/App.config @@ -21,6 +21,10 @@ + + + + diff --git a/Creator/ApplicationArguments.cs b/Creator/ApplicationArguments.cs deleted file mode 100644 index 181a794..0000000 --- a/Creator/ApplicationArguments.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.ComponentModel; -using System.IO; - -namespace KattekerCreator -{ - public class ApplicationArguments - { - private string _changeLog; - private string _programFile; - private string _outputDir; - - [DisplayName("Program")] - [Description("Path to the program file")] - public string ProgramFile - { - get => _programFile; - set => _programFile = Path.GetFullPath(value); - } - - [DisplayName("Changelog")] - [Description("Path of the changelog file")] - public string ChangeLog - { - get => _changeLog; - set => _changeLog = Path.GetFullPath(value); - } - - [DisplayName("Channel")] - [Description("Channel of releasing.")] - public string Channel { get; set; } - - [DisplayName("Out")] - [Description("Directory for the output")] - public string OutputDir - { - get => _outputDir; - set => _outputDir = Path.GetFullPath(value); - } - - [DisplayName("Publish")] - [Description("Path for output from the point of view of the application.")] - public string PublishDir { get; set; } - - [DisplayName("Version")] - [Description("Override version number of the application.")] - public string Version { get; set; } - - [DisplayName("Filter")] - [Description("Filter parameter. Use minimatch pattern.")] - public string FilterAsString { get; set; } - - public string[] Filter => string.IsNullOrWhiteSpace(FilterAsString) ? new string[0] : FilterAsString.Split(';'); - } -} \ No newline at end of file diff --git a/Creator/AssemblyFileInfo.cs b/Creator/AssemblyFileInfo.cs index 9c826e7..b975f59 100644 --- a/Creator/AssemblyFileInfo.cs +++ b/Creator/AssemblyFileInfo.cs @@ -15,17 +15,17 @@ namespace KattekerCreator private readonly string _description; private readonly string _productName; - public AssemblyFileInfo(ApplicationArguments appArguments, string tempDir) + public AssemblyFileInfo(string programFile, string version, string tempDir) { - FileInfo = new FileInfo(appArguments.ProgramFile); + FileInfo = new FileInfo(programFile); using (var resourceInfo = new ResourceInfo()) { - resourceInfo.Load(appArguments.ProgramFile); + resourceInfo.Load(programFile); if (resourceInfo.ResourceTypes.All(x => x.ResourceType != Kernel32.ResourceTypes.RT_VERSION)) return; var versionResource = (VersionResource) resourceInfo[Kernel32.ResourceTypes.RT_VERSION].First(); var stringFileInfo = ((StringFileInfo) versionResource[nameof(StringFileInfo)]).Strings .FirstOrDefault().Value; - AssemblyIconPath = GetAssemblyIcon(appArguments.ProgramFile, tempDir); + AssemblyIconPath = GetAssemblyIcon(programFile, tempDir); if (stringFileInfo.Strings.ContainsKey("CompanyName")) _company = stringFileInfo.Strings["CompanyName"].StringValue.TrimEnd('\0'); if (stringFileInfo.Strings.ContainsKey("FileDescription")) @@ -35,7 +35,7 @@ namespace KattekerCreator if (stringFileInfo.Strings.ContainsKey("ProductName")) _productName = stringFileInfo.Strings["ProductName"].StringValue.TrimEnd('\0'); if (!stringFileInfo.Strings.ContainsKey("ProductVersion")) return; - AssemblyVersion = appArguments.Version != null ? GetSemanticVersion(appArguments.Version) : GetSemanticVersion(stringFileInfo.Strings["ProductVersion"].StringValue.TrimEnd('\0')); + AssemblyVersion = version != null ? GetSemanticVersion(version) : GetSemanticVersion(stringFileInfo.Strings["ProductVersion"].StringValue.TrimEnd('\0')); } } diff --git a/Creator/Helper/Minimatcher.cs b/Creator/Helper/Minimatcher.cs index 450a0e9..3df30fa 100644 --- a/Creator/Helper/Minimatcher.cs +++ b/Creator/Helper/Minimatcher.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Text.RegularExpressions; namespace Minimatch diff --git a/Creator/Helper/SimpleCommandLineParser.cs b/Creator/Helper/SimpleCommandLineParser.cs deleted file mode 100644 index bc9b96c..0000000 --- a/Creator/Helper/SimpleCommandLineParser.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Reflection; - -namespace KattekerCreator.Helper -{ - public class SimpleCommandLineParser where TObject : new() - { - private TObject _parsedObject; - - public SimpleCommandLineParser(IEnumerable args) - { - Parse(args); - } - - private IDictionary Arguments { get; } = new Dictionary(); - - public bool Contains(string name) => Arguments.ContainsKey(name); - - public TObject GetObject() - { - if (!EqualityComparer.Default.Equals(_parsedObject, default(TObject))) - return _parsedObject; - _parsedObject = new TObject(); - try - { - LoopProperties(pi => - { - var displayName = GetDisplayName(pi); - if (displayName == null) return; - if (!Arguments.TryGetValue(displayName.ToLowerInvariant(), out var argument)) return; - if (pi.PropertyType == typeof(bool)) - { - pi.SetValue(_parsedObject, true); - return; - } - if (pi.PropertyType.IsArray) - pi.SetValue(_parsedObject, argument); - else - pi.SetValue(_parsedObject, argument.FirstOrDefault()); - }); - } - catch (Exception e) - { - Log.WriteErrorLine("Error parsing commandline arguments: " + e.Message); - ShowHelp(); - } - return _parsedObject; - } - - public void ShowHelp(TextWriter textWriter = null) - { - if (textWriter == null) textWriter = Console.Out; - var executable = Assembly.GetExecutingAssembly().GetName().Name + ".exe"; - var assemblyTitle = ((AssemblyTitleAttribute) Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyTitleAttribute), false))?.Title ?? executable; - var assemblyDescription = ((AssemblyDescriptionAttribute) Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyDescriptionAttribute), false))?.Description; - - textWriter.WriteLine(); - textWriter.WriteLine(assemblyTitle); - textWriter.WriteLine(); - textWriter.WriteLine(assemblyDescription); - textWriter.WriteLine(); - textWriter.WriteLine(executable + " "); - textWriter.WriteLine(); - var dict = new Dictionary(); - LoopProperties(pi => - { - var displayName = GetDisplayName(pi); - if (displayName == null) return; - var description = GetDescription(pi); - if (pi.PropertyType == typeof(bool)) description = "(Switch) " + description; - dict.Add(displayName, description); - }); - var longestWord = dict.Keys.Max(x => x.Length); - foreach (var kv in dict) - { - textWriter.Write("-" + kv.Key.PadRight(longestWord + 3, ' ')); - textWriter.WriteLine(kv.Value); - } - textWriter.WriteLine(); - textWriter.WriteLine("A switch will be set to true, if it will called."); - textWriter.WriteLine($"Example: {executable} "); - textWriter.WriteLine(); - textWriter.WriteLine("A parameter will be set."); - textWriter.WriteLine($"Example: {executable} \"Value\""); - } - - /// - /// Shows the current set options. - /// - /// If not set Console.Out will be set. - public void ShowProgramArguments(TextWriter textWriter = null) - { - if (textWriter == null) textWriter = Console.Out; - LoopProperties(pi => - { - var displayName = GetDisplayName(pi); - if (displayName == null) return; - var value = pi.GetValue(_parsedObject); - textWriter.Write(displayName.PadRight(20, ' ')); - if (value == null) - { - textWriter.WriteLine(""); - } - else - { - if (value.GetType().IsArray) - { - var arrayAsString = ((IEnumerable) value) - .Cast() - .Aggregate(string.Empty, (current, x) => current + x?.ToString() + ", "); - textWriter.WriteLine(arrayAsString); - } - else - { - textWriter.WriteLine(value.ToString()); - } - } - }); - } - - private static TAttribute GetAttribute(MemberInfo memberInfo) where TAttribute : Attribute => - (TAttribute) Attribute.GetCustomAttribute(memberInfo, typeof(TAttribute)); - - private static string GetDescription(MemberInfo memberInfo) => GetAttribute(memberInfo)?.Description; - - private static string GetDisplayName(MemberInfo memberInfo) => GetAttribute(memberInfo)?.DisplayName; - - private static void LoopProperties(Action action) - { - var properties = typeof(TObject).GetProperties(BindingFlags.Public | BindingFlags.Instance); - foreach (var propertyInfo in properties) - action.Invoke(propertyInfo); - } - - private void Parse(IEnumerable args) - { - var currentName = ""; - var values = new List(); - foreach (var arg in args) - { - if (arg.StartsWith("-", StringComparison.Ordinal)) - { - if (!string.IsNullOrWhiteSpace(currentName)) - Arguments[currentName.ToLowerInvariant()] = values.ToArray(); - values.Clear(); - currentName = arg.Substring(1); - } - else if (string.IsNullOrWhiteSpace(currentName)) - { - Arguments[arg] = new string[0]; - } - else - { - values.Add(arg); - } - } - - if (currentName != "") - Arguments[currentName.ToLowerInvariant()] = values.ToArray(); - } - } -} \ No newline at end of file diff --git a/Creator/KattekerCreator.csproj b/Creator/KattekerCreator.csproj index 8fc81f6..978905b 100644 --- a/Creator/KattekerCreator.csproj +++ b/Creator/KattekerCreator.csproj @@ -38,18 +38,27 @@ 4 false + + false + + + ..\packages\McMaster.Extensions.CommandLineUtils.2.2.5\lib\net45\McMaster.Extensions.CommandLineUtils.dll + + + + ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + ..\packages\Vestris.ResourceLib.1.6.422\lib\Vestris.ResourceLib.dll - @@ -60,8 +69,8 @@ + - True diff --git a/Creator/Program.Arguments.cs b/Creator/Program.Arguments.cs new file mode 100644 index 0000000..8a40ce7 --- /dev/null +++ b/Creator/Program.Arguments.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Linq; +using McMaster.Extensions.CommandLineUtils; + +namespace KattekerCreator +{ + [Command(Name = "KattekerCreator", Description = "Creates Installation and Update packages.")] + [HelpOption("-?")] + public partial class Program + { + private string _changeLog; + private string _programFile; + private string _outputDir; + + [Argument(0, Description = "Path to the program file")] + [FileExists] + [Required] + private string ProgramFile + { + get => _programFile; + set => _programFile = Path.GetFullPath(value); + } + [Option("-L|--changelog", Description = "Path of the changelog file")] + private string ChangeLog + { + get => _changeLog; + set => _changeLog = Path.GetFullPath(value); + } + + [Option("-C|--channel", Description = "Channel of releasing")] + private string Channel { get; set; } + + [Option("-O|--output", Description = "Directory for the output")] + [Required] + private string OutputDir + { + get => _outputDir; + set => _outputDir = Path.GetFullPath(value); + } + + [Option("-P|--publish", Description = "Path for output from the point of view of the application")] + private string PublishDir { get; set; } + + [Option("-V|--version", Description = "Override version number of the application")] + private string Version { get; set; } + + [Option("-F|--filter", Description = "Filter parameter. Use minimatch pattern.")] + private string FilterAsString { get; set; } + + private IEnumerable Filter => string.IsNullOrWhiteSpace(FilterAsString) ? Enumerable.Empty() : FilterAsString.Split(';'); + + } +} \ No newline at end of file diff --git a/Creator/Program.cs b/Creator/Program.cs index 90dbecb..186a506 100644 --- a/Creator/Program.cs +++ b/Creator/Program.cs @@ -5,66 +5,41 @@ using System.IO; using System.Linq; using Katteker; using KattekerCreator.Helper; +using McMaster.Extensions.CommandLineUtils; namespace KattekerCreator { - public class Program + // ReSharper disable once ClassNeverInstantiated.Global + public partial class Program { - //private const string MakeNsis = @"C:\Program Files (x86)\NSIS\makensis.exe"; + private const string MakeNsis = @"C:\Program Files (x86)\NSIS\makensis.exe"; private readonly string _baseDirectory = AppDomain.CurrentDomain.BaseDirectory; - private string MakeNsis => Path.Combine(_baseDirectory, "NSIS", "makensis.exe"); - private ApplicationArguments _appArguments; + //private string MakeNsis => Path.Combine(_baseDirectory, "NSIS", "makensis.exe"); private AssemblyFileInfo _assemblyFileInfo; private TemporaryDirectory _tempDir; private Releases _releases; - private static int Main(string[] args) + private static int Main(string[] args) => CommandLineApplication.Execute(args); + + // ReSharper disable once UnusedMember.Local + private int OnExecute(CommandLineApplication app) { - var program = new Program(); - try - { - return program.Run(args); - } - catch (Exception ex) - { - Log.WriteErrorLine(ex.Message); - if (ex.InnerException != null) - Log.WriteErrorLine(ex.InnerException.Message); -#if DEBUG - throw; -#else - return -1; -#endif - } - } - - private int Run(string[] args) - { - //Parse App-Arguments - var parser = new SimpleCommandLineParser(args); - - if (args.Length == 0) - { - parser.ShowHelp(); - return 1924; - } - - _appArguments = parser.GetObject(); //Create tempdir using (_tempDir = Utils.CreateTempDirectory()) { - _releases = new Releases(_appArguments.OutputDir); - parser.ShowProgramArguments(); + _releases = new Releases(OutputDir); + + // TODO parser.ShowProgramArguments(); //Modify AppStub var appStubFile = ModifyAppStub(); //Acquire infos from Executable. - _assemblyFileInfo = new AssemblyFileInfo(_appArguments, _tempDir.Path); + _assemblyFileInfo = new AssemblyFileInfo(ProgramFile, Version, _tempDir.Path); var configFile = CreateConfigFile(); //Generate NSIS-Script var additionalFiles = new[] { - new PhysicalFile(appStubFile, Path.GetFileName(_appArguments.ProgramFile)), + new PhysicalFile(appStubFile, Path.GetFileName(ProgramFile)), new PhysicalFile(configFile, Path.Combine($"app-{_assemblyFileInfo.AssemblyVersion}", Path.GetFileName(configFile) ?? string.Empty)) }; var templateFile = GenerateNsisTemplate(additionalFiles); @@ -83,7 +58,7 @@ namespace KattekerCreator private void CopyAsSetup(string setupFilePath, ReleaseEntry releaseEntry) { if (!_releases.IsLatestEntry(releaseEntry)) return; - var target = Path.Combine(_appArguments.OutputDir, Constants.SetupFile); + var target = Path.Combine(OutputDir, Constants.SetupFile); File.Delete(target); File.Copy(setupFilePath, target); } @@ -109,18 +84,18 @@ namespace KattekerCreator var setupFile = Path.GetFileName(setupFilePath); if (string.IsNullOrEmpty(setupFile)) throw new ArgumentException(); if (!File.Exists(setupFilePath)) throw new FileNotFoundException(setupFile); - if (!Directory.Exists(_appArguments.OutputDir)) Directory.CreateDirectory(_appArguments.OutputDir); - if (!string.IsNullOrEmpty(_appArguments.ChangeLog)) + if (!Directory.Exists(OutputDir)) Directory.CreateDirectory(OutputDir); + if (!string.IsNullOrEmpty(ChangeLog)) { - var changeLogFilename = Path.GetFileName(_appArguments.ChangeLog); + var changeLogFilename = Path.GetFileName(ChangeLog); - if (!File.Exists(_appArguments.ChangeLog)) throw new FileNotFoundException(_appArguments.ChangeLog); - File.Copy(_appArguments.ChangeLog, Path.Combine(_appArguments.OutputDir, changeLogFilename), true); + if (!File.Exists(ChangeLog)) throw new FileNotFoundException(ChangeLog); + File.Copy(ChangeLog, Path.Combine(OutputDir, changeLogFilename), true); } - File.Copy(setupFilePath, Path.Combine(_appArguments.OutputDir, setupFile), true); + File.Copy(setupFilePath, Path.Combine(OutputDir, setupFile), true); } - private string CompileSetupScript(string templateFile) + private static string CompileSetupScript(string templateFile) { if (!File.Exists(MakeNsis)) throw new FileNotFoundException("NSIS not installed properly."); int exitcode; @@ -148,9 +123,9 @@ namespace KattekerCreator var pathToFile = Path.Combine(_tempDir.Path, Constants.KattekerConfig); var kattekerConfig = new KattekerConfig { - Publish = _appArguments.PublishDir ?? _appArguments.OutputDir, - Changelog = Path.GetFileName(_appArguments.ChangeLog), - Channel = _appArguments.Channel + Publish = PublishDir ?? OutputDir, + Changelog = Path.GetFileName(ChangeLog), + Channel = Channel }; kattekerConfig.WriteToFile(pathToFile); return pathToFile; @@ -160,7 +135,7 @@ namespace KattekerCreator { var baseFile = new FileInfo(Path.Combine(_baseDirectory, Constants.ExecutionStub)); var targetFile = baseFile.CopyTo(Path.Combine(_tempDir.Path, Constants.ExecutionStub)); - Utils.CopyResources(_appArguments.ProgramFile, targetFile.FullName); + Utils.CopyResources(ProgramFile, targetFile.FullName); return targetFile.FullName; } @@ -183,11 +158,11 @@ namespace KattekerCreator UserInterface = Path.Combine(_baseDirectory, "contrib", "LoadingBar_Icon.exe"), UninstallIcon = Path.Combine(_baseDirectory, "contrib", "uninstall.ico"), OutFile = Path.GetFileName(Path.ChangeExtension(outFile, "exe")), - ReleaseChannel = _appArguments.Channel + ReleaseChannel = Channel }; - var path = Path.GetDirectoryName(_appArguments.ProgramFile) ?? string.Empty; + var path = Path.GetDirectoryName(ProgramFile) ?? string.Empty; setupTmpl.Files.AddRange(additionalFiles); - var files = Utils.EnumerateFiles(path, _appArguments.Filter).ToArray(); + var files = Utils.EnumerateFiles(path, Filter).ToArray(); setupTmpl.InstallSize = (long) Math.Floor(files.Sum(x => x.Length) / 1024f); setupTmpl.Files.AddRange(files.Select(x => new PhysicalFile(x.FullName, x.FullName.Replace(path, $"app-{_assemblyFileInfo.AssemblyVersion}")))); var transformText = setupTmpl.TransformText(); diff --git a/Creator/packages.config b/Creator/packages.config index 70a616c..47b12ae 100644 --- a/Creator/packages.config +++ b/Creator/packages.config @@ -1,4 +1,6 @@  + + \ No newline at end of file diff --git a/Katteker.Test/Katteker.Test.csproj b/Katteker.Test/Katteker.Test.csproj index 35f932b..f105306 100644 --- a/Katteker.Test/Katteker.Test.csproj +++ b/Katteker.Test/Katteker.Test.csproj @@ -111,6 +111,7 @@ + PreserveNewest diff --git a/Katteker.Test/app.config b/Katteker.Test/app.config new file mode 100644 index 0000000..557095a --- /dev/null +++ b/Katteker.Test/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file