From bb8d1ce66980a60d1028b0a1854ea1c77881acc7 Mon Sep 17 00:00:00 2001 From: DaNike Date: Mon, 17 Jun 2024 23:17:10 -0500 Subject: [PATCH] Try replacing setup-mono.ps1 with C# script --- .github/gen-test-matrix.ps1 | 116 +++++++------- .github/workflows/run-mono-tests.ps1 | 2 +- .github/workflows/test.yml | 4 +- MonoMod.sln | 17 +++ build/Directory.Build.props | 31 ++++ build/Directory.Build.targets | 5 + build/setup-mono/Program.cs | 219 +++++++++++++++++++++++++++ build/setup-mono/setup-mono.csproj | 16 ++ tools/Common.CS.targets | 2 +- 9 files changed, 351 insertions(+), 61 deletions(-) create mode 100644 build/Directory.Build.props create mode 100644 build/Directory.Build.targets create mode 100644 build/setup-mono/Program.cs create mode 100644 build/setup-mono/setup-mono.csproj diff --git a/.github/gen-test-matrix.ps1 b/.github/gen-test-matrix.ps1 index dba1544c..8173b3fc 100644 --- a/.github/gen-test-matrix.ps1 +++ b/.github/gen-test-matrix.ps1 @@ -4,8 +4,8 @@ param ( ) $ErrorActionPreference = "Stop"; - -$nugetOrgPkgSrc = "nuget.org"; + +$nugetOrgPkgSrc = "nuget.org"; $operatingSystems = @( [pscustomobject]@{ @@ -16,7 +16,7 @@ $operatingSystems = @( runnerArch = 1; hasFramework = $true; unityMonoArch = @("win32", "win64", "win_arm64"); - unityMonoDll = "mono-2.0-bdwgc.dll"; + unityMonoDll = "mono-2.0-bdwgc.dll"; dllSuffix = ".dll"; }, [pscustomobject]@{ @@ -27,8 +27,8 @@ $operatingSystems = @( runnerArch = 0; hasMono = $true; unityMonoArch = @("linux64"); - unityMonoDll = "limonobdwgc-2.0.so"; # TODO - dllPrefix = "lib"; + unityMonoDll = "limonobdwgc-2.0.so"; # TODO + dllPrefix = "lib"; dllSuffix = ".so"; }, [pscustomobject]@{ @@ -39,8 +39,8 @@ $operatingSystems = @( runnerArch = 0; hasMono = $true; unityMonoArch = @("macos_x64"); - unityMonoDll = "limonobdwgc-2.0.dylib"; - dllPrefix = "lib"; + unityMonoDll = "limonobdwgc-2.0.dylib"; + dllPrefix = "lib"; dllSuffix = ".dylib"; }, [pscustomobject]@{ @@ -52,8 +52,8 @@ $operatingSystems = @( runnerArch = 1; hasMono = $true; unityMonoArch = @("macos_x64", "macos_arm64"); - unityMonoDll = "limonobdwgc-2.0.dylib"; - dllPrefix = "lib"; + unityMonoDll = "limonobdwgc-2.0.dylib"; + dllPrefix = "lib"; dllSuffix = ".dylib"; } ); @@ -96,8 +96,8 @@ $dotnetVersions = @( sdk = "6.0"; tfm = "net6.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; - netMonoPkgVer = "6.0.31" + pgo = $true; + netMonoPkgVer = "6.0.31" netMonoNugetSrc = $nugetOrgPkgSrc; }, [pscustomobject]@{ @@ -105,8 +105,8 @@ $dotnetVersions = @( sdk = "7.0"; tfm = "net7.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; - netMonoPkgVer = "7.0.20" + pgo = $true; + netMonoPkgVer = "7.0.20" netMonoNugetSrc = $nugetOrgPkgSrc; }, [pscustomobject]@{ @@ -114,25 +114,25 @@ $dotnetVersions = @( sdk = "8.0"; tfm = "net8.0"; rids = @("win-x86","win-x64","win-arm64","linux-x64","linux-arm","linux-arm64","osx-x64","osx-arm64"); - pgo = $true; - netMonoPkgVer = "8.0.6" + pgo = $true; + netMonoPkgVer = "8.0.6" netMonoNugetSrc = $nugetOrgPkgSrc; } ); - -$netMonoPackageName = "Microsoft.NETCore.App.Runtime.Mono.{RID}"; -$netMonoLibPath = "runtimes/{RID}/lib/{TFM}/"; -$netMonoDllPath = "runtimes/{RID}/native/{DllPre}coreclr{DllSuf}"; - -function Fill-Template($template, $obj) -{ - $result = $template; - foreach ($kvp in $obj.GetEnumerator()) - { + +$netMonoPackageName = "Microsoft.NETCore.App.Runtime.Mono.{RID}"; +$netMonoLibPath = "runtimes/{RID}/lib/{TFM}/"; +$netMonoDllPath = "runtimes/{RID}/native/{DllPre}coreclr{DllSuf}"; + +function Fill-Template($template, $obj) +{ + $result = $template; + foreach ($kvp in $obj.GetEnumerator()) + { $result = $result -replace "{$($kvp.Key)}",$kvp.Value; - } + } return $result; -} +} $monoTfm = "net462"; @@ -172,7 +172,7 @@ function Remove-NullProperties { $obj } -} +} foreach ($os in $operatingSystems) { @@ -253,38 +253,38 @@ foreach ($os in $operatingSystems) arch = $arch; } ); - } - - if ($dotnet.netMonoNugetSrc) - { - # this runtime version has an associated Mono build - $fill = @{ - RID = $rid; - TFM = $dotnet.tfm; - DllPre = $os.dllPrefix; + } + + if ($dotnet.netMonoNugetSrc) + { + # this runtime version has an associated Mono build + $fill = @{ + RID = $rid; + TFM = $dotnet.tfm; + DllPre = $os.dllPrefix; DllSuf = $os.dllSuffix; - }; - - # fill the templates, so that we can add the job - $pkgName = Fill-Template $netMonoPackageName $fill - $libPath = Fill-Template $netMonoLibPath $fill - $dllPath = Fill-Template $netMonoDllPath $fill - - # We always need to do a restore on Mono - $jobdotnet = $outdotnet | Select-Object -ExcludeProperty sdk,pgo -Property *,` - @{n='isMono';e={$true}},` - @{n='netMonoPkgName';e={$pkgName}},` - @{n='monoLibPath';e={$libPath}},` - @{n='monoDllPath';e={$dllPath}} - $jobdotnet.needsRestore = $true; - - $jobs += @( - [pscustomobject]@{ - title = ".NET Mono $($dotnet.netMonoPkgVer) $arch on $($os.name)"; - os = $outos; - dotnet = $jobdotnet | Remove-NullProperties; + }; + + # fill the templates, so that we can add the job + $pkgName = Fill-Template $netMonoPackageName $fill + $libPath = Fill-Template $netMonoLibPath $fill + $dllPath = Fill-Template $netMonoDllPath $fill + + # We always need to do a restore on Mono + $jobdotnet = $outdotnet | Select-Object -ExcludeProperty sdk,pgo -Property *,` + @{n='isMono';e={$true}},` + @{n='netMonoPkgName';e={$pkgName}},` + @{n='monoLibPath';e={$libPath}},` + @{n='monoDllPath';e={$dllPath}} + $jobdotnet.needsRestore = $true; + + $jobs += @( + [pscustomobject]@{ + title = ".NET Mono $($dotnet.netMonoPkgVer) $arch on $($os.name)"; + os = $outos; + dotnet = $jobdotnet | Remove-NullProperties; arch = $arch; - } + } ); } } diff --git a/.github/workflows/run-mono-tests.ps1 b/.github/workflows/run-mono-tests.ps1 index 331b68fc..7880b445 100644 --- a/.github/workflows/run-mono-tests.ps1 +++ b/.github/workflows/run-mono-tests.ps1 @@ -3,5 +3,5 @@ $useMdh = $env:USE_MDH -eq "true"; [string[]]$exeargs = if ($useMdh) { @($env:MONO_DLL) } else { @() }; # TODO: this xUnit TFM is incorrect for some targets. We'd really like to use vstest on .NET Mono, as that will keep things somewhat simpler. -&$exe @exeargs "$($env:NUGET_PACKAGES)/xunit.runner.console/$($env:XunitVersion)/tools/$($env:TFM)/xunit.console.exe" ` +&$exe @exeargs "$($env:NUGET_PACKAGES)/xunit.runner.console/$($env:XunitVersion)/tools/$($env:RUNNER_TFM)/xunit.console.exe" ` "release_$($env:TFM)/MonoMod.UnitTest.dll" -junit "$($env:LOG_FILE_NAME).xml" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25d74377..69da05c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,7 +78,8 @@ jobs: env: INPUT_JOB: ${{ inputs.matrix }} run: | - ./.github/setup-mono.ps1 $env:INPUT_JOB $env:GITHUB_OUTPUT $env:GITHUB_ENV ${{ runner.os }} + #./.github/setup-mono.ps1 $env:INPUT_JOB $env:GITHUB_OUTPUT $env:GITHUB_ENV ${{ runner.os }} + dotnet run --project ./build/setup-mono/setup-mono.csproj -- $env:INPUT_JOB $env:GITHUB_OUTPUT $env:GITHUB_ENV ${{ runner.os }} - name: Print SDK info run: dotnet --info @@ -108,6 +109,7 @@ jobs: if: ${{ fromJSON(inputs.matrix).dotnet.isMono }} env: TFM: ${{ fromJSON(inputs.matrix).dotnet.tfm }} + RUNNER_TFM: ${{ steps.setup_mono.outputs.runner_tfm }} USE_MDH: ${{ steps.setup_mono.outputs.use_mdh }} MDH: ${{ steps.setup_mono.outputs.mdh }} MONO_DLL: ${{ steps.setup_mono.outputs.mono_dll }} diff --git a/MonoMod.sln b/MonoMod.sln index 90dd6da6..ba2fdced 100644 --- a/MonoMod.sln +++ b/MonoMod.sln @@ -54,6 +54,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docs", "docs\docs.csproj", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "docfx", "docfx\docfx.csproj", "{0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{6C33A700-A18E-45F8-BECD-0DBCCD0AC456}" + ProjectSection(SolutionItems) = preProject + build\Directory.Build.props = build\Directory.Build.props + build\Directory.Build.targets = build\Directory.Build.targets + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "setup-mono", "build\setup-mono\setup-mono.csproj", "{945B8D38-9842-49BC-86E2-5228CF0834E8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -214,6 +222,14 @@ Global {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.Release|Any CPU.Build.0 = Release|Any CPU {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU + {945B8D38-9842-49BC-86E2-5228CF0834E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {945B8D38-9842-49BC-86E2-5228CF0834E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {945B8D38-9842-49BC-86E2-5228CF0834E8}.DebugTrace|Any CPU.ActiveCfg = DebugTrace|Any CPU + {945B8D38-9842-49BC-86E2-5228CF0834E8}.DebugTrace|Any CPU.Build.0 = DebugTrace|Any CPU + {945B8D38-9842-49BC-86E2-5228CF0834E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {945B8D38-9842-49BC-86E2-5228CF0834E8}.Release|Any CPU.Build.0 = Release|Any CPU + {945B8D38-9842-49BC-86E2-5228CF0834E8}.ReleaseTrace|Any CPU.ActiveCfg = ReleaseTrace|Any CPU + {945B8D38-9842-49BC-86E2-5228CF0834E8}.ReleaseTrace|Any CPU.Build.0 = ReleaseTrace|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -224,6 +240,7 @@ Global {179EC228-CED4-429E-934F-422C96273F74} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} {4A65448D-466F-4E87-9797-41F43787EFF3} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} {0BE9C4EB-1595-49E7-A2AA-09CA7E74C698} = {BCABFBF3-BB48-4C21-9A52-E140015570A9} + {945B8D38-9842-49BC-86E2-5228CF0834E8} = {6C33A700-A18E-45F8-BECD-0DBCCD0AC456} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E10A3D07-6D9A-4898-B95F-268636312A67} diff --git a/build/Directory.Build.props b/build/Directory.Build.props new file mode 100644 index 00000000..99fd7ded --- /dev/null +++ b/build/Directory.Build.props @@ -0,0 +1,31 @@ + + + + + + + net8.0 + enable + + $(NoWarn);CA2007;CA1308 + + false + false + false + false + false + false + false + false + + + + + + + + + + + + diff --git a/build/Directory.Build.targets b/build/Directory.Build.targets new file mode 100644 index 00000000..648fae40 --- /dev/null +++ b/build/Directory.Build.targets @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/build/setup-mono/Program.cs b/build/setup-mono/Program.cs new file mode 100644 index 00000000..6dd1d93c --- /dev/null +++ b/build/setup-mono/Program.cs @@ -0,0 +1,219 @@ +using NuGet.Common; +using NuGet.Configuration; +using NuGet.Frameworks; +using NuGet.PackageManagement; +using NuGet.Packaging; +using NuGet.Packaging.Signing; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; +using System.IO.Compression; +using System.Runtime.CompilerServices; + +if (args is not [{ } matrixJson, { } githubOutputFile, { } githubEnvFile, { } runnerOsName]) +{ + await StdErr.WriteLineAsync("takes 4 arguments: matrixJson, GITHUB_OUTPUT, GITHUB_ENV, runner.os"); + return 1; +} + +var jobInfo = FromJson(matrixJson, new +{ + arch = "", + dotnet = new + { + isMono = false, + systemMono = false, + tfm = "", + netMonoNugetSrc = (string?)null, + netMonoPkgName = (string?)null, + netMonoPkgVer = (string?)null, + monoLibPath = (string?)null, + monoDllPath = (string?)null, + } +}); + +if (jobInfo is null) +{ + await StdErr.WriteLineAsync("Job info was null"); + return 1; +} + +if (!jobInfo.dotnet.isMono) +{ + await StdOut.WriteLineAsync("Nothing needs to be done, job is not a Mono job"); + return 0; +} + +var ScriptRoot = GetScriptRoot(); +var RepoRoot = Path.GetFullPath(Path.Combine(ScriptRoot, "..", "..")); + +// resolve runner_tfm +var tfm = jobInfo.dotnet.tfm; +var ntfm = NuGetFramework.Parse(tfm); +var resolvedRunnerTfm = NuGetFrameworkUtility.GetNearest([ + // note: these are the TFMs in the /tools/ folder of xunit.runner.console that we use + // https://nuget.info/packages/xunit.runner.console/2.4.2 + "net452", + "net46", + "net461", + "net462", + "net47", + "net471", + "net472", + "netcoreapp1.0", + "netcoreapp2.0", +], ntfm, NuGetFramework.Parse); +// write out the target framework +await File.AppendAllLinesAsync(githubOutputFile, [ + $"runner_tfm={resolvedRunnerTfm}" +]); + +if (jobInfo.dotnet.systemMono) +{ + if (!TryWhich("mono", out var sysMonoPath)) + { + await StdErr.WriteLineAsync("Job is for system Mono, but could not find Mono on PATH"); + return 1; + } + + await StdOut.WriteLineAsync($"Job is for system Mono; using mono={sysMonoPath}"); + await File.WriteAllLinesAsync(githubOutputFile, [ + "use_mdh=false", + $"mono_dll={sysMonoPath}", + ]); + return 0; +} + +var pkgSrc = jobInfo.dotnet.netMonoNugetSrc; +var pkgName = jobInfo.dotnet.netMonoPkgName; +var pkgVer = jobInfo.dotnet.netMonoPkgVer; +var libPath = jobInfo.dotnet.monoLibPath; +var dllPath = jobInfo.dotnet.monoDllPath; + +if (pkgSrc is null || pkgName is null || pkgVer is null || libPath is null || dllPath is null) +{ + await StdErr.WriteLineAsync("Job info is missing some required properties"); + return 1; +} + +var monoDir = Path.Combine(RepoRoot, ".mono"); +var pkgDir = Path.Combine(monoDir, "pkg"); +Directory.CreateDirectory(monoDir); +Directory.CreateDirectory(pkgDir); + +// lets grab mdh +var mdhDir = Path.Combine(monoDir, "mdh"); +var mdhExe = Path.Combine(mdhDir, "mdh"); +{ + var mdhZip = Path.Combine(monoDir, "mdh.zip"); + + var archName = jobInfo.arch; + var osName = runnerOsName.ToLowerInvariant(); + + // fix up name for download + archName = archName switch + { + "x64" => "x86_64", + "arm64" => "aarch64", + var x => x, + }; + if (osName is "linux") osName = "linux-gnu.2.10"; + + // download the zip + var url = $"https://github.com/nike4613/mono-dynamic-host/releases/latest/download/{archName}-{osName}.zip"; + using (var file = File.Create(mdhZip)) + { + using var stream = await FetchStreamAsync(url); + await stream.CopyToAsync(file); + } + + // delete the existing extract if it exists + if (Directory.Exists(mdhDir)) + { + Directory.Delete(mdhDir, true); + } + // extract the archive + ZipFile.ExtractToDirectory(mdhZip, mdhDir); + + // select Windows executable if it exists + if (File.Exists(mdhExe + ".exe")) + { + mdhExe += ".exe"; + } + + // mark it as executable on non-windows + if (!Env.IsWindows) + { + await Run($"chmod +x {mdhExe}"); + } +} + +// load NuGet stuff +var nugetSettings = Settings.LoadDefaultSettings(RepoRoot); +var packageSourceProvider = new PackageSourceProvider(nugetSettings); +var packageManager = new NuGetPackageManager(new CachingSourceProvider(packageSourceProvider), nugetSettings, pkgDir); +var resolutionContext = new ResolutionContext(); + +// now lets try to grab the Mono package +{ + var packageSource = packageSourceProvider.GetPackageSourceByName(pkgSrc); + if (packageSource is null) + { + await StdErr.WriteLineAsync($"There is no package source with name '{pkgSrc}'"); + return 1; + } + + var dir = Path.Combine(pkgDir, pkgName + "." + pkgVer); + + var packageRepo = Repository.Factory.GetCoreV3(packageSource); + var pkgByIdResource = await packageRepo.GetResourceAsync(); + + var nupkg = Path.Combine(pkgDir, pkgName + ".nupkg"); + using (var file = File.Create(nupkg)) + { + using var cacheCtx = new SourceCacheContext(); + var result = await pkgByIdResource.CopyNupkgToStreamAsync( + pkgName, NuGetVersion.Parse(pkgVer), + file, cacheCtx, + NullLogger.Instance, default); + + if (!result) + { + await StdErr.WriteLineAsync($"Could not download {pkgName},{pkgVer}"); + return 1; + } + } + + using (var file = File.OpenRead(nupkg)) + { + await PackageExtractor.ExtractPackageAsync(packageSource.Source, + file, + new PackagePathResolver(pkgDir, true), + new PackageExtractionContext( + PackageSaveMode.Files, XmlDocFileSaveMode.Skip, + ClientPolicyContext.GetClientPolicy(nugetSettings, NullLogger.Instance), + NullLogger.Instance), + default); + } + + var fullLibPath = Path.GetFullPath(Path.Combine(dir, libPath)); + var fullDllPath = Path.GetFullPath(Path.Combine(dir, dllPath)); + + await StdOut.WriteLineAsync("Job is for .NET Mono"); + await StdOut.WriteLineAsync($"mdh={mdhExe}"); + await StdOut.WriteLineAsync($"mono_dll={fullDllPath}"); + await StdOut.WriteLineAsync($"MONO_PATH={fullLibPath}"); + + await File.AppendAllLinesAsync(githubOutputFile, [ + "use_mdh=true", + $"mdh={mdhExe}", + $"mono_dll={fullDllPath}", + ]); + await File.AppendAllLinesAsync(githubEnvFile, [ + $"MONO_PATH={fullLibPath}", + ]); +} + +return 0; + +static string GetScriptRoot([CallerFilePath] string path = "") => Path.GetDirectoryName(path) ?? "."; \ No newline at end of file diff --git a/build/setup-mono/setup-mono.csproj b/build/setup-mono/setup-mono.csproj new file mode 100644 index 00000000..136b73ee --- /dev/null +++ b/build/setup-mono/setup-mono.csproj @@ -0,0 +1,16 @@ + + + + Exe + SetupMono + + + + + + + + + + + diff --git a/tools/Common.CS.targets b/tools/Common.CS.targets index 4eccb1bc..ac4ba0a7 100644 --- a/tools/Common.CS.targets +++ b/tools/Common.CS.targets @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers