Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions eng/Subsets.props
Original file line number Diff line number Diff line change
Expand Up @@ -761,13 +761,9 @@
<ProjectToBuild Condition="'$(RuntimeFlavor)' != 'Mono' and ('$(TargetsMobile)' != 'true' or '$(TargetOS)' == 'browser') and '$(TargetsLinuxBionic)' != 'true' and '$(BuildHostTools)' == 'true'" Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Crossgen2.Host.sfxproj" Category="packs" />

<!--
The wasm-tools workload manifest declares the host crossgen2 pack, so the workload
testing legs need it in their local package feed. Those legs do not set
$(BuildHostTools), so the project above does not produce one for them. The Host variant
pins the RID to the build host, which is exactly the pack the workload resolves.

Opt-in only. The official build already publishes this pack from the host platform
legs, and building it here as well would produce a second package with the same id.
CoreCLR wasm workload tests need the host crossgen2 package in their local feed.
These legs do not set BuildHostTools, so opt in here. Keeping this opt-in avoids
duplicating packages produced by official host builds.
-->
<ProjectToBuild Condition="'$(BuildCrossgen2HostPackForWorkloadTesting)' == 'true' and '$(RuntimeFlavor)' != 'Mono' and '$(BuildHostTools)' != 'true'" Include="$(InstallerProjectRoot)pkg\sfx\Microsoft.NETCore.App\Microsoft.NETCore.App.Crossgen2.Host.sfxproj" Category="packs" />
</ItemGroup>
Expand Down
18 changes: 0 additions & 18 deletions src/installer/pkg/sfx/Microsoft.NETCore.App/Crossgen2/Sdk.props

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
OutputItemType="_RawCrossgenPublishFiles"
ReferenceOutputAssembly="false"
Private="false" />

<!-- Lets a workload acquire this pack and find the tool with an MSBuild Sdk import. -->
<FilesToPackage Include="$(MSBuildThisFileDirectory)Crossgen2\Sdk.props"
TargetPath="Sdk/" />
</ItemGroup>

<Target Name="PublishCrossgen"
Expand Down
24 changes: 18 additions & 6 deletions src/mono/browser/build/BrowserWasmApp.CoreCLR.targets
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
native.wasm.targets uses RuntimeFlavor to gate the CoreCLR-only relink registry. -->
<RuntimeFlavor Condition="'$(RuntimeFlavor)' == ''">CoreCLR</RuntimeFlavor>

<!-- Request crossgen2 here: WebAssembly.Pack is not imported before the first restore.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means the crossgen2 will be required only when the workload is installed, but according to discussion with Pavel, R2R should work even without workload. Is that true?

If that's the case, we need to more this to dotnet/sdk where root skeleton for the SDK lives. Putting it there results in requesting the crossgen2 before nuget restore.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R2R should work even without workload. Is that true?

still yes

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is independent from R2R compilation and this path is generator specific. R2R compilation adds the pack when ReadyToRunEnabled && ReadyToRunUseCrossgen2. https://github.com/dotnet/sdk/blob/02d39b6a25a62e5ac313ab8449704c4428fa68f3/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs#L482

The relinking with generator currently needs workload as it still produces C files.

Only skip an explicit opt-out; otherwise native relinking may be inferred later. -->
<RequiresCrossgen2Pack Condition="'$(RequiresCrossgen2Pack)' == '' and '$(WasmBuildNative)' != 'false'">true</RequiresCrossgen2Pack>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It's more common to compare '$(WasmBuildNative)' == 'true'


<!-- EH and SIMD are always enabled for CoreCLR — detect user overrides before forcing true -->
<_CoreCLRInvalidEH Condition="'$(WasmEnableExceptionHandling)' != '' and '$(WasmEnableExceptionHandling)' != 'true'">true</_CoreCLRInvalidEH>
<_CoreCLRInvalidSIMD Condition="'$(WasmEnableSIMD)' != '' and '$(WasmEnableSIMD)' != 'true'">true</_CoreCLRInvalidSIMD>
Expand Down Expand Up @@ -640,6 +644,15 @@

<!-- ======================== Generate Managed-to-Native ======================== -->

