diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c05b823..c2dc9ae 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -10,8 +10,31 @@ on: jobs: build: + strategy: + fail-fast: false + matrix: + platform: + - {os: windows-latest, framework: net7.0-windows, runtime: win-x64} + - {os: ubuntu-latest, framework: net7.0, runtime: linux-x64} + build_type: + - { + name: 'Build', + configuration: Debug, + command: 'dotnet build ConsoleApp --no-self-contained', + } + - { + name: 'Publish to single file', + configuration: Release, + command: 'dotnet publish ConsoleApp -p:PublishSingleFile=true --self-contained', + } + - { + name: 'Run tests', + configuration: Release, + command: 'dotnet test --logger "console;verbosity=detailed"', + } - runs-on: windows-latest + runs-on: ${{ matrix.platform.os }} + name: ${{ matrix.build_type.name }} (${{ matrix.build_type.configuration }}, ${{ matrix.platform.os }} steps: - name: Checkout Project @@ -20,14 +43,10 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: 7.0.x - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.1 - - name: Build Debug - run: msbuild ConsoleApp\ConsoleApp.csproj -target:Rebuild -property:Configuration=Debug -restore - - name: Build Release - run: msbuild ConsoleApp\ConsoleApp.csproj -target:Rebuild -property:Configuration=Release -restore - - name: Publish to single executable - run: msbuild ConsoleApp\ConsoleApp.csproj -target:Rebuild /p:DeployOnBuild=true /p:PublishProfile=publish-single-file -restore - - name: Test - run: dotnet test --logger "console;verbosity=detailed" --configuration Release + - name: ${{ matrix.build_type.name }} + run: > + ${{ matrix.build_type.command }} + --framework=${{ matrix.platform.framework }} + --runtime=${{ matrix.platform.runtime }} + --configuration=${{ matrix.build_type.configuration }} diff --git a/ConsoleApp/ConsoleApp.csproj b/ConsoleApp/ConsoleApp.csproj index bd12c43..66205fb 100644 --- a/ConsoleApp/ConsoleApp.csproj +++ b/ConsoleApp/ConsoleApp.csproj @@ -2,7 +2,6 @@ Exe - net7.0-windows ../github-resources/investigation.ico AnyCPU;x64 Debug;Release;Debug_ProcessEnts @@ -11,6 +10,7 @@ UntitledParser enable 9999 + net7.0;net7.0-windows @@ -68,8 +68,8 @@ - - - + + + diff --git a/ConsoleApp/src/DemoArgProcessing/DemoParserSubCommand.cs b/ConsoleApp/src/DemoArgProcessing/DemoParserSubCommand.cs index d5b66a4..aff3448 100644 --- a/ConsoleApp/src/DemoArgProcessing/DemoParserSubCommand.cs +++ b/ConsoleApp/src/DemoArgProcessing/DemoParserSubCommand.cs @@ -123,7 +123,7 @@ private void FlattenDirectories(DemoParsingSetupInfo setupInfo) { demoPath, commonParent == "" ? demoPath.FullName - : PathExt.GetRelativePath(commonParent, demoPath.FullName) + : Path.GetRelativePath(commonParent, demoPath.FullName) )); } diff --git a/ConsoleApp/src/PathExt.cs b/ConsoleApp/src/PathExt.cs deleted file mode 100644 index 7376be3..0000000 --- a/ConsoleApp/src/PathExt.cs +++ /dev/null @@ -1,177 +0,0 @@ -/* - * More-or-less ripped straight from .NET Core runtime, with some ws/comments stripped out - * - * Licensed to the .NET Foundation under one or more agreements. - * The .NET Foundation licenses this file to you under the MIT license. - */ - -// FIXME: This is very Windows-specific! This will probably be one of the things -// that needs fixed up if/when doing a Linux/Mono port. - -using System; -using System.IO; -using System.Text; - -namespace ConsoleApp { - - internal static class PathExt { - - private static bool IsDirectorySeparator(char c) => c == '\\' || c == '/'; - private static bool EndsInDirectorySeparator(string path) - => path.Length > 0 && IsDirectorySeparator(path[^1]); - - private static unsafe int EqualStartingCharacterCount(string? first, string? second, bool ignoreCase) { - if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0; - - int commonChars = 0; - fixed (char* f = first) - fixed (char* s = second) { - char* l = f; - char* r = s; - char* leftEnd = l + first!.Length; - char* rightEnd = r + second!.Length; - - while (l != leftEnd && r != rightEnd - && (*l == *r || (ignoreCase && char.ToUpperInvariant(*l) == char.ToUpperInvariant(*r)))) { - commonChars++; - l++; - r++; - } - } - - return commonChars; - } - - private static int GetCommonPathLength(string first, string second, bool ignoreCase) { - int commonChars = EqualStartingCharacterCount(first, second, ignoreCase); - if (commonChars == 0) return commonChars; - if (commonChars == first.Length && (commonChars == second.Length || - IsDirectorySeparator(second[commonChars]))) - return commonChars; - if (commonChars == second.Length && IsDirectorySeparator(first[commonChars])) - return commonChars; - while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1])) - commonChars--; - return commonChars; - } - - private static bool AreRootsEqual(string first, string second, StringComparison comparisonType) { - int firstRootLength = GetRootLength(first); - int secondRootLength = GetRootLength(second); - - return firstRootLength == secondRootLength - && string.Compare( - strA: first, - indexA: 0, - strB: second, - indexB: 0, - length: firstRootLength, - comparisonType: comparisonType) == 0; - } - - private const int - DevicePrefixLength = 4, - UncPrefixLength = 2, - UncExtendedPrefixLength = 8; - - private static bool IsExtended(string path) { - return path.Length >= DevicePrefixLength - && path[0] == '\\' - && (path[1] == '\\' || path[1] == '?') - && path[2] == '?' - && path[3] == '\\'; - } - - private static bool IsDevice(string path) { - return IsExtended(path) - || - ( - path.Length >= DevicePrefixLength - && IsDirectorySeparator(path[0]) - && IsDirectorySeparator(path[1]) - && (path[2] == '.' || path[2] == '?') - && IsDirectorySeparator(path[3]) - ); - } - - private static bool IsDeviceUNC(string path) { - return path.Length >= UncExtendedPrefixLength - && IsDevice(path) - && IsDirectorySeparator(path[7]) - && path[4] == 'U' - && path[5] == 'N' - && path[6] == 'C'; - } - - private static bool IsValidDriveChar(char value) { - return (value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z'); - } - - private static int GetRootLength(string path) { - int pathLength = path.Length; - int i = 0; - - bool deviceSyntax = IsDevice(path); - bool deviceUnc = deviceSyntax && IsDeviceUNC(path); - if ((!deviceSyntax || deviceUnc) && pathLength > 0 && IsDirectorySeparator(path[0])) { - if (deviceUnc || (pathLength > 1 && IsDirectorySeparator(path[1]))) { - i = deviceUnc ? UncExtendedPrefixLength : UncPrefixLength; - int n = 2; - while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0)) i++; - } else { - i = 1; - } - } else if (deviceSyntax) { - i = DevicePrefixLength; - while (i < pathLength && !IsDirectorySeparator(path[i])) i++; - if (i < pathLength && i > DevicePrefixLength && IsDirectorySeparator(path[i])) i++; - } - else if (pathLength >= 2 - && path[1] == ':' - && IsValidDriveChar(path[0])) { - i = 2; - if (pathLength > 2 && IsDirectorySeparator(path[2])) i++; - } - return i; - } - - public static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType = StringComparison.OrdinalIgnoreCase) { - if (relativeTo == null) throw new ArgumentNullException(nameof(relativeTo)); - if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Empty path", nameof(relativeTo)); - if (path == null) throw new ArgumentNullException(nameof(path)); - if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Empty path", nameof(path)); - relativeTo = Path.GetFullPath(relativeTo); - path = Path.GetFullPath(path); - if (!AreRootsEqual(relativeTo, path, comparisonType)) return path; - int commonLength = GetCommonPathLength(relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase); - if (commonLength == 0) return path; - int relativeToLength = relativeTo.Length; - if (EndsInDirectorySeparator(relativeTo)) relativeToLength--; - bool pathEndsInSeparator = EndsInDirectorySeparator(path); - int pathLength = path.Length; - if (pathEndsInSeparator) pathLength--; - if (relativeToLength == pathLength && commonLength >= relativeToLength) return "."; - var sb = new StringBuilder(); - sb.EnsureCapacity(Math.Max(relativeTo.Length, path.Length)); - if (commonLength < relativeToLength) { - sb.Append(".."); - for (int i = commonLength + 1; i < relativeToLength; i++) { - if (IsDirectorySeparator(relativeTo[i])) { - sb.Append("\\.."); - } - } - } - else if (IsDirectorySeparator(path[commonLength])) { - commonLength++; - } - int differenceLength = pathLength - commonLength; - if (pathEndsInSeparator) differenceLength++; - if (differenceLength > 0) { - if (sb.Length > 0) sb.Append(Path.DirectorySeparatorChar); - sb.Append(path.AsSpan(commonLength, differenceLength).ToString()); - } - - return sb.ToString(); - } - } -} diff --git a/ConsoleApp/src/Program.cs b/ConsoleApp/src/Program.cs index 70b2e1e..cc9b77a 100644 --- a/ConsoleApp/src/Program.cs +++ b/ConsoleApp/src/Program.cs @@ -150,6 +150,8 @@ public static void Main(string[] args) { Environment.ExitCode = 3; } Console.ResetColor(); + if (!Utils.IsWindows()) + Console.WriteLine(); // TODO: checkout why linux doesn't give us as many lines as windows if (Utils.WillBeDestroyedOnExit()) Console.ReadLine(); } diff --git a/ConsoleApp/src/Utils.cs b/ConsoleApp/src/Utils.cs index 6f2fc20..a482c77 100644 --- a/ConsoleApp/src/Utils.cs +++ b/ConsoleApp/src/Utils.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; namespace ConsoleApp { @@ -108,13 +109,15 @@ public static string FormatTime(double seconds) { public static string GetExeName() => QuoteIfHasSpaces(AppDomain.CurrentDomain.FriendlyName); - [System.Runtime.InteropServices.DllImport("kernel32.dll")] + [DllImport("kernel32.dll")] private static extern int GetConsoleProcessList(int[] buffer, int size); // used to check if we should use ReadKey() once we finish public static bool WillBeDestroyedOnExit() { - return !Debugger.IsAttached && GetConsoleProcessList(new int[2], 2) <= 1; + if (IsWindows()) + return !Debugger.IsAttached && GetConsoleProcessList(new int[2], 2) <= 1; + return false; // TODO this is a bit of a hack, see if there's a better solution on linux } @@ -140,8 +143,13 @@ public static bool WillBeDestroyedOnExit() { } + public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + // microsoft, you autocomplete dir to '.\dir name\' and then that last single quote escapes into a double quote which fucks everything public static string[] FixPowerShellBullshit(string[] args) { + if (!IsWindows()) + return args; // example input: '.\nosla 12 50 89\' '.\nosla 12 50 89\' gets converted to [.\nosla 12 50 89" .\nosla, 12, 50, 89"] var newArgs = new List(); bool fixing = false; diff --git a/DemoParser/DemoParser.csproj b/DemoParser/DemoParser.csproj index 18321ef..78ec453 100644 --- a/DemoParser/DemoParser.csproj +++ b/DemoParser/DemoParser.csproj @@ -1,7 +1,7 @@  - net7.0-windows + net7.0;net7.0-windows true UntitledDemoParser UncraftedName diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 6cb00f3..7806084 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,7 +1,7 @@  - net7.0-windows + net7.0;net7.0-windows false AnyCPU;x64 Debug;Release;Debug_ProcessEnts diff --git a/Tests/src/DemoParseTests.cs b/Tests/src/DemoParseTests.cs index 86f63e5..9d9aa49 100644 --- a/Tests/src/DemoParseTests.cs +++ b/Tests/src/DemoParseTests.cs @@ -47,9 +47,30 @@ public abstract class DemoParseTests { private static readonly Dictionary RawDemoDataLookup = RawDemoDataLookup = new Dictionary(); private static bool _loaded; - public static readonly string ProjectDir = - // bin/Debug/net461 -> ../../.. - Directory.GetParent(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!)!.Parent!.Parent!.FullName; + private static string _projectDir; + public static string ProjectDir { + get { + if (_projectDir != null) + return _projectDir; + /* + * I keep running into stupid issues with the executable being nested at different levels in the + * bin folder, and it seems like C# doesn't provide a way for us to know what the project folder + * is (maybe there's some assembly attribute stuff I could use?). So I'll just iterate up the + * folders until I get to the project folder; this should always work and will throw exceptions + * when it doesn't. + */ + string projectName = Assembly.GetCallingAssembly().GetName().Name!; + string dir = Path.GetFullPath(Environment.CurrentDirectory); + for (int i = 0; i < 10; i++) { + if (dir.EndsWith(projectName, StringComparison.OrdinalIgnoreCase)) { + _projectDir = dir; + return _projectDir; + } + dir = Directory.GetParent(dir)!.FullName; + } + throw new DirectoryNotFoundException($"Could not find directory '{projectName}'"); + } + } [OneTimeSetUp] diff --git a/publish-linux.sh b/publish-linux.sh new file mode 100644 index 0000000..5b6be3f --- /dev/null +++ b/publish-linux.sh @@ -0,0 +1 @@ +dotnet publish ConsoleApp --runtime=linux-x64 --framework=net7.0 -p:PublishSingleFile=true --self-contained=true --configuration=Release \ No newline at end of file