using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using Katteker; using KattekerCreator.Helper; namespace KattekerCreator { public class Program { private const string Executable = @"C:\Program Files (x86)\NSIS\makensis.exe"; private readonly string _baseDirectory = AppDomain.CurrentDomain.BaseDirectory; private ApplicationArguments _appArguments; private AssemblyFileInfo _assemblyFileInfo; private string _tempDir; private Releases _releases; private static int Main(string[] args) { 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 _tempDir = Utils.CreateTempDirectory(); _releases = new Releases(_appArguments.OutputDir); parser.ShowProgramArguments(); //Modify AppStub var appStubFile = ModifyAppStub(); //Acquire infos from Executable. _assemblyFileInfo = new AssemblyFileInfo(_appArguments, _tempDir); var configFile = CreateConfigFile(); //Generate NSIS-Script var additionalFiles = new[] { new PhysicalFile(appStubFile, Path.GetFileName(_appArguments.ProgramFile)), new PhysicalFile(configFile, Path.Combine($"app-{_assemblyFileInfo.AssemblyVersion}", Path.GetFileName(configFile))) }; var templateFile = GenerateNsisTemplate(additionalFiles); //Start makensis.exe var setupFilePath = CompileSetupScript(templateFile); //Copy to Output-Folder CopyToOutputFolder(setupFilePath); //Create/Modify RELEASE File var releaseEntry = AddPackageToReleaseFile(setupFilePath); //Copy installer as setup.exe CopyAsSetup(setupFilePath, releaseEntry); return 0; } private void CopyAsSetup(string setupFilePath, ReleaseEntry releaseEntry) { if (!_releases.IsLatestEntry(releaseEntry)) return; var target = Path.Combine(_appArguments.OutputDir, Constants.SetupFile); File.Delete(target); File.Copy(setupFilePath, target); } private ReleaseEntry AddPackageToReleaseFile(string setupFilePath) { try { var result = _releases.Add(setupFilePath, _assemblyFileInfo.AssemblyVersion); _releases.WriteReleaseFile(); return result; } catch (Exception e) { Log.WriteErrorLine(e.Message); throw; } } private void CopyToOutputFolder(string setupFilePath) { if (setupFilePath == null) throw new ArgumentNullException(nameof(setupFilePath)); 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)) { var changeLogFilename = Path.GetFileName(_appArguments.ChangeLog); if (!File.Exists(_appArguments.ChangeLog)) throw new FileNotFoundException(_appArguments.ChangeLog); File.Copy(_appArguments.ChangeLog, Path.Combine(_appArguments.OutputDir, changeLogFilename), true); } File.Copy(setupFilePath, Path.Combine(_appArguments.OutputDir, setupFile), true); } private static string CompileSetupScript(string templateFile) { if (!File.Exists(Executable)) throw new FileNotFoundException("NSIS not installed properly."); int exitcode; using (var p = new Process()) { p.StartInfo = new ProcessStartInfo { FileName = Executable, Arguments = $"\"{templateFile}\"", UseShellExecute = false }; var sw = Stopwatch.StartNew(); p.Start(); p.WaitForExit(); exitcode = p.ExitCode; Log.WriteInfoLine($"{Path.GetFileName(Executable)} has exited with Exitcode: {exitcode} (Took: {sw.ElapsedMilliseconds}ms)"); } if (exitcode != 0) throw new Exception($"{Path.GetFileName(Executable)} has exited with Exitcode: {exitcode}"); return Path.ChangeExtension(templateFile, "exe"); } private string CreateConfigFile() { var pathToFile = Path.Combine(_tempDir, Constants.KattekerConfig); var kattekerConfig = new KattekerConfig { Publish = _appArguments.PublishDir ?? _appArguments.OutputDir, Changelog = Path.GetFileName(_appArguments.ChangeLog) }; kattekerConfig.WriteToFile(pathToFile); return pathToFile; } private string ModifyAppStub() { var baseFile = new FileInfo(Path.Combine(_baseDirectory, Constants.ExecutionStub)); var targetFile = baseFile.CopyTo(Path.Combine(_tempDir, Constants.ExecutionStub)); Utils.CopyResources(_appArguments.ProgramFile, targetFile.FullName); return targetFile.FullName; } private static string GenerateFilename(string name, string version, string extension, bool isDelta = false) => $"{name}-{version}-{(isDelta ? "delta" : "full")}.{extension}"; private string GenerateNsisTemplate(IEnumerable additionalFiles) { try { var outFile = Path.Combine(_tempDir, GenerateFilename(_assemblyFileInfo.ProductName, _assemblyFileInfo.AssemblyVersion.ToString(), "nsi")); var setupTmpl = new SetupTmpl { Executable = _assemblyFileInfo.FileInfo.Name, AppName = _assemblyFileInfo.ProductName, CompanyName = _assemblyFileInfo.Company, Description = _assemblyFileInfo.Description, IconPath = _assemblyFileInfo.AssemblyIconPath, Version = _assemblyFileInfo.AssemblyVersion, HelpUrl = "http://example.org", //Not used at the moment. 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 }; var path = Path.GetDirectoryName(_appArguments.ProgramFile) ?? string.Empty; setupTmpl.Files.AddRange(additionalFiles); var files = Utils.EnumerateFiles(_appArguments.ProgramFile).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(); File.WriteAllText(outFile, transformText); return outFile; } catch (Exception e) { Log.WriteErrorLine(e.Message); throw; } } } }