<!-- Check overrides after all imports; the SDK resolver cannot use the in-repo crossgen2 layout.
Keep CallTarget separate so its output items reach the generator. -->
<Target Name="_CoreCLRResolvePortableCallHelpersGenerator"
BeforeTargets="_CoreCLRGenerateManagedToNative"
DependsOnTargets="ResolveFrameworkReferences">
<CallTarget Targets="ResolveReadyToRunCompilers"
Condition="'$(Crossgen2Path)' == '' and '$(Crossgen2InBuildDir)' == '' and '$(Crossgen2ToolPath)' == '' and '@(ResolvedCrossgen2Pack)' != ''" />
</Target>

<Target Name="_CoreCLRGenerateManagedToNative">
<PropertyGroup>
<_WasmPInvokeTablePath>$(_WasmIntermediateOutputPath)callhelpers-pinvoke.cpp</_WasmPInvokeTablePath>
Expand Down Expand Up @@ -694,21 +707,20 @@
<_WasmManagedAssemblies Include="$(_CoreLibPath)" />
</ItemGroup>

<!-- crossgen2 has the type system that knows the wasm ABI, so it generates the helpers itself.
In the repo it comes from the build output; outside it, from the crossgen2 pack
the wasm-tools workload acquires, which sets $(Crossgen2ToolPath) from its Sdk.props. -->
<!-- crossgen2 provides the wasm type layout needed to generate call helpers. -->
<PropertyGroup>
<_Crossgen2ExeSuffix Condition="'$(OS)' == 'Windows_NT'">.exe</_Crossgen2ExeSuffix>
<Crossgen2Path Condition="'$(Crossgen2Path)' == '' and '$(Crossgen2InBuildDir)' != ''">$([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(_Crossgen2ExeSuffix)'))</Crossgen2Path>
<Crossgen2Path Condition="'$(Crossgen2Path)' == ''">$(Crossgen2ToolPath)</Crossgen2Path>
<Crossgen2Path Condition="'$(Crossgen2Path)' == ''">@(Crossgen2Tool)</Crossgen2Path>
</PropertyGroup>

<Error Condition="'$(Crossgen2Path)' == ''"
Text="crossgen2 was not found, so the portable call helpers cannot be generated. Install the 'wasm-tools' workload, or set %24(Crossgen2Path) to a crossgen2 executable." />
Text="Could not resolve crossgen2. Update the .NET SDK and restore the project, or set %24(Crossgen2Path) to a crossgen2 executable." />
<Error Condition="'$([System.IO.Path]::GetExtension(`$(Crossgen2Path)`))' == '.dll'"
Text="%24(Crossgen2Path) is '$(Crossgen2Path)'. It has to name a crossgen2 executable; an IL assembly cannot be launched directly." />
Text="Set %24(Crossgen2Path) to a crossgen2 executable, not the DLL '$(Crossgen2Path)'." />
<Error Condition="!Exists('$(Crossgen2Path)')"
Text="%24(Crossgen2Path) is '$(Crossgen2Path)', which does not exist. In the repo, build crossgen2 first; outside it, install the 'wasm-tools' workload." />
Text="crossgen2 executable not found: '$(Crossgen2Path)'. Build or restore crossgen2, or correct %24(Crossgen2Path)." />

<PropertyGroup>
<_Crossgen2GeneratorRsp>$(_WasmIntermediateOutputPath)callhelpers-generator.rsp</_Crossgen2GeneratorRsp>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
"Microsoft.NET.Sdk.WebAssembly.Pack.${NetVersion}",
"Microsoft.NETCore.App.Runtime.Mono.${NetVersion}.browser-wasm",
"Microsoft.NETCore.App.Runtime.${NetVersion}.browser-wasm",
"Microsoft.NETCore.App.Runtime.AOT.Cross.${NetVersion}.browser-wasm",
"Microsoft.NETCore.App.Crossgen2.${NetVersion}"
"Microsoft.NETCore.App.Runtime.AOT.Cross.${NetVersion}.browser-wasm"
],
"extends": [ "microsoft-net-runtime-mono-tooling", "microsoft-net-sdk-emscripten" ],
"platforms": [ "win-x64", "win-arm64", "linux-x64", "linux-arm64", "linux-musl-x64", "linux-musl-arm64", "osx-x64", "osx-arm64" ]
Expand Down Expand Up @@ -453,20 +452,6 @@
"osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-arm64.Cross.wasi-wasm"
}
},
"Microsoft.NETCore.App.Crossgen2.${NetVersion}": {
"kind": "Sdk",
"version": "${PackageVersion}",
"alias-to": {
"win-x64": "Microsoft.NETCore.App.Crossgen2.win-x64",
"win-arm64": "Microsoft.NETCore.App.Crossgen2.win-arm64",
"linux-x64": "Microsoft.NETCore.App.Crossgen2.linux-x64",
"linux-arm64": "Microsoft.NETCore.App.Crossgen2.linux-arm64",
"linux-musl-x64": "Microsoft.NETCore.App.Crossgen2.linux-musl-x64",
"linux-musl-arm64": "Microsoft.NETCore.App.Crossgen2.linux-musl-arm64",
"osx-x64": "Microsoft.NETCore.App.Crossgen2.osx-x64",
"osx-arm64": "Microsoft.NETCore.App.Crossgen2.osx-arm64"
}
},
"Microsoft.NETCore.App.Runtime.Mono.${NetVersion}.browser-wasm" : {
"kind": "framework",
"version": "${PackageVersion}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,18 +212,11 @@
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Runtime.MonoTargets.Sdk.${NetVersion}" />
</ImportGroup>

<!-- CoreCLR browser-wasm: only needs the WebAssembly.Sdk (build targets + emscripten).
No MonoTargets.Sdk, no MonoAOTCompiler, no AOT.Cross pack — the SDK resolves
the CoreCLR runtime pack via its own KnownRuntimePack.

crossgen2 is imported for its type system, not to compile ReadyToRun images: it generates the
wasm P/Invoke call helpers, which encode struct sizes that cannot be derived from metadata
alone. The SDK only resolves its own KnownCrossgen2Pack when PublishReadyToRun is set, so the
pack is acquired through the workload instead. -->
<!-- CoreCLR uses WebAssembly.Sdk for native builds, without Mono tooling.
The .NET SDK resolves the runtime and crossgen2 packs. -->
<ImportGroup Condition="'$(TargetsCurrent)' == 'true' and '$(RuntimeIdentifier)' == 'browser-wasm' and '$(UsingBrowserRuntimeWorkload)' == 'true' and '$(UseMonoRuntime)' == 'false'">
<Import Project="Sdk.props" Sdk="Microsoft.NET.Runtime.WebAssembly.Sdk.${NetVersion}" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Runtime.WebAssembly.Sdk.${NetVersion}" />
<Import Project="Sdk.props" Sdk="Microsoft.NETCore.App.Crossgen2.${NetVersion}" />
</ImportGroup>

<ImportGroup Condition="'$(TargetsCurrent)' == 'true' and '$(RuntimeIdentifier)' == 'wasi-wasm' and '$(UsingWasiRuntimeWorkload)' == 'true'">
Expand Down
24 changes: 16 additions & 8 deletions src/mono/wasi/build/WasiApp.CoreCLR.targets
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<PropertyGroup>
<WasmAppRuntimeFlavor>CoreCLR</WasmAppRuntimeFlavor>
<UseMonoRuntime>false</UseMonoRuntime>
<RequiresCrossgen2Pack Condition="'$(RequiresCrossgen2Pack)' == ''">true</RequiresCrossgen2Pack>
<!-- The Mono per-app clang relink (WasmBuildNative) is not used; CoreCLR links the
wasihost corehost (libWasiHost.a) per-app through its own targets below. -->
<WasmBuildNative>false</WasmBuildNative>
Expand Down Expand Up @@ -86,6 +87,16 @@

<!-- ============================ Per-app wasihost link ============================ -->

<!-- Check overrides after all imports; the SDK resolver cannot use the in-repo crossgen2 layout.
Keep CallTarget separate so its output items reach the relink target. -->
<Target Name="_WasiResolvePortableCallHelpersGenerator"
BeforeTargets="_WasiCoreCLRRelinkCoreRun"
Condition="'$(WasmGenerateAppBundle)' == 'true' and '$(WasmSingleFileBundle)' != 'true'"
DependsOnTargets="ResolveFrameworkReferences">
<CallTarget Targets="ResolveReadyToRunCompilers"
Condition="'$(Crossgen2Path)' == '' and '$(Crossgen2InBuildDir)' == '' and '$(Crossgen2ToolPath)' == '' and '@(ResolvedCrossgen2Pack)' != ''" />
</Target>

<Target Name="_WasiCoreCLRRelinkCoreRun"
AfterTargets="_WasiCopyFrameworkToManaged"
DependsOnTargets="_SetupWasiSdk"
Expand Down Expand Up @@ -152,23 +163,20 @@
<Output TaskParameter="ManagedAssemblies" ItemName="_WasiManagedAssemblies" />
</FilterManagedAssemblies>

<!-- crossgen2 has the type system that knows the wasm ABI, so it generates the helpers itself.
In the repo it comes from the build output; outside it, from a crossgen2 pack
that sets $(Crossgen2ToolPath). The wasi-experimental workload does not acquire that pack
today, so out-of-repo wasi relink needs $(Crossgen2Path) or
$(Crossgen2ToolPath) to be set explicitly. -->
<!-- crossgen2 provides the wasm type layout needed to generate call helpers. -->
<PropertyGroup>
<_Crossgen2ExeSuffix Condition="'$(OS)' == 'Windows_NT'">.exe</_Crossgen2ExeSuffix>
<Crossgen2Path Condition="'$(Crossgen2Path)' == '' and '$(Crossgen2InBuildDir)' != ''">$([MSBuild]::NormalizePath('$(Crossgen2InBuildDir)', 'crossgen2$(_Crossgen2ExeSuffix)'))</Crossgen2Path>
<Crossgen2Path Condition="'$(Crossgen2Path)' == ''">$(Crossgen2ToolPath)</Crossgen2Path>
<Crossgen2Path Condition="'$(Crossgen2Path)' == ''">@(Crossgen2Tool)</Crossgen2Path>
</PropertyGroup>

<Error Condition="'$(Crossgen2Path)' == ''"
Text="crossgen2 was not found, so the portable call helpers cannot be generated. Set %24(Crossgen2Path) to a crossgen2 executable." />
Text="Could not resolve crossgen2. Update the .NET SDK and restore the project, or set %24(Crossgen2Path) to a crossgen2 executable." />
<Error Condition="'$([System.IO.Path]::GetExtension(`$(Crossgen2Path)`))' == '.dll'"
Text="%24(Crossgen2Path) is '$(Crossgen2Path)'. It has to name a crossgen2 executable; an IL assembly cannot be launched directly." />
Text="Set %24(Crossgen2Path) to a crossgen2 executable, not the DLL '$(Crossgen2Path)'." />
<Error Condition="!Exists('$(Crossgen2Path)')"
Text="%24(Crossgen2Path) is '$(Crossgen2Path)', which does not exist. In the repo, build crossgen2 first; outside it, set it to a crossgen2 executable." />
Text="crossgen2 executable not found: '$(Crossgen2Path)'. Build or restore crossgen2, or correct %24(Crossgen2Path)." />

<PropertyGroup>
<_Crossgen2GeneratorRsp>$(_WasiRelinkObjDir)callhelpers-generator.rsp</_Crossgen2GeneratorRsp>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,72 @@ public void PublishReadyToRunDirectoryMatchesSdkOutputCasing()
Assert.Equal(Path.Combine(GetObjDir(config), "R2R") + Path.DirectorySeparatorChar, match.Groups[1].Value);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void NativeRelinkResolvesCrossgen2WithoutReadyToRun(bool publish)
{
ProjectInfo info = CopyTestAsset(
Configuration.Debug,
aot: false,
TestAsset.WasmBasicTestApp,
"coreclr_sdk_crossgen2",
extraProperties: """
<PublishReadyToRun>false</PublishReadyToRun>
<WasmBuildNative>true</WasmBuildNative>
""",
insertAtEnd: $$"""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Please refactor it to a targets file linked in the csproj to improve maintainability. For example as NativeRelinkResolvesCrossgen2WithoutReadyToRun.Build.targets that gets copied into the testapp in this testcase.

<Target Name="CheckCrossgen2PackRequest" BeforeTargets="ProcessFrameworkReferences">
<Error Condition="'$(RequiresCrossgen2Pack)' != 'true'"
Text="Expected RequiresCrossgen2Pack=true before restore." />
</Target>
<Target Name="CheckSdkCrossgen2" AfterTargets="_CoreCLRGenerateManagedToNative">
<Error Condition="'{{publish}}' == 'true' and '$(WasmBuildingForNestedPublish)' != 'true'"
Text="Expected helper generation during nested publish." />
<Error Condition="'$(PublishReadyToRun)' == 'true'"
Text="Expected PublishReadyToRun=false." />
<Error Condition="'$(Crossgen2InBuildDir)' != '' or '$(Crossgen2ToolPath)' != ''"
Text="Expected SDK resolution without crossgen2 path overrides." />
<Error Condition="'@(Crossgen2Tool)' == '' or '$(Crossgen2Path)' != '@(Crossgen2Tool)'"
Text="Expected the generator path to match the SDK Crossgen2Tool item." />
<Error Condition="!Exists('$(_WasmPInvokeTablePath)') or !Exists('$(_WasmReversePInvokeTablePath)') or !Exists('$(_WasmInterpToNativeTablePath)')"
Text="A generated call-helper table is missing." />
<Error Text="Stopping after validating SDK crossgen2" />
</Target>
""");

// Run the generator, then stop before native compilation.
string output = publish
? PublishProject(info, Configuration.Debug, new PublishOptions(ExpectSuccess: false)).buildOutput
: BuildProject(info, Configuration.Debug, new BuildOptions(ExpectSuccess: false)).buildOutput;

Assert.Contains("Stopping after validating SDK crossgen2", output);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void NativeRelinkWithoutCrossgen2PackReportsMissingGenerator(bool publish)
{
ProjectInfo info = CopyTestAsset(
Configuration.Debug,
aot: false,
TestAsset.WasmBasicTestApp,
"coreclr_missing_crossgen2",
extraProperties: """
<PublishReadyToRun>false</PublishReadyToRun>
<RequiresCrossgen2Pack>false</RequiresCrossgen2Pack>
<WasmBuildNative>true</WasmBuildNative>
""");

string output = publish
? PublishProject(info, Configuration.Debug, new PublishOptions(ExpectSuccess: false)).buildOutput
: BuildProject(info, Configuration.Debug, new BuildOptions(ExpectSuccess: false)).buildOutput;

Assert.Contains("Could not resolve crossgen2. Update the .NET SDK and restore the project, or set $(Crossgen2Path) to a crossgen2 executable.", output);
Assert.DoesNotContain("NETSDK1094", output);
}

private string? BuildAndGetWasmBuildNativeLine(string projectPrefix, string extraProperties, bool expectSuccess)
=> BuildAndGetOutput(projectPrefix, extraProperties, extraItems: "", expectSuccess).line;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,15 @@ private static void AddCoreClrProjectProperties(ref string extraProperties, ref
""";
insertAtEnd +=
$$"""
<Target Name="_UpdateKnownWebAssemblySdkPack" BeforeTargets="ProcessFrameworkReferences"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This target is already removed on main. The packs are correctly resolved from the installed workload in dotnet-latest

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still see it on main

<Target Name="_UpdateKnownWebAssemblySdkPack" BeforeTargets="ProcessFrameworkReferences"
Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<ItemGroup>
<KnownWebAssemblySdkPack Update="@(KnownWebAssemblySdkPack)">
<WebAssemblySdkPackVersion Condition="'%(KnownWebAssemblySdkPack.TargetFramework)' == '{{DefaultTargetFramework}}'">{{runtimePackVersion}}</WebAssemblySdkPackVersion>
</KnownWebAssemblySdkPack>
</ItemGroup>
</Target>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my mistake. I looked at wrong branch.
Anyway I'll remove it in a follow-up. This shouldn't not be needed anymore and if it does, something else is wrong.

<Target Name="_UpdateKnownCoreClrWebAssemblyPacks" BeforeTargets="ProcessFrameworkReferences"
Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<ItemGroup>
<KnownWebAssemblySdkPack Update="@(KnownWebAssemblySdkPack)">
<WebAssemblySdkPackVersion Condition="'%(KnownWebAssemblySdkPack.TargetFramework)' == '{{DefaultTargetFramework}}'">{{runtimePackVersion}}</WebAssemblySdkPackVersion>
</KnownWebAssemblySdkPack>
<KnownCrossgen2Pack Update="@(KnownCrossgen2Pack)">
<Crossgen2PackVersion Condition="'%(KnownCrossgen2Pack.TargetFramework)' == '{{DefaultTargetFramework}}'">{{runtimePackVersion}}</Crossgen2PackVersion>
</KnownCrossgen2Pack>
Comment thread
radekdoulik marked this conversation as resolved.
</ItemGroup>
</Target>
""";
Expand Down
Loading