Use WASM CoreLib when compiling crossgen2 WASM unit tests - #131024
Use WASM CoreLib when compiling crossgen2 WASM unit tests#131024jtschuster wants to merge 20 commits into
Conversation
- Use CopyAllJitLibrariesToAotCompilerOutput=true instead of copying ourselves. - Set crossgenDir based on crossgen2_inbuild.csproj output items - Reorder properties and items to keep related items close to each other
…ter-wasm-r2r-crossgen-test
|
Azure Pipelines: Successfully started running 5 pipeline(s). 11 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR updates the ReadyToRun crossgen2 test runner and CI wiring so WASM-targeted R2R compilations can reference the browser-wasm runtime pack (including WASM System.Private.CoreLib) when it’s available, instead of always using the host runtime pack.
Changes:
- Add browser-wasm runtime-pack path plumbing (and an opt-in “require wasm references” switch) to the ILCompiler.ReadyToRun.Tests test infrastructure.
- Add a per-compilation target selector (
TargetRid) so tests can request browser-wasm (or host-arm) crossgen2 args and reference sets. - Update pipeline to build + publish browser-wasm managed runtime-pack artifacts and download them into the CLR tools test job.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.CoreCLR.sfxproj | Makes IncludeStaticLibrariesInPack overridable so CI can disable static libs for browser/wasi runtime-pack builds. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs | Simplifies path config and adds optional wasm runtime-pack locations + RequireWasmReferences switch. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs | Chooses host vs browser-wasm reference paths per compilation and passes reference paths into the resolver. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs | Changes Roslyn compilation reference setup to consume an explicit reference list. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs | Updates SimpleAssemblyResolver to resolve framework assemblies from a provided reference set rather than TestPaths. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs | Removes the dedicated “target arch arm” option in favor of TargetRid-driven args. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs | Updates wasm and arm test cases to use TargetRid and tightens arm test gating. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj | Adds wasm runtime-pack discovery/config propagation and CI-oriented validation/warnings. |
| eng/pipelines/runtime.yml | Builds browser-wasm libs for CLR tools tests, publishes them as artifacts, and downloads them into the tools test job. |
Comments suppressed due to low confidence (1)
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs:8
- System.Text.RegularExpressions is no longer used in this file after removing the config-probing helpers; leaving it will either produce an unnecessary compiler warning (CS8019) or fail builds that treat warnings as errors.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Xunit.Abstractions;
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (2)
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestCaseCompiler.cs:77
R2RTestCaseCompilernow compiles test assemblies withOptimizationLevel.Debug, which changes the IL shape (and potentially crossgen2 output/validations) and seems unrelated to the PR’s wasm-reference selection. Unless there’s a specific reason, keepingReleasehere avoids altering baseline test behavior.
new CSharpCompilationOptions(outputKind)
.WithOptimizationLevel(OptimizationLevel.Debug)
.WithAllowUnsafe(true)
.WithNullableContextOptions(NullableContextOptions.Enable));
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj:67
- MSBuild
Exists(...)checks should avoid hardcoding\path separators. On non-Windows builds this can cause false negatives (and in this case, the error is raised very early). Using/(orNormalizeDirectory) keeps the check portable.
<Error Condition="!Exists('$(CoreCLRArtifactsPath)\$(BuildArchitecture)\crossgen2')"
Text="Crossgen2 inbuild publish directory '$(CoreCLRArtifactsPath)\$(BuildArchitecture)\crossgen2' does not exist. This is a build authoring error and should be generated in the test project build." />
|
Azure Pipelines: Successfully started running 5 pipeline(s). 11 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj:67
- The ValidatePathsExist target builds a path using backslashes (e.g. '$(CoreCLRArtifactsPath)$(BuildArchitecture)\crossgen2'). On non-Windows agents this can become a literal backslash in the path and cause a false failure even when the directory exists. Use forward slashes or NormalizeDirectory/NormalizePath to keep the Exists check cross-platform.
<Error Condition="!Exists('$(CoreCLRArtifactsPath)\$(BuildArchitecture)\crossgen2')"
Text="Crossgen2 inbuild publish directory '$(CoreCLRArtifactsPath)\$(BuildArchitecture)\crossgen2' does not exist. This is a build authoring error and should be generated in the test project build." />
| # Depend on the browser-wasm libraries built below so the wasm crossgen2 R2R test | ||
| # (ILCompiler.ReadyToRun.Tests) can compile against real browser-wasm references. | ||
| dependsOn: | ||
| - build_browser_wasm_linux_Release_CLR_Tools_Tests_WasmLibs |
There was a problem hiding this comment.
Instead of all this hackery and optional WASM builds, can we add a separate ToolsTests leg (ReadyToRunTests) that simply builds the runtime as -arch wasm and then one more leg that builds as -arch arm?
We might want to move ILCompiler.ReadyToRun.Tests out of toolstests as part of that since none of the other toolstests are actually architecture specific and it's a waste of time to run them.
I believe we'll not need any of the project file hackery if we do that because CoreLib that is passed to the tests will simply be the correct corelib. The attributes in the tests can still decide whether the test should run or not.
There was a problem hiding this comment.
can we add a separate ToolsTests leg (ReadyToRunTests) that simply builds the runtime as -arch wasm
I don't think we'd be able to shell out to crossgen if we run ILCompiler.ReadyToRun.Tests on browser-wasm, would we?
Or am I misunderstanding what you mean?
There was a problem hiding this comment.
I don't think we'd be able to shell out to crossgen if we run ILCompiler.ReadyToRun.Tests on browser-wasm, would we?
Why not? ILCompiler.ReadyToRun.Tests is already using the crossgen2_inbuild.csproj project - that's the build machine hosted flavor of crossgen2. We build two flavors of crossgen: crossgen2.csproj - this is the one that is built for the target: so if you're building -arch arm64, this crossgen will only work on an arm64 machine. Then there is crossgen2_inbuild.csproj: that's the one that works on the build machine and ILCompiler.ReadyToRun.Tests is using. The tests need to build for the same flavor as crossgen2_inbuild.csproj that they use.
There was a problem hiding this comment.
I moved the test to a new clr.r2rtests subset, and separated it into a job that downloads artifacts from previous arm32 and wasm builds. It still runs the tests on linux-x64, though, so I left the CopyAllJitLibrariesToAotCompilerOutput property.
I think removing the host libs fallback and separating the tests into a run for each architecture that the tests build for would trade off a simpler build file for worse inner loop experience (if you have to build 3 different runtimes and run dotnet test 3 times to get full validation), and I see this project as particularly helpful for inner loop validation.
There was a problem hiding this comment.
I think removing the host libs fallback and separating the tests into a run for each architecture that the tests build for would trade off a simpler build file for worse inner loop experience (if you have to build 3 different runtimes and run
dotnet test3 times to get full validation), and I see this project as particularly helpful for inner loop validation.
If I'm working on a WASM change, I'd expect to do a single build.cmd -arch wasm build, use the crossgen2_inbuild.csproj project and associated unit test (all running on top of the bits created by build.cmd -arch wasm) and nothing else. I'd leave arm32 or x64 specific testing to the CI since I'm not touching it and don't expect to break or having to troubleshoot it.
If the problem is that ILCompiler.ReadyToRun.Tests are currently built for the target (i.e. wasm) and we need one more build.cmd without -arch wasm to be able to execute the unit tests, the fix should be to switch the unit tests to build same as crossgen2_inbuild.csproj - i.e. for the build host architecture - it is the same flavor that the test itself invokes crossgen2 for after all.
I shouldn't have to build for x64 Linux/Windows if I'm working on WASM at all.
What you have here will work. I feel it's more complicated than it needs to be.
There was a problem hiding this comment.
I shouldn't have to build for x64 Linux/Windows if I'm working on WASM at all.
(The reason for this is that -arch wasm will already build a non-WASM crossgen that is suitable to execute on the build machine as part of building crosstools. This is basically about how we run the tests on crosstools. The unit test already depends on crossgen2_inbuild.csproj - the crosstool. The unit test does not invoke crossgen2.csproj - the target-flavored crossgen,)
There was a problem hiding this comment.
I think I understand what you're getting at and modified the tests to run on the host machine and target the TargetOS and TargetArch. I've also updated the description to reflect the code changes. Is this in line with what you were thinking?
…ter-wasm-r2r-crossgen-test
Adds a fourth CLR_R2R_Tests leg that compiles the crossgen2 test suite for ios-arm64, exercising the Apple ARM64 ABI codegen path. The same input produces a different R2R image for --targetos ios (74,752 bytes) than for --targetos linux (74,240 bytes) at the same arch, so this is not redundant with the existing linux-arm leg. The leg runs on a linux_x64 agent with no Apple SDK. Unlike the other three legs it builds its inputs from source rather than downloading a prebuilt artifact, because the iOS CoreCLR jobs run on macOS pools and their cross components are Mach-O. Two details make this work: - clr.crossarchtools targets the build host, not the build target. build-runtime.sh passes __HostOS as build_native's targetOS, so the cross components configure for linux and land in ios.arm64.Checked/x64/. libs.sfx is used rather than libs, since libs.native does need the Apple SDK. - The subsets are split across two build invocations. clr.r2rtests is declared ahead of libs.sfx in Subsets.props, and Build.proj is a Traversal over ProjectToBuild in declaration order, so a single invocation would run the tests before the shared framework exists. Also drops the hardcoded --targetos/--targetarch overrides from the two StripILBodies test cases so they compile for the build's own target, and gates AppleMobileStripILBodiesUsesFixedInstructionSet on ios-arm64. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs:29
- The SystemPrivateCoreLibPath comment still describes probing the runtime pack native/ directory and CoreCLR artifacts, but the implementation now always points at the staged "libraries/" directory next to the test output. Updating the comment will avoid confusion when the build errors mention missing staged references.
/// <summary>
/// Path to System.Private.CoreLib.dll. It lives in the runtime pack native/ dir in full builds
/// (placed by externals.csproj BinPlace during libs.pretest), but partial builds that skip
/// libs.pretest only have it in the CoreCLR artifacts directory.
/// </summary>
public static string SystemPrivateCoreLibPath => Path.Combine(LibrariesDir, "System.Private.CoreLib.dll");
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs:544
- This test's doc comment says it is Windows-only because it relies on Windows PDB generation (a host constraint). Using TestPaths.IsWindowsTarget (target OS) can cause the test to run on non-Windows hosts when cross-targeting Windows, which is unlikely to work reliably. Prefer gating on the host OS here.
[ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindowsTarget))]
public void CompositeManifestSectionsArePaddedWhenPdbPresent()
{
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs:13
- The class summary says all paths come from RuntimeHostConfigurationOption items, but this is no longer true now that Crossgen2Exe/LibrariesDir are derived from files staged next to the test output (AppContext.BaseDirectory). This comment is now misleading for anyone diagnosing missing assets.
This issue also appears on line 24 of the same file.
/// <summary>
/// Provides paths to build artifacts needed by the test infrastructure.
/// All paths come from RuntimeHostConfigurationOption items in the csproj.
/// </summary>
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs:452
- These two tests are documented as "Windows-only because it relies on Windows PDB generation", which is a host capability. Switching the skip condition from the host OS to the build target OS can enable this test on non-Windows hosts when targeting Windows, which is likely to fail in confusing ways. Consider gating on the host OS here (as the prior implementation did).
This issue also appears on line 542 of the same file.
[ConditionalFact(typeof(TestPaths), nameof(TestPaths.IsWindowsTarget))]
public void CompositeManifestAssemblyMvidsArePaddedWhenPdbPresent()
{
The two CompositeManifest*ArePaddedWhenPdbPresent cases were switched from a host-OS gate to a target-OS gate when the suite became per-target. Both constraints are actually required, for different reasons: - crossgen2.props stages Microsoft.DiaSymReader.Native only when crossgen2's own target is Windows, so a non-Windows target never has the library at all. - That library is a Windows binary, so even when staged it can only load on a Windows host. Compiling with --pdb on Linux fails with a DllNotFoundException before writing anything. Gate on both. Gating on the target alone (as before this commit) would let the tests run on a Linux host and fail; gating on the host alone would let them run on a Windows host cross-targeting wasm or iOS, where the library is absent. With every leg running on a Linux host, these two cases were being skipped everywhere, so add a windows-x64 leg to restore the coverage. It also gives the per-target test infrastructure its first Windows run, covering the command-line length limit that the reference-argument wildcard exists for. Also refresh two stale doc comments in TestPaths that still described the pre-per-target artifact probing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/WasmArgumentLayoutTests.cs:162
- CreateWasmContext always configures a wasm32/browser target, but it now unconditionally pulls System.Private.CoreLib.dll from TestPaths.SystemPrivateCoreLibPath (the staged CoreLib for the outer build target RID). This means the wasm layout tests will still run on non-wasm test legs (e.g., linux-x64/windows-x64) using a non-wasm CoreLib, which undermines the PR goal of ensuring wasm compilations use wasm CoreLib (and will reintroduce the original risk if wasm-only members become required).
Consider either (1) gating this test class/methods to run only when TargetArchitecture == "wasm" (e.g., ConditionalTheory/ConditionalFact), or (2) staging/locating a browser-wasm CoreLib specifically for these tests even when the outer target isn’t wasm.
private ReadyToRunCompilerContext CreateWasmContext()
{
string coreLibPath = TestPaths.SystemPrivateCoreLibPath;
Assert.True(File.Exists(coreLibPath), $"System.Private.CoreLib.dll not found at '{coreLibPath}'");
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs:73
- PR description mentions a local-development fallback to a host-targeting CoreLib with a warning, but TestPaths now hard-throws when the staged 'libraries' directory is missing. If the fallback behavior is still desired, the path resolution logic likely needs to be reintroduced (or the PR description adjusted).
string dir = Path.Combine(AppContext.BaseDirectory, "libraries");
if (!Directory.Exists(dir))
{
throw new DirectoryNotFoundException(
$"No shared framework assemblies staged for {TargetRid} at '{dir}'. Build the clr and " +
The class builds a wasm type system context unconditionally, but reads System.Private.CoreLib from whatever the outer build targets. On a non-wasm leg it was therefore computing wasm argument layouts from an ios-arm64 or linux-x64 corelib, which passes without testing anything meaningful. ConditionalClass filters the whole class at discovery, so the 20 theory cases no longer appear at all outside the wasm leg (59 -> 39 cases there) and still run and pass on it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ter-wasm-r2r-crossgen-test
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs:206
- BuildReferencePaths() enumerates the staged libraries directory but it’s currently called twice per test-case run: once in Run() (for validation/resolution) and again inside CompileAllAssemblies() (for Roslyn references). This duplicates filesystem I/O and reference construction; it should be computed once and reused for both steps (and ideally only computed if needed).
var assemblyPaths = CompileAllAssemblies(assembliesToCompile);
// Step 2: Run each crossgen2 compilation and validate
var driver = new R2RDriver(_output);
List<string> refPaths = BuildReferencePaths();
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/TestPaths.cs:56
- TestPaths now hard-fails if the staged crossgen2/libraries assets aren’t present. The PR description mentions a local-development fallback to a host-targeting CoreLib with a warning, but there’s no warning/fallback path here—only exceptions. Either implement the intended fallback/probing behavior (and emit a clear warning), or update the PR narrative/comments to match the enforced prerequisites.
string exe = Path.Combine(AppContext.BaseDirectory, "crossgen2", Crossgen2ExeName);
if (!File.Exists(exe))
{
throw new FileNotFoundException(
$"No crossgen2 staged for {TargetRid} at '{exe}'. Build the clr and libs subsets for " +
$"{TargetRid}, then rebuild this test project.");
}
…uildArgs The R2R test legs all restated -os/-arch in buildArgs even though global-build-step.yml already passes $(_osParameter)/$(_archParameter) to the build script, so every leg emitted both and relied on last-wins argument parsing. Override those two variables per leg instead. The job-level variables list is expanded after global-build-job.yml defines its defaults, so a later definition wins - the same mechanism global-build-job.yml already uses to redefine _osParameter for linux-bionic. The iOS leg gets both of its build invocations covered by the one override. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| <ItemGroup> | ||
| <_SharedFrameworkAssembly Include="$(LibrariesSharedFrameworkBinArtifactsPath)*.dll" /> | ||
| <_SharedFrameworkAssembly Include="$(CoreCLRArtifactsPath)IL/System.Private.CoreLib.dll" /> | ||
| </ItemGroup> |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs:58
- Crossgen2's option name for input bubble is "--inputbubble" (see src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs), but this enum maps InputBubble to "--input-bubble". If a test ever uses Crossgen2Option.InputBubble, crossgen2 argument parsing will fail.
public static string ToArg(this Crossgen2Option kind) => kind switch
{
Crossgen2Option.Composite => $"--composite",
Crossgen2Option.InputBubble => $"--input-bubble",
Crossgen2Option.HotColdSplitting => $"--hot-cold-splitting",
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/ILCompiler.ReadyToRun.Tests.csproj:10
- This test csproj dropped properties that are consistently present in other CoreCLR AOT tool test projects (e.g., ILCompiler.Trimming.Tests.csproj and ILCompiler.Compiler.Tests.csproj): IsPackable=false and CopyLocalLockFileAssemblies=true. Keeping them avoids accidentally packing test projects and ensures runtime/package dependencies are available in the test output layout used by runtime's build/test infrastructure.
<PropertyGroup>
<AssemblyName>ILCompiler.ReadyToRun.Tests</AssemblyName>
<TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
<Nullable>enable</Nullable>
<TestRunnerAdditionalArguments>-notrait category=failing</TestRunnerAdditionalArguments>
ILCompiler.ReadyToRun.Tests uses the corelib for the build machine to compile for wasm. If crossgen ever takes a dependency on a wasm-only member being present in corelib, it could cause failures. The same issue is possible for tests targeting windows, arm, or ios-arm64.
Add a new clr.r2rtests subset to separate the tests from platform-independent clr.toolstests.
The ILCompiler.ReadyToRun.Tests now runs only tests that are compatible with the TargetArchitecture and TargetOS set in msbuild. Those properties are passed to the test via RuntimeConfiguration options. Unfortunately, this means that getting full coverage in a local run requires building libs for multiple targets and re-running the test for each target RID you want tests for.
The libraries and crossgen2 are now copied directly to the test bin directory.
Add jobs to runtime.yml to run r2rtests for each target we have tests for. Only the compiler input assets are platform-dependent - the runner itself compiles for AnyCPU and crossgen2 can cross-compile, so (most) tests can still run on linux-x64 after downloading the artifacts from previous builds (for iOS it builds the artifacts since there isn't a prior build that uploads the right artifacts). However, there are 2 regression tests which require --pdb to repro and is Windows only. For that reason, a Windows-targeting job runs on Windows pipeline machines.
We also could consider moving all but linux-x64 to the
runtime-coreclr crossgen2pipeline if these end up taking too long.