diff --git a/FLExBridge.sln b/FLExBridge.sln index 78a2e43e..aef1ac96 100644 --- a/FLExBridge.sln +++ b/FLExBridge.sln @@ -52,6 +52,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LfMergeBridgeTestApp", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LfMergeBridgeTests", "src\LfMergeBridgeTests\LfMergeBridgeTests.csproj", "{6CB1246D-956A-4759-AA13-D434CBB383FE}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MkFwData", "src\MkFwData\MkFwData.csproj", "{5CDB086A-79DE-4EF4-BB48-4AEAEEB0827B}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiftFileCheckerApp", "src\LiftFileCheckerApp\LiftFileCheckerApp.csproj", "{30AA046B-5E14-408C-89EF-8601BB27FB32}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibTriboroughBridge-ChorusPluginTests", "src\LibTriboroughBridge-ChorusPluginTests\LibTriboroughBridge-ChorusPluginTests.csproj", "{AA6CC4E2-6FD8-4B30-99EC-A446E9CAA176}" @@ -124,6 +126,10 @@ Global {6CB1246D-956A-4759-AA13-D434CBB383FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {6CB1246D-956A-4759-AA13-D434CBB383FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {6CB1246D-956A-4759-AA13-D434CBB383FE}.Release|Any CPU.Build.0 = Release|Any CPU + {5CDB086A-79DE-4EF4-BB48-4AEAEEB0827B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5CDB086A-79DE-4EF4-BB48-4AEAEEB0827B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CDB086A-79DE-4EF4-BB48-4AEAEEB0827B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5CDB086A-79DE-4EF4-BB48-4AEAEEB0827B}.Release|Any CPU.Build.0 = Release|Any CPU {30AA046B-5E14-408C-89EF-8601BB27FB32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {30AA046B-5E14-408C-89EF-8601BB27FB32}.Debug|Any CPU.Build.0 = Debug|Any CPU {30AA046B-5E14-408C-89EF-8601BB27FB32}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/MkFwData/MkFwData.csproj b/src/MkFwData/MkFwData.csproj new file mode 100644 index 00000000..580f131a --- /dev/null +++ b/src/MkFwData/MkFwData.csproj @@ -0,0 +1,30 @@ + + + + + fwdata + Exe + net8.0 + net8.0 + enable + enable + true + $(MSBuildProjectDirectory) + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/MkFwData/Program.cs b/src/MkFwData/Program.cs new file mode 100644 index 00000000..419e076b --- /dev/null +++ b/src/MkFwData/Program.cs @@ -0,0 +1,151 @@ +using Chorus.VcsDrivers.Mercurial; +using SIL.Progress; +using System.CommandLine; + +class Program +{ + static async Task Main(string[] args) + { + var rootCommand = new RootCommand("Make or split .fwdata file"); + + var verboseOption = new Option( + ["--verbose", "-v"], + "Display verbose output" + ); + rootCommand.AddGlobalOption(verboseOption); + + var quietOption = new Option( + ["--quiet", "-q"], + "Suppress all output (overrides --verbose if present)" + ); + rootCommand.AddGlobalOption(quietOption); + + var splitCommand = new Command("split", "Split .fwdata file (push Humpty off the wall)"); + var buildCommand = new Command("build", "Rebuild .fwdata file (put Humpty together again)"); + + rootCommand.Add(splitCommand); + rootCommand.Add(buildCommand); + + var filename = new Argument( + "file", + "Name of .fwdata file to create or split, or directory to create/split it in" + ); + splitCommand.Add(filename); + buildCommand.Add(filename); + + var hgRevOption = new Option( + ["--rev", "-r"], + "Revision to check out (default \"tip\")" + ); + hgRevOption.SetDefaultValue("tip"); + buildCommand.AddGlobalOption(hgRevOption); + + var timeoutOption = new Option( + ["--timeout", "-t"], + "Timeout in seconds for Hg commands (default 600)" + ); + timeoutOption.SetDefaultValue(600); + buildCommand.AddGlobalOption(timeoutOption); + + var cleanupOption = new Option( + ["--cleanup", "-c"], + "Clean repository after creating .fwdata file (CAUTION: deletes every other file except .fwdata)" + ); + buildCommand.Add(cleanupOption); + + buildCommand.SetHandler(BuildFwData, filename, verboseOption, quietOption, hgRevOption, timeoutOption, cleanupOption); + + var cleanupOptionForSplit = new Option( + ["--cleanup", "-c"], + "Delete .fwdata file after splitting" + ); + splitCommand.Add(cleanupOptionForSplit); + + splitCommand.SetHandler(SplitFwData, filename, verboseOption, quietOption, cleanupOptionForSplit); + + return await rootCommand.InvokeAsync(args); + } + + static FileInfo? MaybeLocateFwDataFile(string input) + { + if (Directory.Exists(input)) { + var dirInfo = new DirectoryInfo(input); + var fname = dirInfo.Name + ".fwdata"; + return new FileInfo(Path.Join(input, fname)); + } else if (File.Exists(input)) { + return new FileInfo(input); + } else if (File.Exists(input + ".fwdata")) { + return new FileInfo(input + ".fwdata"); + } else { + return null; + } + } + + static FileInfo LocateFwDataFile(string input) + { + var result = MaybeLocateFwDataFile(input); + if (result != null) return result; + if (input.EndsWith(".fwdata")) return new FileInfo(input); + return new FileInfo(input + ".fwdata"); + } + + static Task SplitFwData(string filename, bool verbose, bool quiet, bool cleanup) + { + IProgress progress = quiet ? new NullProgress() : new ConsoleProgress(); + progress.ShowVerbose = verbose; + var file = MaybeLocateFwDataFile(filename); + if (file == null || !file.Exists) { + progress.WriteError("Could not find {0}", filename); + return Task.FromResult(1); + } + string name = file.FullName; + progress.WriteVerbose("Splitting {0} ...", name); + LfMergeBridge.LfMergeBridge.DisassembleFwdataFile(progress, writeVerbose: true, name); + progress.WriteMessage("Finished splitting {0}", name); + if (cleanup) + { + progress.WriteVerbose("Cleaning up..."); + var fwdataFile = new FileInfo(name); + if (fwdataFile.Exists) { + fwdataFile.Delete(); + progress.WriteVerbose("Deleted {0}", fwdataFile.FullName); + } else { + progress.WriteVerbose("File not found, so not deleting: {0}", fwdataFile.FullName); + } + } + return Task.FromResult(0); + } + + static Task BuildFwData(string filename, bool verbose, bool quiet, string rev, int timeout, bool cleanup) + { + IProgress progress = quiet ? new NullProgress() : new ConsoleProgress(); + progress.ShowVerbose = verbose; + var file = LocateFwDataFile(filename); + if (file.Exists) { + progress.WriteWarning("File {0} already exists and will be overwritten", file.FullName); + } + var dir = file.Directory; + if (dir == null || !dir.Exists) { + progress.WriteError("Could not find directory {0}. MkFwData needs a Mercurial repo to work with.", dir?.FullName ?? "(null)"); + return Task.FromResult(1); + } + string name = file.FullName; + progress.WriteMessage("Checking out {0}", rev); + var result = HgRunner.Run($"hg checkout {rev}", dir.FullName, timeout, progress); + if (result.ExitCode != 0) + { + progress.WriteMessage("Could not find Mercurial repo in directory {0}. MkFwData needs a Mercurial repo to work with.", dir.FullName ?? "(null)"); + return Task.FromResult(result.ExitCode); + } + progress.WriteVerbose("Creating {0} ...", name); + LfMergeBridge.LfMergeBridge.ReassembleFwdataFile(progress, writeVerbose: true, name); + progress.WriteMessage("Created {0}", name); + if (cleanup) + { + progress.WriteVerbose("Cleaning up..."); + HgRunner.Run($"hg checkout null", dir.FullName, timeout, progress); + HgRunner.Run($"hg purge --no-confirm --exclude *.fwdata --exclude hgRunner.log", dir.FullName, timeout, progress); + } + return Task.FromResult(0); + } +}