From 32cd0e73ed72c01f3f72e35513dc11e3ff7d158b Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 23 Sep 2025 14:15:27 -0400 Subject: [PATCH] Add hashing tool implementation, for fun --- .github/workflows/build_and_test.yml | 2 +- .vscode/launch.json | 28 ++ Hasher/Hasher.csproj | 34 ++ Hasher/Options.cs | 168 +++++++++ Hasher/Program.cs | 134 +++++++ README.MD | 19 + SabreTools.Hashing.sln | 40 +- SabreTools.Hashing/ExtensionAttribute.cs | 9 + SabreTools.Hashing/Extensions.cs | 441 +++++++++++++++++++++++ publish-nix.sh | 127 ++++++- publish-win.ps1 | 114 +++++- 11 files changed, 1103 insertions(+), 13 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 Hasher/Hasher.csproj create mode 100644 Hasher/Options.cs create mode 100644 Hasher/Program.cs create mode 100644 SabreTools.Hashing/ExtensionAttribute.cs create mode 100644 SabreTools.Hashing/Extensions.cs diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 13f7e64..7c65946 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -31,7 +31,7 @@ jobs: uses: ncipollo/release-action@v1.14.0 with: allowUpdates: True - artifacts: "*.nupkg,*.snupkg" + artifacts: "*.nupkg,*.snupkg,*.zip" body: 'Last built commit: ${{ github.sha }}' name: 'Rolling Release' prerelease: True diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..29bcc25 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/Hasher/bin/Debug/net9.0/Hasher.dll", + "args": [], + "cwd": "${workspaceFolder}", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false, + "justMyCode": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/Hasher/Hasher.csproj b/Hasher/Hasher.csproj new file mode 100644 index 0000000..036daf5 --- /dev/null +++ b/Hasher/Hasher.csproj @@ -0,0 +1,34 @@ + + + + net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0 + Exe + false + true + false + latest + enable + true + true + 1.5.0 + + + + + win-x86;win-x64 + + + win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64 + + + win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64 + + + net6.0;net7.0;net8.0;net9.0 + + + + + + + \ No newline at end of file diff --git a/Hasher/Options.cs b/Hasher/Options.cs new file mode 100644 index 0000000..995eef7 --- /dev/null +++ b/Hasher/Options.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using SabreTools.Hashing; + +namespace Hasher +{ + /// + /// Set of options for the test executable + /// + /// TODO: Add file output + internal sealed class Options + { + #region Properties + + /// + /// Enable debug output for relevant operations + /// + public bool Debug { get; private set; } = false; + + /// + /// List of all hash types to process + /// + public List HashTypes { get; private set; } = []; + + /// + /// Set of input paths to use for operations + /// + public List InputPaths { get; private set; } = []; + + /// + /// Print all available hashes and then quit + /// + /// Ignores all other flags if found + public bool PrintAvailableHashes { get; private set; } = false; + + #endregion + + #region Instance Variables + + /// + /// Special flag to enable all hash types + /// + /// Skips adding hash types specified otherwise + private bool _allHashTypesEnabled = false; + + #endregion + + /// + /// Parse commandline arguments into an Options object + /// + public static Options? ParseOptions(string[] args) + { + // If we have invalid arguments + if (args == null || args.Length == 0) + return null; + + // Create an Options object + var options = new Options(); + + // Parse the features + int index = 0; + for (; index < args.Length; index++) + { + string arg = args[index]; + bool featureFound = false; + switch (arg) + { + case "-?": + case "-h": + case "--help": + return null; + + default: + break; + } + + // If the flag wasn't a feature + if (!featureFound) + break; + } + + // Parse the options and paths + for (; index < args.Length; index++) + { + string arg = args[index]; + switch (arg) + { + case "-d": + case "--debug": + options.Debug = true; + break; + + case "-l": + case "--list": + options.PrintAvailableHashes = true; + break; + + case "-t": + case "--type": + string value = index + 1 < args.Length ? args[++index] : string.Empty; + if (value.Equals("all", StringComparison.OrdinalIgnoreCase)) + { + options._allHashTypesEnabled = true; + break; + } + + if (!options._allHashTypesEnabled) + { + HashType? hashType = value.GetHashType(); + if (hashType != null && !options.HashTypes.Contains(hashType.Value)) + options.HashTypes.Add(item: hashType.Value); + } + + break; + + default: + options.InputPaths.Add(arg); + break; + } + } + + // Validate we have any input paths to work on + if (options.InputPaths.Count == 0) + { + Console.WriteLine("At least one path is required!"); + return null; + } + + // If the all hashes flag was enabled + if (options._allHashTypesEnabled) + { + options.HashTypes = [.. (HashType[])Enum.GetValues(typeof(HashType))]; + } + + // If no hash types are provided, set defaults + if (options.HashTypes.Count == 0) + { + options.HashTypes.Add(HashType.CRC32); + options.HashTypes.Add(HashType.MD5); + options.HashTypes.Add(HashType.SHA1); + options.HashTypes.Add(HashType.SHA256); + } + + return options; + } + + /// + /// Display help text + /// + public static void DisplayHelp() + { + Console.WriteLine("File Hashing Program"); + Console.WriteLine(); + Console.WriteLine("Hasher file|directory ..."); + Console.WriteLine(); + Console.WriteLine("Options:"); + Console.WriteLine("-?, -h, --help Display this help text and quit"); + Console.WriteLine("-d, --debug Enable debug mode"); + Console.WriteLine("-l, --list List all available hashes and quit"); + Console.WriteLine("-t, --type [TYPE] Output file hashes"); + Console.WriteLine(); + Console.WriteLine("If no hash types are provided, this tool will default"); + Console.WriteLine("to outputting CRC-32, MD5, SHA-1, and SHA-256."); + Console.WriteLine("Optionally, all supported hashes can be output"); + Console.WriteLine("by specifying a value of 'all'."); + } + } +} diff --git a/Hasher/Program.cs b/Hasher/Program.cs new file mode 100644 index 0000000..e911b9f --- /dev/null +++ b/Hasher/Program.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Text; +using SabreTools.Hashing; + +namespace Hasher +{ + public static class Program + { + public static void Main(string[] args) + { + // Get the options from the arguments + var options = Options.ParseOptions(args); + + // If we have an invalid state + if (options == null) + { + Options.DisplayHelp(); + return; + } + + // If a printing option was defined + if (options.PrintAvailableHashes) + { + PrintAvailableHashes(); + return; + } + + // Loop through the input paths + foreach (string inputPath in options.InputPaths) + { + PrintPathHashes(inputPath, options); + } + } + + /// + /// Print all available hashes along with their short names + /// + /// TODO: Print all supported variants of names? + private static void PrintAvailableHashes() + { + Console.WriteLine("Hash Name Parameter Name "); + Console.WriteLine("--------------------------------------------------------------"); + + var hashTypes = (HashType[])Enum.GetValues(typeof(HashType)); + foreach (var hashType in hashTypes) + { + // Derive the parameter name + string paramName = $"{hashType}"; + paramName = paramName.Replace("-", string.Empty); + paramName = paramName.Replace(" ", string.Empty); + paramName = paramName.Replace("/", "_"); + paramName = paramName.Replace("\\", "_"); + paramName = paramName.ToLowerInvariant(); + + Console.WriteLine($"{hashType.GetHashName()?.PadRight(39, ' ')} {paramName}"); + } + } + + /// + /// Wrapper to print hashes for a single path + /// + /// File or directory path + /// User-defined options + private static void PrintPathHashes(string path, Options options) + { + Console.WriteLine($"Checking possible path: {path}"); + + // Check if the file or directory exists + if (File.Exists(path)) + { + PrintFileHashes(path, options); + } + else if (Directory.Exists(path)) + { + foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories)) + { + PrintFileHashes(file, options); + } + } + else + { + Console.WriteLine($"{path} does not exist, skipping..."); + } + } + + /// + /// Print information for a single file, if possible + /// + /// File path + /// User-defined options + private static void PrintFileHashes(string file, Options options) + { + Console.WriteLine($"Attempting to hash {file}, this may take a while..."); + Console.WriteLine(); + + // If the file doesn't exist + if (!File.Exists(file)) + { + Console.WriteLine($"{file} does not exist, skipping..."); + return; + } + + try + { + // Get all file hashes for flexibility + var hashes = HashTool.GetFileHashes(file); + if (hashes == null) + { + if (options.Debug) Console.WriteLine($"Hashes for {file} could not be retrieved"); + return; + } + + // Output subset of available hashes + var builder = new StringBuilder(); + foreach (HashType hashType in options.HashTypes) + { + // TODO: Make helper to pretty-print hash type names + if (hashes.TryGetValue(hashType, out string? hash) && hash != null) + builder.AppendLine($"{hashType}: {hash}"); + } + + // Create and print the output data + string hashData = builder.ToString(); + Console.WriteLine(hashData); + } + catch (Exception ex) + { + Console.WriteLine(options.Debug ? ex : "[Exception opening file, please try again]"); + return; + } + } + } +} diff --git a/README.MD b/README.MD index 047cce5..9079695 100644 --- a/README.MD +++ b/README.MD @@ -39,3 +39,22 @@ External implementations of hash and checksum types may not be compatible with a | [System.Security.Cryptography](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography) | MD5, SHA-1, SHA-256, SHA-384, SHA-512, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256 | Built-in library; SHA3-256, SHA3-384, SHA3-512, SHAKE128, and SHAKE256 are `net8.0` and above only for [supported platforms](https://learn.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography) | **Note:** If all you care about is performance, I encourage you to forego this library and use the ones listed above directly instead. + +## Hasher + +**Hasher** is a reference implementation for hashing and checksumming features of the library, packaged as a standalone executable for all supported platforms. It will attempt to select the correct hashing types based on input and then print the calculated values to standard output. + +``` +Hasher file|directory ... + +Options: +-?, -h, --help Display this help text and quit +-d, --debug Enable debug mode +-l, --list List all available hashes and quit +-t, --type [TYPE] Output file hashes + +If no hash types are provided, this tool will default +to outputting CRC-32, MD5, SHA-1, and SHA-256. +Optionally, all supported hashes can be output +by specifying a value of 'all'. +``` diff --git a/SabreTools.Hashing.sln b/SabreTools.Hashing.sln index b5fcb40..986a877 100644 --- a/SabreTools.Hashing.sln +++ b/SabreTools.Hashing.sln @@ -7,22 +7,56 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Hashing", "Sabre EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Hashing.Test", "SabreTools.Hashing.Test\SabreTools.Hashing.Test.csproj", "{A2BCBFDE-685B-4817-B724-050A99E02601}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hasher", "Hasher\Hasher.csproj", "{5DAC74F2-22AB-409B-B828-2FD3851586A3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Debug|x64.ActiveCfg = Debug|Any CPU + {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Debug|x64.Build.0 = Debug|Any CPU + {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Debug|x86.ActiveCfg = Debug|Any CPU + {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Debug|x86.Build.0 = Debug|Any CPU {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Release|Any CPU.Build.0 = Release|Any CPU + {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Release|x64.ActiveCfg = Release|Any CPU + {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Release|x64.Build.0 = Release|Any CPU + {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Release|x86.ActiveCfg = Release|Any CPU + {F7E34528-080E-4E60-B9D1-8ADF70A24BB0}.Release|x86.Build.0 = Release|Any CPU {A2BCBFDE-685B-4817-B724-050A99E02601}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2BCBFDE-685B-4817-B724-050A99E02601}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2BCBFDE-685B-4817-B724-050A99E02601}.Debug|x64.ActiveCfg = Debug|Any CPU + {A2BCBFDE-685B-4817-B724-050A99E02601}.Debug|x64.Build.0 = Debug|Any CPU + {A2BCBFDE-685B-4817-B724-050A99E02601}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2BCBFDE-685B-4817-B724-050A99E02601}.Debug|x86.Build.0 = Debug|Any CPU {A2BCBFDE-685B-4817-B724-050A99E02601}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2BCBFDE-685B-4817-B724-050A99E02601}.Release|Any CPU.Build.0 = Release|Any CPU + {A2BCBFDE-685B-4817-B724-050A99E02601}.Release|x64.ActiveCfg = Release|Any CPU + {A2BCBFDE-685B-4817-B724-050A99E02601}.Release|x64.Build.0 = Release|Any CPU + {A2BCBFDE-685B-4817-B724-050A99E02601}.Release|x86.ActiveCfg = Release|Any CPU + {A2BCBFDE-685B-4817-B724-050A99E02601}.Release|x86.Build.0 = Release|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Debug|x64.ActiveCfg = Debug|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Debug|x64.Build.0 = Debug|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Debug|x86.ActiveCfg = Debug|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Debug|x86.Build.0 = Debug|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Release|Any CPU.Build.0 = Release|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Release|x64.ActiveCfg = Release|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Release|x64.Build.0 = Release|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Release|x86.ActiveCfg = Release|Any CPU + {5DAC74F2-22AB-409B-B828-2FD3851586A3}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection EndGlobal diff --git a/SabreTools.Hashing/ExtensionAttribute.cs b/SabreTools.Hashing/ExtensionAttribute.cs new file mode 100644 index 0000000..b40aaca --- /dev/null +++ b/SabreTools.Hashing/ExtensionAttribute.cs @@ -0,0 +1,9 @@ +#if NET20 + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] + internal sealed class ExtensionAttribute : Attribute {} +} + +#endif diff --git a/SabreTools.Hashing/Extensions.cs b/SabreTools.Hashing/Extensions.cs new file mode 100644 index 0000000..9477f01 --- /dev/null +++ b/SabreTools.Hashing/Extensions.cs @@ -0,0 +1,441 @@ +namespace SabreTools.Hashing +{ + public static class Extensions + { + /// + /// Get the name of a given hash type, if possible + /// + /// TODO: This should be automated instead of hardcoded + public static string? GetHashName(this HashType hashType) + { + return hashType switch + { + HashType.Adler32 => "Mark Adler's 32-bit checksum", + +#if NET7_0_OR_GREATER + HashType.BLAKE3 => "BLAKE3 512-bit digest", +#endif + + HashType.CRC1_ZERO => "CRC-1/ZERO [Parity bit with 0 start]", + HashType.CRC1_ONE => "CRC-1/ONE [Parity bit with 1 start]", + + HashType.CRC3_GSM => "CRC-3/GSM", + HashType.CRC3_ROHC => "CRC-3/ROHC", + + HashType.CRC4_G704 => "CRC-4/G-704 [CRC-4/ITU]", + HashType.CRC4_INTERLAKEN => "CRC-4/INTERLAKEN", + + HashType.CRC5_EPCC1G2 => "CRC-5/EPC-C1G2 [CRC-5/EPC]", + HashType.CRC5_G704 => "CRC-5/G-704 [CRC-5/ITU]", + HashType.CRC5_USB => "CRC-5/USB", + + HashType.CRC6_CDMA2000A => "CRC-6/CDMA2000-A", + HashType.CRC6_CDMA2000B => "CRC-6/CDMA2000-B", + HashType.CRC6_DARC => "CRC-6/DARC", + HashType.CRC6_G704 => "CRC-6/G-704 [CRC-6/ITU]", + HashType.CRC6_GSM => "CRC-6/GSM", + + HashType.CRC7_MMC => "CRC-7/MMC [CRC-7]", + HashType.CRC7_ROHC => "CRC-7/ROHC", + HashType.CRC7_UMTS => "CRC-7/UMTS", + + HashType.CRC8 => "CRC-8", + HashType.CRC8_AUTOSAR => "CRC-8/AUTOSAR", + HashType.CRC8_BLUETOOTH => "CRC-8/BLUETOOTH", + HashType.CRC8_CDMA2000 => "CRC-8/CDMA2000", + HashType.CRC8_DARC => "CRC-8/DARC", + HashType.CRC8_DVBS2 => "CRC-8/DVB-S2", + HashType.CRC8_GSMA => "CRC-8/GSM-A", + HashType.CRC8_GSMB => "CRC-8/GSM-B", + HashType.CRC8_HITAG => "CRC-8/HITAG", + HashType.CRC8_I4321 => "CRC-8/I-432-1 [CRC-8/ITU]", + HashType.CRC8_ICODE => "CRC-8/I-CODE", + HashType.CRC8_LTE => "CRC-8/LTE", + HashType.CRC8_MAXIMDOW => "CRC-8/MAXIM-DOW [CRC-8/MAXIM, DOW-CRC]", + HashType.CRC8_MIFAREMAD => "CRC-8/MIFARE-MAD", + HashType.CRC8_NRSC5 => "CRC-8/NRSC-5", + HashType.CRC8_OPENSAFETY => "CRC-8/OPENSAFETY", + HashType.CRC8_ROHC => "CRC-8/ROHC", + HashType.CRC8_SAEJ1850 => "CRC-8/SAE-J1850", + HashType.CRC8_SMBUS => "CRC-8/SMBUS [CRC-8]", + HashType.CRC8_TECH3250 => "CRC-8/TECH-3250 [CRC-8/AES, CRC-8/EBU]", + HashType.CRC8_WCDMA => "CRC-8/WCDMA", + + HashType.CRC10_ATM => "CRC-10/ATM [CRC-10, CRC-10/I-610]", + HashType.CRC10_CDMA2000 => "CRC-10/CDMA2000", + HashType.CRC10_GSM => "CRC-10/GSM", + + HashType.CRC11_FLEXRAY => "CRC-11/FLEXRAY [CRC-11]", + HashType.CRC11_UMTS => "CRC-11/UMTS", + + HashType.CRC12_CDMA2000 => "CRC-12/CDMA2000", + HashType.CRC12_DECT => "CRC-12/DECT [X-CRC-12]", + HashType.CRC12_GSM => "CRC-12/GSM", + HashType.CRC12_UMTS => "CRC-12/UMTS [CRC-12/3GPP]", + + HashType.CRC13_BBC => "CRC-13/BBC", + + HashType.CRC14_DARC => "CRC-14/DARC", + HashType.CRC14_GSM => "CRC-14/GSM", + + HashType.CRC15_CAN => "CRC-15/CAN [CRC-15]", + HashType.CRC15_MPT1327 => "CRC-15/MPT1327", + + HashType.CRC16 => "CRC-16", + HashType.CRC16_ARC => "CRC-16/ARC [ARC, CRC-16, CRC-16/LHA, CRC-IBM]", + HashType.CRC16_CDMA2000 => "CRC-16/CDMA2000", + HashType.CRC16_CMS => "CRC-16/CMS", + HashType.CRC16_DDS110 => "CRC-16/DDS-110", + HashType.CRC16_DECTR => "CRC-16/DECT-R [R-CRC-16]", + HashType.CRC16_DECTX => "CRC-16/DECT-X [X-CRC-16]", + HashType.CRC16_DNP => "CRC-16/DNP", + HashType.CRC16_EN13757 => "CRC-16/EN-13757", + HashType.CRC16_GENIBUS => "CRC-16/GENIBUS [CRC-16/DARC, CRC-16/EPC, CRC-16/EPC-C1G2, CRC-16/I-CODE]", + HashType.CRC16_GSM => "CRC-16/GSM", + HashType.CRC16_IBM3740 => "CRC-16/IBM-3740 [CRC-16/AUTOSAR, CRC-16/CCITT-FALSE]", + HashType.CRC16_IBMSDLC => "CRC-16/IBM-SDLC [CRC-16/ISO-HDLC, CRC-16/ISO-IEC-14443-3-B, CRC-16/X-25, CRC-B, X-25]", + HashType.CRC16_ISOIEC144433A => "CRC-16/ISO-IEC-14443-3-A [CRC-A]", + HashType.CRC16_KERMIT => "CRC-16/KERMIT [CRC-16/BLUETOOTH, CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-16/V-41-LSB, CRC-CCITT, KERMIT]", + HashType.CRC16_LJ1200 => "CRC-16/LJ1200", + HashType.CRC16_M17 => "CRC-16/M17", + HashType.CRC16_MAXIMDOW => "CRC-16/MAXIM-DOW [CRC-16/MAXIM]", + HashType.CRC16_MCRF4XX => "CRC-16/MCRF4XX", + HashType.CRC16_MODBUS => "CRC-16/MODBUS [MODBUS]", + HashType.CRC16_NRSC5 => "CRC-16/NRSC-5", + HashType.CRC16_OPENSAFETYA => "CRC-16/OPENSAFETY-A", + HashType.CRC16_OPENSAFETYB => "CRC-16/OPENSAFETY-B", + HashType.CRC16_PROFIBUS => "CRC-16/PROFIBUS [CRC-16/IEC-61158-2]", + HashType.CRC16_RIELLO => "CRC-16/RIELLO", + HashType.CRC16_SPIFUJITSU => "CRC-16/SPI-FUJITSU [CRC-16/AUG-CCITT]", + HashType.CRC16_T10DIF => "CRC-16/T10-DIF", + HashType.CRC16_TELEDISK => "CRC-16/TELEDISK", + HashType.CRC16_TMS37157 => "CRC-16/TMS37157", + HashType.CRC16_UMTS => "CRC-16/UMTS [CRC-16/BUYPASS, CRC-16/VERIFONE]", + HashType.CRC16_USB => "CRC-16/USB", + HashType.CRC16_XMODEM => "CRC-16/XMODEM [CRC-16/ACORN, CRC-16/LTE, CRC-16/V-41-MSB, XMODEM, ZMODEM]", + + HashType.CRC17_CANFD => "CRC-17/CAN-FD", + + HashType.CRC21_CANFD => "CRC-21/CAN-FD", + + HashType.CRC24_BLE => "CRC-24/BLE", + HashType.CRC24_FLEXRAYA => "CRC-24/FLEXRAY-A", + HashType.CRC24_FLEXRAYB => "CRC-24/FLEXRAY-B", + HashType.CRC24_INTERLAKEN => "CRC-24/INTERLAKEN", + HashType.CRC24_LTEA => "CRC-24/LTE-A", + HashType.CRC24_LTEB => "CRC-24/LTE-B", + HashType.CRC24_OPENPGP => "CRC-24/OPENPGP", + HashType.CRC24_OS9 => "CRC-24/OS-9", + + HashType.CRC30_CDMA => "CRC-30/CDMA", + + HashType.CRC31_PHILIPS => "CRC-31/PHILIPS", + + HashType.CRC32 => "CRC-32", + HashType.CRC32_AIXM => "CRC-32/AIXM", + HashType.CRC32_AUTOSAR => "CRC-32/AUTOSAR", + HashType.CRC32_BASE91D => "CRC-32/BASE91-D", + HashType.CRC32_BZIP2 => "BZIP2", + HashType.CRC32_CDROMEDC => "CRC-32/CD-ROM-EDC", + HashType.CRC32_CKSUM => "CRC-32/CKSUM", + HashType.CRC32_ISCSI => "CRC-32/ISCSI", + HashType.CRC32_ISOHDLC => "CRC-32/ISO-HDLC", + HashType.CRC32_JAMCRC => "CRC-32/JAMCRC", + HashType.CRC32_MEF => "CRC-32/MEF", + HashType.CRC32_MPEG2 => "CRC-32/MPEG-2", + HashType.CRC32_XFER => "CRC-32/XFER", + + HashType.CRC40_GSM => "CRC-40/GSM", + + HashType.CRC64 => "CRC-64", + HashType.CRC64_ECMA182 => "CRC-64/ECMA-182, Microsoft implementation", + HashType.CRC64_GOISO => "CRC-64/GO-ISO", + HashType.CRC64_MS => "CRC-64/MS", + HashType.CRC64_NVME => "CRC-64/NVME", + HashType.CRC64_REDIS => "CRC-64/REDIS", + HashType.CRC64_WE => "CRC-64/WE", + HashType.CRC64_XZ => "CRC-64/XZ", + + HashType.Fletcher16 => "John G. Fletcher's 16-bit checksum", + HashType.Fletcher32 => "John G. Fletcher's 32-bit checksum", + HashType.Fletcher64 => "John G. Fletcher's 64-bit checksum", + + HashType.FNV0_32 => "FNV hash (Variant 0, 32-bit)", + HashType.FNV0_64 => "FNV hash (Variant 0, 64-bit)", + HashType.FNV1_32 => "FNV hash (Variant 1, 32-bit)", + HashType.FNV1_64 => "FNV hash (Variant 1, 64-bit)", + HashType.FNV1a_32 => "FNV hash (Variant 1a, 32-bit)", + HashType.FNV1a_64 => "FNV hash (Variant 1a, 64-bit)", + + HashType.MD2 => "MD2 message-digest algorithm", + HashType.MD4 => "MD4 message-digest algorithm", + HashType.MD5 => "MD5 message-digest algorithm", + + HashType.RIPEMD128 => "RIPEMD-128 hash", + HashType.RIPEMD160 => "RIPEMD-160 hash", + HashType.RIPEMD256 => "RIPEMD-256 hash", + HashType.RIPEMD320 => "RIPEMD-320 hash", + + HashType.SHA1 => "SHA-1 hash", + HashType.SHA256 => "SHA-256 hash", + HashType.SHA384 => "SHA-384 hash", + HashType.SHA512 => "SHA-512 hash", +#if NET8_0_OR_GREATER + HashType.SHA3_256 => "SHA3-256 hash", + HashType.SHA3_384 => "SHA3-384 hash", + HashType.SHA3_512 => "SHA3-512 hash", + HashType.SHAKE128 => "SHAKE128 SHA-3 family hash (256-bit)", + HashType.SHAKE256 => "SHAKE256 SHA-3 family hash (512-bit)", +#endif + + HashType.SpamSum => "spamsum fuzzy hash", + + HashType.Tiger128_3 => "Tiger 128-bit hash, 3 passes", + HashType.Tiger128_4 => "Tiger 128-bit hash, 4 passes", + HashType.Tiger160_3 => "Tiger 160-bit hash, 3 passes", + HashType.Tiger160_4 => "Tiger 160-bit hash, 4 passes", + HashType.Tiger192_3 => "Tiger 192-bit hash, 3 passes", + HashType.Tiger192_4 => "Tiger 192-bit hash, 4 passes", + HashType.Tiger2_128_3 => "Tiger2 128-bit hash, 3 passes", + HashType.Tiger2_128_4 => "Tiger2 128-bit hash, 4 passes", + HashType.Tiger2_160_3 => "Tiger2 160-bit hash, 3 passes", + HashType.Tiger2_160_4 => "Tiger2 160-bit hash, 4 passes", + HashType.Tiger2_192_3 => "Tiger2 192-bit hash, 3 passes", + HashType.Tiger2_192_4 => "Tiger2 192-bit hash, 4 passes", + + HashType.XxHash32 => "xxHash32 hash", + HashType.XxHash64 => "xxHash64 hash", +#if NET462_OR_GREATER || NETCOREAPP + HashType.XxHash3 => "XXH3 64-bit hash", + HashType.XxHash128 => "XXH128 128-bit hash", +#endif + + _ => $"{hashType}", + }; + } + + /// + /// Get the hash type associated to a string, if possible + /// + /// TODO: This should be automated instead of hardcoded + public static HashType? GetHashType(this string? str) + { + // Ignore invalid strings + if (string.IsNullOrEmpty(str)) + return null; + + // Normalize the string before matching + str = str!.Replace("-", string.Empty); + str = str.Replace(" ", string.Empty); + str = str.Replace("/", "_"); + str = str.Replace("\\", "_"); + str = str.ToLowerInvariant(); + + // Match based on potential names + return str switch + { + "adler" or "adler32" => HashType.Adler32, + +#if NET7_0_OR_GREATER + "blake3" => HashType.BLAKE3, +#endif + + "crc1_0" or "crc1_zero" => HashType.CRC1_ZERO, + "crc1_1" or "crc1_one" => HashType.CRC1_ONE, + + "crc3_gsm" => HashType.CRC3_GSM, + "crc3_rohc" => HashType.CRC3_ROHC, + + "crc4_g704" or "crc4_itu" => HashType.CRC4_G704, + "crc4_interlaken" => HashType.CRC4_INTERLAKEN, + + "crc5_epc" or "crc5_epcc1g2" => HashType.CRC5_EPCC1G2, + "crc5_g704" or "crc5_itu" => HashType.CRC5_G704, + "crc5_usb" => HashType.CRC5_USB, + + "crc6_cdma2000a" => HashType.CRC6_CDMA2000A, + "crc6_cdma2000b" => HashType.CRC6_CDMA2000B, + "crc6_darc" => HashType.CRC6_DARC, + "crc6_g704" or "crc6_itu" => HashType.CRC6_G704, + "crc6_gsm" => HashType.CRC6_GSM, + + "crc7" or "crc7_mmc" => HashType.CRC7_MMC, + "crc7_rohc" => HashType.CRC7_ROHC, + "crc7_umts" => HashType.CRC7_UMTS, + + "crc8" => HashType.CRC8, + "crc8_autosar" => HashType.CRC8_AUTOSAR, + "crc8_bluetooth" => HashType.CRC8_BLUETOOTH, + "crc8_cdma2000" => HashType.CRC8_CDMA2000, + "crc8_darc" => HashType.CRC8_DARC, + "crc8_dvbs2" => HashType.CRC8_DVBS2, + "crc8_gsma" => HashType.CRC8_GSMA, + "crc8_gsmb" => HashType.CRC8_GSMB, + "crc8_hitag" => HashType.CRC8_HITAG, + "crc8_i4321" or "crc8_itu" => HashType.CRC8_I4321, + "crc8_icode" => HashType.CRC8_ICODE, + "crc8_lte" => HashType.CRC8_LTE, + "crc8_maximdow" or "crc8_maxim" or "dowcrc" => HashType.CRC8_MAXIMDOW, + "crc8_mifaremad" => HashType.CRC8_MIFAREMAD, + "crc8_nrsc5" => HashType.CRC8_NRSC5, + "crc8_opensafety" => HashType.CRC8_OPENSAFETY, + "crc8_rohc" => HashType.CRC8_ROHC, + "crc8_saej1850" => HashType.CRC8_SAEJ1850, + "crc8_smbus" => HashType.CRC8_SMBUS, + "crc8_tech3250" or "crc8_aes" or "crc8_ebu" => HashType.CRC8_TECH3250, + "crc8_wcdma" => HashType.CRC8_WCDMA, + + "crc10_atm" or "crc10" or "crc10_i610" => HashType.CRC10_ATM, + "crc10_cdma2000" => HashType.CRC10_CDMA2000, + "crc10_gsm" => HashType.CRC10_GSM, + + "crc11_flexray" or "crc11" => HashType.CRC11_FLEXRAY, + "crc11_umts" => HashType.CRC11_UMTS, + + "crc12_cdma2000" => HashType.CRC12_CDMA2000, + "crc12_dect" or "xcrc12" => HashType.CRC12_DECT, + "crc12_gsm" => HashType.CRC12_GSM, + "crc12_umts" or "crc12_3gpp" => HashType.CRC12_UMTS, + + "crc13_bbc" => HashType.CRC13_BBC, + + "crc14_darc" => HashType.CRC14_DARC, + "crc14_gsm" => HashType.CRC14_GSM, + + "crc15_can" or "crc15" => HashType.CRC15_CAN, + "crc15_mpt1327" => HashType.CRC15_MPT1327, + + "crc16" => HashType.CRC16, + "crc16_arc" or "arc" or "crc16_lha" or "crcibm" => HashType.CRC16_ARC, + "crc16_cdma2000" => HashType.CRC16_CDMA2000, + "crc16_cms" => HashType.CRC16_CMS, + "crc16_dds110" => HashType.CRC16_DDS110, + "crc16_dectr" or "rcrc16" => HashType.CRC16_DECTR, + "crc16_dectx" or "xcrc16" => HashType.CRC16_DECTX, + "crc16_dnp" => HashType.CRC16_DNP, + "crc16_en13757" => HashType.CRC16_EN13757, + "crc16_genibus" or "crc16_darc" or "crc16_epc" or "crc16_epcc1g2" or "crc16_icode" => HashType.CRC16_GENIBUS, + "crc16_gsm" => HashType.CRC16_GSM, + "crc16_ibm3740" or "crc16_autosar" or "crc16_cittfalse" => HashType.CRC16_IBM3740, + "crc16_ibmsdlc" or "crc16_isohdlc" or "crc16_isoiec144433b" or "crc16_x25" or "crcb" or "x25" => HashType.CRC16_IBMSDLC, + "crc16_isoiec144433a" or "crca" => HashType.CRC16_ISOIEC144433A, + "crc16_kermit" or "crc16_bluetooth" or "crc16_ccitt" or "crc16_ccitttrue" or "crc16_v41lsb" or "crcccitt" or "kermit" => HashType.CRC16_KERMIT, + "crc16_lj1200" => HashType.CRC16_LJ1200, + "crc16_m17" => HashType.CRC16_M17, + "crc16_maximdow" or "crc16_maxim" => HashType.CRC16_MAXIMDOW, + "crc16_mcrf4xx" => HashType.CRC16_MCRF4XX, + "crc16_modbus" or "modbus" => HashType.CRC16_MODBUS, + "crc16_nrsc5" => HashType.CRC16_NRSC5, + "crc16_opensafetya" => HashType.CRC16_OPENSAFETYA, + "crc16_opensafetyb" => HashType.CRC16_OPENSAFETYB, + "crc16_profibus" or "crc16_iec611582" => HashType.CRC16_PROFIBUS, + "crc16_riello" => HashType.CRC16_RIELLO, + "crc16_spifujitsu" or "crc16_augccitt" => HashType.CRC16_SPIFUJITSU, + "crc16_t10dif" => HashType.CRC16_T10DIF, + "crc16_teledisk" => HashType.CRC16_TELEDISK, + "crc16_tms37157" => HashType.CRC16_TMS37157, + "crc16_umts" or "crc16_buypass" or "crc16_verifone" => HashType.CRC16_UMTS, + "crc16_usb" => HashType.CRC16_USB, + "crc16_xmodem" or "crc16_acorn" or "crc16_lte" or "crc16_v41msb" or "xmodem" or "zmodem" => HashType.CRC16_XMODEM, + + "crc17_canfd" => HashType.CRC17_CANFD, + + "crc21_canfd" => HashType.CRC21_CANFD, + + "crc24_ble" => HashType.CRC24_BLE, + "crc24_flexraya" => HashType.CRC24_FLEXRAYA, + "crc24_flexrayb" => HashType.CRC24_FLEXRAYB, + "crc24_interlaken" => HashType.CRC24_INTERLAKEN, + "crc24_ltea" => HashType.CRC24_LTEA, + "crc24_lteb" => HashType.CRC24_LTEB, + "crc24_openpgp" => HashType.CRC24_OPENPGP, + "crc24_os9" => HashType.CRC24_OS9, + + "crc30_cdma" => HashType.CRC30_CDMA, + + "crc31_philips" => HashType.CRC31_PHILIPS, + + "crc32" => HashType.CRC32, + "crc32_aixm" => HashType.CRC32_AIXM, + "crc32_autosar" => HashType.CRC32_AUTOSAR, + "crc32_base91d" => HashType.CRC32_BASE91D, + "crc32_bzip2" => HashType.CRC32_BZIP2, + "crc32_cdromedc" => HashType.CRC32_CDROMEDC, + "crc32_cksum" => HashType.CRC32_CKSUM, + "crc32_iscsi" => HashType.CRC32_ISCSI, + "crc32_isohdlc" => HashType.CRC32_ISOHDLC, + "crc32_jamcrc" => HashType.CRC32_JAMCRC, + "crc32_mef" => HashType.CRC32_MEF, + "crc32_mpeg2" => HashType.CRC32_MPEG2, + "crc32_xfer" => HashType.CRC32_XFER, + + "crc40_gsm" => HashType.CRC40_GSM, + + "crc64" => HashType.CRC64, + "crc64_ecma182" => HashType.CRC64_ECMA182, + "crc64_goiso" => HashType.CRC64_GOISO, + "crc64_ms" => HashType.CRC64_MS, + "crc64_nvme" => HashType.CRC64_NVME, + "crc64_redis" => HashType.CRC64_REDIS, + "crc64_we" => HashType.CRC64_WE, + "crc64_xz" => HashType.CRC64_XZ, + + "fletcher16" => HashType.Fletcher16, + "fletcher32" => HashType.Fletcher32, + "fletcher64" => HashType.Fletcher64, + + "fnv0_32" => HashType.FNV0_32, + "fnv0_64" => HashType.FNV0_64, + "fnv1_32" => HashType.FNV1_32, + "fnv1_64" => HashType.FNV1_64, + "fnv1a_32" => HashType.FNV1a_32, + "fnv1a_64" => HashType.FNV1a_64, + + "md2" => HashType.MD2, + "md4" => HashType.MD4, + "md5" => HashType.MD5, + + "ripemd128" => HashType.RIPEMD128, + "ripemd160" => HashType.RIPEMD160, + "ripemd256" => HashType.RIPEMD256, + "ripemd320" => HashType.RIPEMD320, + + "sha1" => HashType.SHA1, + "sha256" => HashType.SHA256, + "sha384" => HashType.SHA384, + "sha512" => HashType.SHA512, +#if NET8_0_OR_GREATER + "sha3_256" => HashType.SHA3_256, + "sha3_384" => HashType.SHA3_384, + "sha3_512" => HashType.SHA3_512, + "shake128" => HashType.SHAKE128, + "shake256" => HashType.SHAKE256, +#endif + + "spamsum" => HashType.SpamSum, + + "tiger128_3" => HashType.Tiger128_3, + "tiger128_4" => HashType.Tiger128_4, + "tiger160_3" => HashType.Tiger160_3, + "tiger160_4" => HashType.Tiger160_4, + "tiger192_3" => HashType.Tiger192_3, + "tiger192_4" => HashType.Tiger192_4, + "tiger2_128_3" => HashType.Tiger2_128_3, + "tiger2_128_4" => HashType.Tiger2_128_4, + "tiger2_160_3" => HashType.Tiger2_160_3, + "tiger2_160_4" => HashType.Tiger2_160_4, + "tiger2_192_3" => HashType.Tiger2_192_3, + "tiger2_192_4" => HashType.Tiger2_192_4, + + "xxh" or "xxh32" or "xxh_32" or "xxhash" or "xxhash32" or "xxhash_32" => HashType.XxHash32, + "xxh64" or "xxh_64" or "xxhash64" or "xxhash_64" => HashType.XxHash64, +#if NET462_OR_GREATER || NETCOREAPP + "xxh3" or "xxh3_64" or "xxhash3" or "xxhash_3" => HashType.XxHash3, + "xxh128" or "xxh_128" or "xxhash128" or "xxhash_128" => HashType.XxHash128, +#endif + + _ => null, + }; + } + } +} \ No newline at end of file diff --git a/publish-nix.sh b/publish-nix.sh index a2bc965..0c83c53 100755 --- a/publish-nix.sh +++ b/publish-nix.sh @@ -1,19 +1,32 @@ -#! /bin/bash +#!/bin/bash # This batch file assumes the following: # - .NET 9.0 (or newer) SDK is installed and in PATH +# - zip is installed and in PATH +# - Git is installed and in PATH # # If any of these are not satisfied, the operation may fail # in an unpredictable way and result in an incomplete output. # Optional parameters +USE_ALL=false +INCLUDE_DEBUG=false NO_BUILD=false -while getopts "b" OPTION -do +NO_ARCHIVE=false +while getopts "udba" OPTION; do case $OPTION in + u) + USE_ALL=true + ;; + d) + INCLUDE_DEBUG=true + ;; b) NO_BUILD=true ;; + a) + NO_ARCHIVE=true + ;; *) echo "Invalid option provided" exit 1 @@ -24,13 +37,115 @@ done # Set the current directory as a variable BUILD_FOLDER=$PWD +# Set the current commit hash +COMMIT=$(git log --pretty=%H -1) + +# Output the selected options +echo "Selected Options:" +echo " Use all frameworks (-u) $USE_ALL" +echo " Include debug builds (-d) $INCLUDE_DEBUG" +echo " No build (-b) $NO_BUILD" +echo " No archive (-a) $NO_ARCHIVE" +echo " " + +# Create the build matrix arrays +FRAMEWORKS=("net9.0") +RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64" "osx-arm64") + +# Use expanded lists, if requested +if [ $USE_ALL = true ]; then + FRAMEWORKS=("net20" "net35" "net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0" "net9.0") +fi + +# Create the filter arrays +SINGLE_FILE_CAPABLE=("net5.0" "net6.0" "net7.0" "net8.0" "net9.0") +VALID_APPLE_FRAMEWORKS=("net6.0" "net7.0" "net8.0" "net9.0") +VALID_CROSS_PLATFORM_FRAMEWORKS=("netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0" "net9.0") +VALID_CROSS_PLATFORM_RUNTIMES=("win-arm64" "linux-x64" "linux-arm64" "osx-x64" "osx-arm64") + # Only build if requested -if [ $NO_BUILD = false ] -then +if [ $NO_BUILD = false ]; then # Restore Nuget packages for all builds echo "Restoring Nuget packages" dotnet restore # Create Nuget Package dotnet pack SabreTools.Hashing/SabreTools.Hashing.csproj --output $BUILD_FOLDER -fi \ No newline at end of file + + # Build Hasher + for FRAMEWORK in "${FRAMEWORKS[@]}"; do + for RUNTIME in "${RUNTIMES[@]}"; do + # Output the current build + echo "===== Build Hasher - $FRAMEWORK, $RUNTIME =====" + + # If we have an invalid combination of framework and runtime + if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then + if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then + echo "Skipped due to invalid combination" + continue + fi + fi + + # If we have Apple silicon but an unsupported framework + if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then + if [ $RUNTIME = "osx-arm64" ]; then + echo "Skipped due to no Apple Silicon support" + continue + fi + fi + + # Only .NET 5 and above can publish to a single file + if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then + # Only include Debug if set + if [ $INCLUDE_DEBUG = true ]; then + dotnet publish Hasher/Hasher.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true + fi + dotnet publish Hasher/Hasher.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false + else + # Only include Debug if set + if [ $INCLUDE_DEBUG = true ]; then + dotnet publish Hasher/Hasher.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT + fi + dotnet publish Hasher/Hasher.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false + fi + done + done +fi + +# Only create archives if requested +if [ $NO_ARCHIVE = false ]; then + # Create Hasher archives + for FRAMEWORK in "${FRAMEWORKS[@]}"; do + for RUNTIME in "${RUNTIMES[@]}"; do + # Output the current build + echo "===== Archive Hasher - $FRAMEWORK, $RUNTIME =====" + + # If we have an invalid combination of framework and runtime + if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then + if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then + echo "Skipped due to invalid combination" + continue + fi + fi + + # If we have Apple silicon but an unsupported framework + if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then + if [ $RUNTIME = "osx-arm64" ]; then + echo "Skipped due to no Apple Silicon support" + continue + fi + fi + + # Only include Debug if set + if [ $INCLUDE_DEBUG = true ]; then + cd $BUILD_FOLDER/Hasher/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/ + zip -r $BUILD_FOLDER/Hasher_${FRAMEWORK}_${RUNTIME}_debug.zip . + fi + cd $BUILD_FOLDER/Hasher/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/ + zip -r $BUILD_FOLDER/Hasher_${FRAMEWORK}_${RUNTIME}_release.zip . + done + done + + # Reset the directory + cd $BUILD_FOLDER +fi diff --git a/publish-win.ps1 b/publish-win.ps1 index 4d349d2..7ac94a2 100644 --- a/publish-win.ps1 +++ b/publish-win.ps1 @@ -6,21 +6,129 @@ # Optional parameters param( + [Parameter(Mandatory = $false)] + [Alias("UseAll")] + [switch]$USE_ALL, + + [Parameter(Mandatory = $false)] + [Alias("IncludeDebug")] + [switch]$INCLUDE_DEBUG, + [Parameter(Mandatory = $false)] [Alias("NoBuild")] - [switch]$NO_BUILD + [switch]$NO_BUILD, + + [Parameter(Mandatory = $false)] + [Alias("NoArchive")] + [switch]$NO_ARCHIVE ) # Set the current directory as a variable $BUILD_FOLDER = $PSScriptRoot +# Set the current commit hash +$COMMIT = git log --pretty=format:"%H" -1 + +# Output the selected options +Write-Host "Selected Options:" +Write-Host " Use all frameworks (-UseAll) $USE_ALL" +Write-Host " Include debug builds (-IncludeDebug) $INCLUDE_DEBUG" +Write-Host " No build (-NoBuild) $NO_BUILD" +Write-Host " No archive (-NoArchive) $NO_ARCHIVE" +Write-Host " " + +# Create the build matrix arrays +$FRAMEWORKS = @('net9.0') +$RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64') + +# Use expanded lists, if requested +if ($USE_ALL.IsPresent) { + $FRAMEWORKS = @('net20', 'net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0', 'net9.0') +} + +# Create the filter arrays +$SINGLE_FILE_CAPABLE = @('net5.0', 'net6.0', 'net7.0', 'net8.0', 'net9.0') +$VALID_APPLE_FRAMEWORKS = @('net6.0', 'net7.0', 'net8.0', 'net9.0') +$VALID_CROSS_PLATFORM_FRAMEWORKS = @('netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0', 'net9.0') +$VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64') + # Only build if requested -if (!$NO_BUILD.IsPresent) -{ +if (!$NO_BUILD.IsPresent) { # Restore Nuget packages for all builds Write-Host "Restoring Nuget packages" dotnet restore # Create Nuget Package dotnet pack SabreTools.Hashing\SabreTools.Hashing.csproj --output $BUILD_FOLDER + + # Build Hasher + foreach ($FRAMEWORK in $FRAMEWORKS) { + foreach ($RUNTIME in $RUNTIMES) { + # Output the current build + Write-Host "===== Build Hasher - $FRAMEWORK, $RUNTIME =====" + + # If we have an invalid combination of framework and runtime + if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) { + Write-Host "Skipped due to invalid combination" + continue + } + + # If we have Apple silicon but an unsupported framework + if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') { + Write-Host "Skipped due to no Apple Silicon support" + continue + } + + # Only .NET 5 and above can publish to a single file + if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) { + # Only include Debug if set + if ($INCLUDE_DEBUG.IsPresent) { + dotnet publish Hasher\Hasher.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true + } + dotnet publish Hasher\Hasher.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false + } + else { + # Only include Debug if set + if ($INCLUDE_DEBUG.IsPresent) { + dotnet publish Hasher\Hasher.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT + } + dotnet publish Hasher\Hasher.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false + } + } + } +} + +# Only create archives if requested +if (!$NO_ARCHIVE.IsPresent) { + # Create Hasher archives + foreach ($FRAMEWORK in $FRAMEWORKS) { + foreach ($RUNTIME in $RUNTIMES) { + # Output the current build + Write-Host "===== Archive Hasher - $FRAMEWORK, $RUNTIME =====" + + # If we have an invalid combination of framework and runtime + if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) { + Write-Host "Skipped due to invalid combination" + continue + } + + # If we have Apple silicon but an unsupported framework + if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') { + Write-Host "Skipped due to no Apple Silicon support" + continue + } + + # Only include Debug if set + if ($INCLUDE_DEBUG.IsPresent) { + Set-Location -Path $BUILD_FOLDER\Hasher\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\ + 7z a -tzip $BUILD_FOLDER\Hasher_${FRAMEWORK}_${RUNTIME}_debug.zip * + } + + Set-Location -Path $BUILD_FOLDER\Hasher\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\ + 7z a -tzip $BUILD_FOLDER\Hasher_${FRAMEWORK}_${RUNTIME}_release.zip * + } + } + + # Reset the directory + Set-Location -Path $PSScriptRoot }