diff --git a/eng/Versions.props b/eng/Versions.props
index 9cfd309e073b43..eff67b24848130 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -163,7 +163,7 @@
2.0.4
4.12.0
2.14.3
- 6.0.100-rc.2.21425.12
+ 6.0.100-rc.2.21463.12
5.0.0-preview-20201009.2
diff --git a/eng/testing/scenarios/BuildWasmAppsJobsList.txt b/eng/testing/scenarios/BuildWasmAppsJobsList.txt
index ba322274dfe62c..c4c5a1875c3bfc 100644
--- a/eng/testing/scenarios/BuildWasmAppsJobsList.txt
+++ b/eng/testing/scenarios/BuildWasmAppsJobsList.txt
@@ -1,14 +1,18 @@
-BlazorWasmTests
-FlagsChangeRebuildTest
-InvariantGlobalizationTests
-LocalEMSDKTests
-MainWithArgsTests
-NativeBuildTests
-NativeLibraryTests
-NoopNativeRebuildTest
-RebuildTests
-ReferenceNewAssemblyRebuildTest
-SatelliteAssembliesTests
-SimpleSourceChangeRebuildTest
-WasmBuildAppTest
-WorkloadTests
+Wasm.Build.NativeRebuild.Tests.FlagsChangeRebuildTest
+Wasm.Build.NativeRebuild.Tests.NoopNativeRebuildTest
+Wasm.Build.NativeRebuild.Tests.ReferenceNewAssemblyRebuildTest
+Wasm.Build.NativeRebuild.Tests.SimpleSourceChangeRebuildTest
+Wasm.Build.Tests.BlazorWasmBuildPublishTests
+Wasm.Build.Tests.BlazorWasmTests
+Wasm.Build.Tests.BuildPublishTests
+Wasm.Build.Tests.CleanTests
+Wasm.Build.Tests.InvariantGlobalizationTests
+Wasm.Build.Tests.LocalEMSDKTests
+Wasm.Build.Tests.MainWithArgsTests
+Wasm.Build.Tests.NativeBuildTests
+Wasm.Build.Tests.NativeLibraryTests
+Wasm.Build.Tests.RebuildTests
+Wasm.Build.Tests.SatelliteAssembliesTests
+Wasm.Build.Tests.WasmBuildAppTest
+Wasm.Build.Tests.WasmNativeDefaultsTests
+Wasm.Build.Tests.WorkloadTests
diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets
index fc196e0053efbf..ce6bfe2302ce6b 100644
--- a/eng/testing/tests.mobile.targets
+++ b/eng/testing/tests.mobile.targets
@@ -7,6 +7,7 @@
true
BundleTestAppleApp;BundleTestAndroidApp
+ Publish
@@ -104,6 +105,7 @@
OutputType="AsmOnly"
Assemblies="@(AotInputAssemblies)"
AotModulesTablePath="$(BundleDir)\modules.c"
+ IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(MonoEnableLLVM)"
LLVMPath="$(MonoAotCrossDir)">
@@ -189,6 +191,7 @@
Assemblies="@(AotInputAssemblies)"
AotModulesTablePath="$(BundleDir)\modules.m"
AotModulesTableLanguage="ObjC"
+ IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(MonoEnableLLVM)"
LLVMPath="$(MonoAotCrossDir)">
@@ -291,7 +294,7 @@
+ DependsOnTargets="$(PublishTestAsSelfContainedDependsOn);$(BundleTestAppTargets);ArchiveTests" />
+ true
+ true
$(BundleTestAppTargets);BundleTestWasmApp
true
+
+
+
+ true
- WasmBuildApp
- $(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix
+
+
+ PrepareForWasmBuildApp;$(WasmNestedPublishAppDependsOn)
@@ -80,6 +92,8 @@
$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))
+
+
@@ -154,7 +168,7 @@
-
+
<_CopyLocalPaths
diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj
index 14ea741c8c5956..93cbb9aca5c907 100644
--- a/src/libraries/sendtohelixhelp.proj
+++ b/src/libraries/sendtohelixhelp.proj
@@ -415,10 +415,10 @@
-
+
$(_BuildWasmAppsPayloadArchive)
- set "HELIX_XUNIT_ARGS=-class Wasm.Build.Tests.%(Identity)"
- export "HELIX_XUNIT_ARGS=-class Wasm.Build.Tests.%(Identity)"
+ set "HELIX_XUNIT_ARGS=-class %(Identity)"
+ export "HELIX_XUNIT_ARGS=-class %(Identity)"
$(HelixCommand)
$(_workItemTimeout)
diff --git a/src/libraries/workloads-testing.targets b/src/libraries/workloads-testing.targets
index 3e668a38c2400b..d3dc57267a608a 100644
--- a/src/libraries/workloads-testing.targets
+++ b/src/libraries/workloads-testing.targets
@@ -6,7 +6,7 @@
-
+
@@ -16,6 +16,8 @@
+
+
@@ -23,7 +25,7 @@
-
+
@@ -39,6 +41,7 @@
+
diff --git a/src/mono/sample/Android/AndroidSampleApp.csproj b/src/mono/sample/Android/AndroidSampleApp.csproj
index 1abf7659bd0997..a5129bdfe82c68 100644
--- a/src/mono/sample/Android/AndroidSampleApp.csproj
+++ b/src/mono/sample/Android/AndroidSampleApp.csproj
@@ -72,6 +72,7 @@
AotModulesTablePath="$(_AotModulesTablePath)"
ToolPrefix="$(_AotToolPrefix)"
LibraryFormat="$(_AotLibraryFormat)"
+ IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(UseLLVM)"
LLVMPath="$(MonoAotCrossDir)">
diff --git a/src/mono/sample/iOS/Program.csproj b/src/mono/sample/iOS/Program.csproj
index 8c3b217fc90911..64ffef7e16d545 100644
--- a/src/mono/sample/iOS/Program.csproj
+++ b/src/mono/sample/iOS/Program.csproj
@@ -60,6 +60,7 @@
AotModulesTablePath="$(AppDir)\modules.m"
AotModulesTableLanguage="ObjC"
OutputDir="$(PublishDir)"
+ IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(UseLLVM)"
LLVMPath="$(MonoAotCrossDir)">
diff --git a/src/mono/sample/mbr/browser/WasmDelta.csproj b/src/mono/sample/mbr/browser/WasmDelta.csproj
index 322a01ba8595a7..c150529f753c6d 100644
--- a/src/mono/sample/mbr/browser/WasmDelta.csproj
+++ b/src/mono/sample/mbr/browser/WasmDelta.csproj
@@ -31,7 +31,6 @@
-
\%(_DeltaFileForPublish.Filename)%(_DeltaFileForPublish.Extension)
diff --git a/src/mono/sample/wasm/Directory.Build.targets b/src/mono/sample/wasm/Directory.Build.targets
index 492c61cf048467..bc7f6b24b0ba27 100644
--- a/src/mono/sample/wasm/Directory.Build.targets
+++ b/src/mono/sample/wasm/Directory.Build.targets
@@ -1,10 +1,5 @@
-
-
-
-
-
Main(string[] args)
}
return args.Length;
}
-}
\ No newline at end of file
+}
diff --git a/src/mono/sample/wasm/wasm.mk b/src/mono/sample/wasm/wasm.mk
index 2eb4adcfb45853..e3405dbcc0d0d5 100644
--- a/src/mono/sample/wasm/wasm.mk
+++ b/src/mono/sample/wasm/wasm.mk
@@ -10,9 +10,12 @@ CONFIG?=Release
WASM_DEFAULT_BUILD_ARGS?=/p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG)
-all: build
+all: publish
build:
+ EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) build $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) $(MSBUILD_ARGS) $(PROJECT_NAME)
+
+publish:
EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) publish $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) $(MSBUILD_ARGS) $(PROJECT_NAME)
clean:
diff --git a/src/mono/wasm/BlazorOverwrite.targets b/src/mono/wasm/BlazorOverwrite.targets
new file mode 100644
index 00000000000000..a276d385723725
--- /dev/null
+++ b/src/mono/wasm/BlazorOverwrite.targets
@@ -0,0 +1,741 @@
+
+
+
+
+ true
+
+
+ true
+
+
+
+
+ $(MSBuildThisFileDirectory)..\
+ <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net6.0
+ <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net472
+ <_BlazorWebAssemblySdkTasksAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\$(_BlazorWebAssemblySdkTasksTFM)\Microsoft.NET.Sdk.BlazorWebAssembly.Tasks.dll
+ <_BlazorWebAssemblySdkToolAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\net6.0\Microsoft.NET.Sdk.BlazorWebAssembly.Tool.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+
+
+ false
+ false
+ true
+ false
+ false
+ false
+ <_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true
+ false
+ true
+
+
+ false
+ false
+ false
+ false
+ true
+
+
+ false
+
+ <_TargetingNET60OrLater Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '6.0'))">true
+
+
+
+ false
+
+ true
+ true
+ false
+ ComputeFilesToPublish;_GatherWasmFilesToPublish;$(WasmNestedPublishAppDependsOn)
+ <_ScrambleDotnetJsFileNameAfterThisTarget Condition="'$(UsingBrowserRuntimeWorkload)' != 'true'">ResolveRuntimePackAssets
+ <_ScrambleDotnetJsFileNameAfterThisTarget Condition="'$(UsingBrowserRuntimeWorkload)' == 'true'">WasmBuildApp
+
+
+ Publish
+
+
+
+
+
+
+
+
+
+
+
+ $(ResolveStaticWebAssetsInputsDependsOn);
+ _AddBlazorWasmStaticWebAssets;
+
+
+
+ _GenerateBuildBlazorBootJson;
+ $(StaticWebAssetsPrepareForRunDependsOn)
+
+
+
+ $(ResolvePublishStaticWebAssetsDependsOn);
+ ProcessPublishFilesForBlazor;
+ ComputeBlazorExtensions;
+ _AddPublishBlazorBootJsonToStaticWebAssets;
+
+
+
+ $(GenerateStaticWebAssetsPublishManifestDependsOn);
+ GeneratePublishBlazorBootJson;
+
+
+
+
+
+
+
+
+
+ <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)
+ <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)
+ <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js
+
+
+
+ <_DotNetJsItem Remove="@(_DotNetJsItem)" />
+ <_DotNetJsItem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">
+ _framework/$(_BlazorDotnetJsFileName)
+ native
+ true
+
+
+
+
+ <_DotnetJsStaticWebAssetCandidate Remove="@(_DotnetJsStaticWebAssetCandidate)" />
+ <_DotnetJsCopyCandidates Remove="@(_DotnetJsCopyCandidates)" />
+
+
+
+
+
+
+
+
+
+
+
+ <_DotnetJsStaticWebAsset Include="@(_DotnetJsStaticWebAssetCandidate->'%(ContentRoot)_framework\dotnet.js')" />
+ <_BlazorStaticWebAsset Include="@(_DotnetJsStaticWebAsset)" />
+
+
+
+
+
+
+ <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)
+ <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)
+ <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js
+
+
+
+ <_DotNetJsItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.DestinationSubPath)' == 'dotnet.js' AND '%(ReferenceCopyLocalPaths.AssetType)' == 'native'">
+ _framework/$(_BlazorDotnetJsFileName)
+
+
+
+
+ <_DotNetJsItem Remove="@(_DotNetJsItem)" />
+ <_DotNetJsItem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">
+ _framework/$(_BlazorDotnetJsFileName)
+ native
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+ <_DotnetJsStaticWebAsset Include="@(_DotnetJsStaticWebAssetCandidate->'%(ContentRoot)_framework\dotnet.js')" />
+ <_BlazorStaticWebAsset Include="@(_DotnetJsStaticWebAsset)" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorEnableTimeZoneSupport>$(BlazorEnableTimeZoneSupport)
+ <_BlazorEnableTimeZoneSupport Condition="'$(_BlazorEnableTimeZoneSupport)' == ''">true
+ <_BlazorInvariantGlobalization>$(InvariantGlobalization)
+ <_BlazorInvariantGlobalization Condition="'$(_BlazorInvariantGlobalization)' == ''">true
+ <_BlazorCopyOutputSymbolsToOutputDirectory>$(CopyOutputSymbolsToOutputDirectory)
+ <_BlazorCopyOutputSymbolsToOutputDirectory Condition="'$(_BlazorCopyOutputSymbolsToOutputDirectory)'==''">true
+ <_BlazorWebAssemblyLoadAllGlobalizationData>$(BlazorWebAssemblyLoadAllGlobalizationData)
+ <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(_BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false
+
+
+ $(OutputPath)$(PublishDirName)\
+
+
+
+
+
+ <_BlazorJSFile Include="$(BlazorWebAssemblyJSPath)" />
+ <_BlazorJSFile Include="$(BlazorWebAssemblyJSMapPath)" Condition="Exists('$(BlazorWebAssemblyJSMapPath)')" />
+ <_BlazorJsFile>
+ _framework/%(Filename)%(Extension)
+
+
+
+
+
+ <_BlazorConfigFileCandidates Include="@(StaticWebAsset)" Condition="'%(SourceType)' == 'Discovered'" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorBuildGZipCompressDirectory>$(IntermediateOutputPath)build-gz\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorBuildGZipCompressedFile>
+ %(RelatedAsset)
+
+
+ <_BlazorGzipStaticWebAsset Include="@(_BlazorBuildGZipCompressedFile->'%(FullPath)')" />
+
+
+
+ <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json
+
+
+
+ <_BuildBlazorBootJson
+ Include="$(_BlazorBuildBootJsonPath)"
+ RelativePath="_framework/blazor.boot.json" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json
+ <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false
+
+
+
+ <_BlazorJsModuleCandidatesForBuild
+ Include="@(StaticWebAsset)"
+ Condition="'%(StaticWebAsset.AssetTraitName)' == 'JSModule' and '%(StaticWebAsset.AssetTraitValue)' == 'JSLibraryModule' and '%(AssetKind)' != 'Publish'" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml
+
+
+
+ <_BlazorTypeGranularAssembly
+ Include="@(ManagedAssemblyToLink)"
+ Condition="'%(Extension)' == '.dll' AND $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))">
+ false
+ all
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorAotEnabled>$(UsingBrowserRuntimeWorkload)
+ <_BlazorAotEnabled Condition="'$(_BlazorAotEnabled)' == ''">false
+ <_BlazorLinkerEnabled>$(PublishTrimmed)
+ <_BlazorLinkerEnabled Condition="'$(_BlazorLinkerEnabled)' == ''">true
+
+
+
+
+
+ <_BlazorPublishPrefilteredAssets
+ Include="@(StaticWebAsset)"
+ Condition="'%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture' or '%(AssetRole)' == 'Alternative'" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorExtensionsCandidate Include="@(BlazorPublishExtension->'%(FullPath)')">
+ $(PackageId)
+ Computed
+ $(PublishDir)wwwroot
+ $(StaticWebAssetBasePath)
+ %(BlazorPublishExtension.RelativePath)
+ Publish
+ All
+ Primary
+ BlazorWebAssemblyResource
+ extension:%(BlazorPublishExtension.ExtensionName)
+ Never
+ PreserveNewest
+ %(BlazorPublishExtension.Identity)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_PublishBlazorBootJson
+ Include="$(IntermediateOutputPath)blazor.publish.boot.json"
+ RelativePath="_framework/blazor.boot.json" />
+
+
+
+
+
+
+
+
+
+
+ <_BlazorPublishAsset
+ Include="@(StaticWebAsset)"
+ Condition="'%(AssetKind)' != 'Build' and '%(StaticWebAsset.AssetTraitValue)' != 'manifest' and ('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture') and '%(StaticWebAsset.AssetTraitValue)' != 'boot'" />
+
+ <_BlazorPublishConfigFile
+ Include="@(StaticWebAsset)"
+ Condition="'%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' and '%(StaticWebAsset.AssetTraitValue)' == 'settings'"/>
+
+ <_BlazorJsModuleCandidatesForPublish
+ Include="@(StaticWebAsset)"
+ Condition="'%(StaticWebAsset.AssetTraitName)' == 'JSModule' and '%(StaticWebAsset.AssetTraitValue)' == 'JSLibraryModule' and '%(AssetKind)' != 'Build'" />
+
+
+ <_BlazorPublishAsset Remove="@(_BlazorExtensionsCandidatesForPublish)" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_CompressedFileOutputPath>$(IntermediateOutputPath)compress\
+ <_BlazorWebAssemblyBrotliIncremental>true
+
+
+
+ <_DotNetHostDirectory>$(NetCoreRoot)
+ <_DotNetHostFileName>dotnet
+ <_DotNetHostFileName Condition="'$(OS)' == 'Windows_NT'">dotnet.exe
+
+
+
+
+
+
+
+ <_GzipFileToCompressForPublish Include="@(StaticWebAsset)"
+ Condition="'%(AssetKind)' != 'Build' and ('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture')" >
+ %(Identity)
+ Alternative
+ Content-Encoding
+ gzip
+
+
+ <_BrotliFileToCompressForPublish Include="@(_GzipFileToCompressForPublish)" Condition="'%(AssetKind)' != 'Build'">
+ br
+
+
+
+ <_AlreadyGzipCompressedAssets
+ Include="@(StaticWebAsset)"
+ Condition="'%(AssetKind)' != 'Build' and ('%(StaticWebAsset.AssetTraitName)' == 'Content-Encoding' and '%(StaticWebAsset.AssetTraitValue)' == 'gzip')" />
+ <_GzipFileToCompressForPublish Remove="@(_AlreadyGzipCompressedAssets->'%(RelatedAsset)')" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_BlazorPublishGZipCompressedFile>
+ %(RelatedAsset)
+
+ <_BlazorPublishBrotliCompressedFile>
+ %(RelatedAsset)
+
+
+
+
+
+
+
+
diff --git a/src/mono/wasm/build/README.md b/src/mono/wasm/build/README.md
index 854ea041398fa0..312b5308645c39 100644
--- a/src/mono/wasm/build/README.md
+++ b/src/mono/wasm/build/README.md
@@ -1,3 +1,58 @@
+# Wasm app build
+
+This usually consists of taking the built assemblies, and related files, and generating an app bundle.
+
+Wasm app build can run in two scenarios:
+
+1. After build, eg. when running the app in VS, or `dotnet build foo.csproj`
+2. For Publish, eg, when publishing the app to a folder, or Azure
+
+A dotnet wasm app has some native wasm files (`dotnet.wasm`, and `dotnet.js`). How these files are obtained, or generated:
+
+1. Build
+ a. with no native libraries referenced (AOT setting is ignored here)
+ - files from the runtime pack are used as-is
+ b. with native libraries referenced
+ - dotnet.wasm is relinked with the native libraries
+2. Publish
+ - dotnet.wasm is relinked with the native libraries, and updated pinvoke/icalls from the trimmed assemblies
+ - if `RunAOTCompilation=true`, then the relinking includes AOT'ed assemblies
+
+## `Build`
+
+Implementation:
+
+- Target `WasmBuildApp`
+- runs after `Build` by default
+ - which can be disabled by `$(DisableAutoWasmBuildApp)`
+ - or the run-after target can be set via `$(WasmBuildAppAfterThisTarget)`
+
+- To run a custom target
+ - *before* any of the wasm build targets, use `$(WasmBuildAppDependsOn)`, and prepend your target name to that
+ - *after* any of the wasm build targets, use `AfterTargets="WasmBuildApp"` on that target
+- Avoid depending on this target, because it is available only when the workload is installed. Use `$(WasmNativeWorkload)` to check if it is installed.
+
+## `Publish`
+
+Implementation:
+
+- This part runs as a nested build using a `MSBuild` task, which means that the project gets reevaluated. So, if there were any changes made to items/properties in targets before this, then they won't be visible in the nested build.
+- By default `WasmTriggerPublishApp` runs after the `Publish` target, and that triggers the nested build
+ - The nested build runs `WasmNestedPublishApp`, which causes `Build`, and `Publish` targets to be run
+ - Because this causes `Build` to be run again, if you have any targets that get triggered by that, then they will be running twice.
+ - But the original *build* run, and this *publish* run can be differentiated using `$(WasmBuildingForNestedPublish)`
+
+- `WasmTriggerPublishApp` essentially just invokes the nested publish
+ - This runs after `Publish`
+ - which can be disabled by `$(DisableAutoWasmPublishApp)`
+ - or the run-after target can be set via `$(WasmTriggerPublishAppAfterThisTarget)`
+
+ - To influence the wasm build for publish, use `WasmNestedPublishApp`
+ - To run a custom target before it, use `$(WasmNestedPublishAppDependsOn)`
+ - to run a custom target *after* it, use `AfterTargets="WasmNestedPublishApp"`
+
+ - If you want to *dependsOn* on this, then use `DependsOnTargets="WasmTriggerPublishApp"`
+
# `WasmApp.{props,targets}`, and `WasmApp.InTree.{props,targets}`
- Any project that wants to use this, can import the props+targets, and set up the
@@ -74,4 +129,4 @@ them for the new task assembly.
3. Make changes similar to the one for existing dependent tasks in
- `eng/testing/linker/trimmingTests.targets`,
- `src/tests/Common/wasm-test-runner/WasmTestRunner.proj`
- - `src/tests/Directory.Build.targets`
\ No newline at end of file
+ - `src/tests/Directory.Build.targets`
diff --git a/src/mono/wasm/build/WasmApp.InTree.targets b/src/mono/wasm/build/WasmApp.InTree.targets
index b345da95cf93c2..f8c26080f0268d 100644
--- a/src/mono/wasm/build/WasmApp.InTree.targets
+++ b/src/mono/wasm/build/WasmApp.InTree.targets
@@ -17,7 +17,7 @@
Condition="'$(_LocalMicrosoftNetCoreAppRuntimePackDir)' != '' and
'%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App'" />
-
+
@@ -35,9 +35,9 @@
+ DependsOnTargets="WasmTriggerPublishApp">
$(MSBuildProjectName)
diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.targets b/src/mono/wasm/build/WasmApp.LocalBuild.targets
index 23f5249786d665..47559bccb84288 100644
--- a/src/mono/wasm/build/WasmApp.LocalBuild.targets
+++ b/src/mono/wasm/build/WasmApp.LocalBuild.targets
@@ -25,7 +25,7 @@
- true
+ true
link
@@ -45,7 +45,7 @@
Condition="'$(MicrosoftNetCoreAppRuntimePackLocationToUse)' != '' and
'%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App'" />
-
+
diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets
index 5b43374ab16b7f..1c282834433a6d 100644
--- a/src/mono/wasm/build/WasmApp.Native.targets
+++ b/src/mono/wasm/build/WasmApp.Native.targets
@@ -17,12 +17,6 @@
_CompleteWasmBuildNative
-
- _InitializeCommonProperties;
- _PrepareForWasmBuildNativeOnly;
- _WasmBuildNativeCore;
-
-
<_BeforeWasmBuildAppDependsOn>
$(_BeforeWasmBuildAppDependsOn);
_SetupEmscripten;
@@ -39,16 +33,6 @@
-
-
-
-
-
- <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
- <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" />
-
-
-
<_EMSDKMissingPaths Condition="'$(_EMSDKMissingPaths)' == '' and ('$(EmscriptenSdkToolsPath)' == '' or !Exists('$(EmscriptenSdkToolsPath)'))">%24(EmscriptenSdkToolsPath)=$(EmscriptenSdkToolsPath)
@@ -117,11 +101,27 @@
-
+
+
+
+ true
+
+ true
+ false
+
+
+
+
+
true
true
- false
- true
+
+
+ false
+
+
+ true
+
false
@@ -152,7 +152,7 @@
<_WasmPInvokeHPath>$(_WasmRuntimePackIncludeDir)wasm\pinvoke.h
<_DriverGenCPath>$(_WasmIntermediateOutputPath)driver-gen.c
- <_DriverGenCNeeded Condition="'$(_DriverGenCNeeded)' == '' and '$(RunAOTCompilation)' == 'true'">true
+ <_DriverGenCNeeded Condition="'$(_DriverGenCNeeded)' == '' and '$(_WasmShouldAOT)' == 'true'">true
<_EmccAssertionLevelDefault>0
<_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault)
@@ -190,8 +190,8 @@
<_EmccCFlags Include="$(EmccCompileOptimizationFlag)" />
<_EmccCFlags Include="@(_EmccCommonFlags)" />
- <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" />
- <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" />
+ <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(_WasmShouldAOT)' == 'true'" />
+ <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(_WasmShouldAOT)' == 'true'" />
<_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
<_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCFlags Include="-DCORE_BINDINGS" />
@@ -236,7 +236,9 @@
+ OutputPath="$(_WasmPInvokeTablePath)">
+
+
@@ -244,10 +246,16 @@
Text="Could not find AOT cross compiler at %24(_MonoAotCrossCompilerPath)=$(_MonoAotCrossCompilerPath)" />
+
+
+
+
+ OutputPath="$(_WasmICallTablePath)">
+
+
@@ -263,6 +271,9 @@
+
+
+
@@ -276,13 +287,15 @@
SourceFiles="@(_WasmSourceFileToCompile)"
Arguments='"@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileRsp)"'
EnvironmentVariables="@(EmscriptenEnvVars)"
- OutputMessageImportance="$(_EmccCompileOutputMessageImportance)" />
+ OutputMessageImportance="$(_EmccCompileOutputMessageImportance)">
+
+
@@ -294,7 +307,9 @@
SourceFiles="@(_BitCodeFile)"
Arguments=""@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileBitcodeRsp)""
EnvironmentVariables="@(EmscriptenEnvVars)"
- OutputMessageImportance="$(_EmccCompileOutputMessageImportance)" />
+ OutputMessageImportance="$(_EmccCompileOutputMessageImportance)">
+
+
@@ -303,6 +318,9 @@
<_BitcodeLDFlags Include="$(EmccExtraBitcodeLDFlags)" />
+
+
+
@@ -312,8 +330,8 @@
<_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />
<_WasmNativeFileForLinking
- Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a"
- Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\%(Identity)')" />
+ Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)*.a"
+ Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)%(Identity)')" />
<_WasmExtraJSFile Include="@(Content)" Condition="'%(Content.Extension)' == '.js'" />
@@ -336,6 +354,9 @@
+
+
+
+
+
+
+
@@ -358,7 +383,7 @@
-
+
$(EmccExtraCFlags) -DDRIVER_GEN=1
<_DriverGenCNeeded>true
@@ -414,7 +439,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
*******************************
-->
-
+
<_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm'))
@@ -433,13 +458,11 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
- <_AotInputAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'">
+ <_AotInputAssemblies Include="@(_WasmAssembliesInternal)">
@(MonoAOTCompilerDefaultAotArguments, ';')
@(MonoAOTCompilerDefaultProcessArguments, ';')
- <_AOT_InternalForceInterpretAssemblies Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
-
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
<_WasmAOTSearchPaths Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)" />
@@ -452,16 +475,13 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
<_AOTCompilerCacheFile>$(_WasmIntermediateOutputPath)aot_compiler_cache.json
-
-
<_WasmDedupAssembly>$(_WasmIntermediateOutputPath)\aot-instances.dll
-
+
+ LLVMPath="$(EmscriptenUpstreamBinPath)"
+ IntermediateOutputPath="$(_WasmIntermediateOutputPath)">
-
- <_WasmAssembliesInternal Include="@(_AOT_InternalForceInterpretAssemblies)" />
-
- <_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" />
<_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" />
<_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" />
-
+
<_WasmStrippedAssembliesPath>$([MSBuild]::NormalizeDirectory($(_WasmIntermediateOutputPath), 'stripped-assemblies'))
+ <_AOTedAssemblies Include="@(_WasmAssembliesInternal)" />
<_WasmStrippedAssemblies
- Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'"
- Include="@(_WasmAssembliesInternal->'$(_WasmStrippedAssembliesPath)%(FileName)%(Extension)')"
+ Include="@(_AOTedAssemblies)"
OriginalPath="%(_WasmAssembliesInternal.Identity)" />
- <_WasmInterpOnlyAssembly Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
@@ -531,7 +547,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
- <_WasmAssembliesInternal Include="@(_WasmStrippedAssemblies);@(_WasmInterpOnlyAssembly)" />
+ <_WasmAssembliesInternal Include="@(_WasmStrippedAssemblies)" />
diff --git a/src/mono/wasm/build/WasmApp.props b/src/mono/wasm/build/WasmApp.props
index bc45a6d54d73d8..7fdde27c4affb5 100644
--- a/src/mono/wasm/build/WasmApp.props
+++ b/src/mono/wasm/build/WasmApp.props
@@ -5,8 +5,7 @@
browser-wasm
true
- Publish
-
+ <_WasmBuildCoreDependsOn>
_InitializeCommonProperties;
_BeforeWasmBuildApp;
_WasmResolveReferences;
@@ -15,6 +14,16 @@
_WasmBuildNativeCore;
_WasmGenerateAppBundle;
_AfterWasmBuildApp
+
+
+
+ _PrepareForAfterBuild;
+ $(_WasmBuildCoreDependsOn)
+
+
+ _PrepareForNestedPublish;
+ $(_WasmBuildCoreDependsOn)
+
diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets
index e5ceb13def292e..9c96d759a228a1 100644
--- a/src/mono/wasm/build/WasmApp.targets
+++ b/src/mono/wasm/build/WasmApp.targets
@@ -5,9 +5,6 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_WasmRuntimeConfigFilePath Condition="$([System.String]::new(%(PublishItemsOutputGroupOutputs.Identity)).EndsWith('$(AssemblyName).runtimeconfig.json'))">@(PublishItemsOutputGroupOutputs)
+
+
+
+
+
+
-
-
+
+ <_WasmRuntimeConfigFilePath Condition="$([System.String]::new(%(PublishItemsOutputGroupOutputs.Identity)).EndsWith('$(AssemblyName).runtimeconfig.json'))">@(PublishItemsOutputGroupOutputs)
+
+
+
+
-
+
+
$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include'))
<_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src'))
- <_WasmIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm'))
+ <_WasmIntermediateOutputPath Condition="'$(WasmBuildingForNestedPublish)' == ''">$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm', 'for-build'))
+ <_WasmIntermediateOutputPath Condition="'$(WasmBuildingForNestedPublish)' != ''">$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm', 'for-publish'))
<_DriverGenCPath>$(_WasmIntermediateOutputPath)driver-gen.c
+ <_WasmShouldAOT Condition="'$(WasmBuildingForNestedPublish)' == 'true' and '$(RunAOTCompilation)' == 'true'">true
+ <_WasmShouldAOT Condition="'$(RunAOTCompilationAfterBuild)' == 'true' and '$(RunAOTCompilation)' == 'true'">true
+ <_WasmShouldAOT Condition="'$(_WasmShouldAOT)' == ''">false
+
+
+
+
+
+
- true
+ true
+ false
$([MSBuild]::NormalizeDirectory($(OutputPath), 'AppBundle'))
$(TargetFileName)
$([MSBuild]::NormalizeDirectory($(WasmAppDir)))
<_MainAssemblyPath Condition="'%(WasmAssembliesToBundle.FileName)' == $(AssemblyName) and '%(WasmAssembliesToBundle.Extension)' == '.dll' and $(WasmGenerateAppBundle) == 'true'">%(WasmAssembliesToBundle.Identity)
- <_WasmRuntimeConfigFilePath Condition="$(_MainAssemblyPath) != ''">$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))
- <_ParsedRuntimeConfigFilePath Condition="'$(_MainAssemblyPath)' != ''">$([System.IO.Path]::GetDirectoryName($(_MainAssemblyPath)))\runtimeconfig.bin
+ <_WasmRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' == '' and $(_MainAssemblyPath) != ''">$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))
+ <_ParsedRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' != ''">$([System.IO.Path]::GetDirectoryName($(_WasmRuntimeConfigFilePath)))\runtimeconfig.bin
-
+
+ <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
<_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" />
+ <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" />
<_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" />
<_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" />
@@ -166,9 +244,7 @@
-
-
-
+
icudt.dat
@@ -184,7 +260,16 @@
+
+
+
+
+
+
+
diff --git a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj
index 3a7b66fac7389f..65a0df19fb640c 100644
--- a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj
+++ b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj
@@ -5,6 +5,7 @@
$(MSBuildThisFileDirectory)..\wasm_build\
true
+ true
$(TestRootDir)..\publish\
$(OriginalPublishDir)..\extraFiles\
$(TestRootDir)\obj\
diff --git a/src/mono/wasm/debugger/tests/Directory.Build.props b/src/mono/wasm/debugger/tests/Directory.Build.props
index 0cff1f10a52d40..ff8864839daf17 100644
--- a/src/mono/wasm/debugger/tests/Directory.Build.props
+++ b/src/mono/wasm/debugger/tests/Directory.Build.props
@@ -4,6 +4,7 @@
$(AspNetCoreAppCurrent)
Library
+ true
Debug
Release
diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj
index 0503feaaff07e8..bf098dd83f578f 100644
--- a/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj
+++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-test.csproj
@@ -5,6 +5,7 @@
true
false
PrepareForWasmBuildApp;$(WasmBuildAppDependsOn)
+ true
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
index 71ae231a0fb042..4ee7389c04df01 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
@@ -191,11 +191,15 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
///
public string? CacheFilePath { get; set; }
+ [Required]
+ public string IntermediateOutputPath { get; set; } = string.Empty;
+
[Output]
public string[]? FileWrites { get; private set; }
private List _fileWrites = new();
+ private IList? _assembliesToCompile;
private ConcurrentDictionary compiledAssemblies = new();
private MonoAotMode parsedAotMode;
@@ -207,7 +211,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
private int _numCompiled;
private int _totalNumAssemblies;
- public override bool Execute()
+ private bool ProcessAndValidateArguments()
{
if (!File.Exists(CompilerBinaryPath))
{
@@ -230,6 +234,9 @@ public override bool Execute()
return false;
}
+ if (!Directory.Exists(IntermediateOutputPath))
+ Directory.CreateDirectory(IntermediateOutputPath);
+
if (AotProfilePath != null)
{
foreach (var path in AotProfilePath)
@@ -246,7 +253,7 @@ public override bool Execute()
{
if (string.IsNullOrEmpty(LLVMPath))
// prevent using some random llc/opt from PATH (installed with clang)
- throw new ArgumentException($"'{nameof(LLVMPath)}' is required when '{nameof(UseLLVM)}' is true.", nameof(LLVMPath));
+ throw new LogAsErrorException($"'{nameof(LLVMPath)}' is required when '{nameof(UseLLVM)}' is true.");
if (!Directory.Exists(LLVMPath))
{
@@ -270,7 +277,7 @@ public override bool Execute()
Log.LogWarning($"'{nameof(OutputType)}=Normal' is deprecated, use 'ObjectFile' instead.");
parsedOutputType = MonoAotOutputType.ObjectFile; break;
default:
- throw new ArgumentException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.ObjectFile)}', '{nameof(MonoAotOutputType.AsmOnly)}', '{nameof(MonoAotOutputType.Library)}'. Received: '{OutputType}'.", nameof(OutputType));
+ throw new LogAsErrorException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.ObjectFile)}', '{nameof(MonoAotOutputType.AsmOnly)}', '{nameof(MonoAotOutputType.Library)}'. Received: '{OutputType}'.");
}
switch (LibraryFormat)
@@ -280,13 +287,13 @@ public override bool Execute()
case "So": parsedLibraryFormat = MonoAotLibraryFormat.So; break;
default:
if (parsedOutputType == MonoAotOutputType.Library)
- throw new ArgumentException($"'{nameof(LibraryFormat)}' must be one of: '{nameof(MonoAotLibraryFormat.Dll)}', '{nameof(MonoAotLibraryFormat.Dylib)}', '{nameof(MonoAotLibraryFormat.So)}'. Received: '{LibraryFormat}'.", nameof(LibraryFormat));
+ throw new LogAsErrorException($"'{nameof(LibraryFormat)}' must be one of: '{nameof(MonoAotLibraryFormat.Dll)}', '{nameof(MonoAotLibraryFormat.Dylib)}', '{nameof(MonoAotLibraryFormat.So)}'. Received: '{LibraryFormat}'.");
break;
}
if (parsedAotMode == MonoAotMode.LLVMOnly && !UseLLVM)
{
- throw new ArgumentException($"'{nameof(UseLLVM)}' must be true when '{nameof(Mode)}' is {nameof(MonoAotMode.LLVMOnly)}.", nameof(UseLLVM));
+ throw new LogAsErrorException($"'{nameof(UseLLVM)}' must be true when '{nameof(Mode)}' is {nameof(MonoAotMode.LLVMOnly)}.");
}
switch (AotModulesTableLanguage)
@@ -294,32 +301,55 @@ public override bool Execute()
case "C": parsedAotModulesTableLanguage = MonoAotModulesTableLanguage.C; break;
case "ObjC": parsedAotModulesTableLanguage = MonoAotModulesTableLanguage.ObjC; break;
default:
- throw new ArgumentException($"'{nameof(AotModulesTableLanguage)}' must be one of: '{nameof(MonoAotModulesTableLanguage.C)}', '{nameof(MonoAotModulesTableLanguage.ObjC)}'. Received: '{AotModulesTableLanguage}'.", nameof(AotModulesTableLanguage));
+ throw new LogAsErrorException($"'{nameof(AotModulesTableLanguage)}' must be one of: '{nameof(MonoAotModulesTableLanguage.C)}', '{nameof(MonoAotModulesTableLanguage.ObjC)}'. Received: '{AotModulesTableLanguage}'.");
}
if (!string.IsNullOrEmpty(AotModulesTablePath))
{
// AOT modules for static linking, needs the aot modules table
UseStaticLinking = true;
-
- if (!GenerateAotModulesTable(Assemblies, Profilers, AotModulesTablePath))
- return false;
}
if (UseDirectIcalls && !UseStaticLinking)
{
- throw new ArgumentException($"'{nameof(UseDirectIcalls)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectIcalls));
+ throw new LogAsErrorException($"'{nameof(UseDirectIcalls)}' can only be used with '{nameof(UseStaticLinking)}=true'.");
}
if (UseDirectPInvoke && !UseStaticLinking)
{
- throw new ArgumentException($"'{nameof(UseDirectPInvoke)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectPInvoke));
+ throw new LogAsErrorException($"'{nameof(UseDirectPInvoke)}' can only be used with '{nameof(UseStaticLinking)}=true'.");
}
if (UseStaticLinking && (parsedOutputType == MonoAotOutputType.Library))
{
- throw new ArgumentException($"'{nameof(OutputType)}=Library' can not be used with '{nameof(UseStaticLinking)}=true'.", nameof(OutputType));
+ throw new LogAsErrorException($"'{nameof(OutputType)}=Library' can not be used with '{nameof(UseStaticLinking)}=true'.");
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ public override bool Execute()
+ {
+ try
+ {
+ return ExecuteInternal();
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
}
+ }
+
+ private bool ExecuteInternal()
+ {
+ if (!ProcessAndValidateArguments())
+ return false;
+
+ _assembliesToCompile = EnsureAndGetAssembliesInTheSameDir(Assemblies);
+
+ if (!string.IsNullOrEmpty(AotModulesTablePath) && !GenerateAotModulesTable(_assembliesToCompile, Profilers, AotModulesTablePath))
+ return false;
string? monoPaths = null;
if (AdditionalAssemblySearchPaths != null)
@@ -329,14 +359,14 @@ public override bool Execute()
//FIXME: check the nothing changed at all case
- _totalNumAssemblies = Assemblies.Length;
- int allowedParallelism = Math.Min(Assemblies.Length, Environment.ProcessorCount);
+ _totalNumAssemblies = _assembliesToCompile.Count;
+ int allowedParallelism = Math.Min(_assembliesToCompile.Count, Environment.ProcessorCount);
if (BuildEngine is IBuildEngine9 be9)
allowedParallelism = be9.RequestCores(allowedParallelism);
if (DisableParallelAot || allowedParallelism == 1)
{
- foreach (var assemblyItem in Assemblies)
+ foreach (var assemblyItem in _assembliesToCompile)
{
if (!PrecompileLibrarySerial(assemblyItem, monoPaths))
return !Log.HasLoggedErrors;
@@ -345,15 +375,12 @@ public override bool Execute()
else
{
ParallelLoopResult result = Parallel.ForEach(
- Assemblies,
+ _assembliesToCompile,
new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
(assemblyItem, state) => PrecompileLibraryParallel(assemblyItem, monoPaths, state));
if (!result.IsCompleted)
{
- if (!Log.HasLoggedErrors)
- Log.LogError("Unknown failure occured while compiling");
-
return false;
}
}
@@ -365,15 +392,71 @@ public override bool Execute()
if (_cache.Save(CacheFilePath!))
_fileWrites.Add(CacheFilePath!);
- CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, Assemblies).ToArray();
+ CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, _assembliesToCompile).ToArray();
FileWrites = _fileWrites.ToArray();
return !Log.HasLoggedErrors;
}
+ private IList EnsureAndGetAssembliesInTheSameDir(ITaskItem[] originalAssemblies)
+ {
+ List filteredAssemblies = new();
+ string firstAsmDir = Path.GetDirectoryName(originalAssemblies[0].GetMetadata("FullPath")) ?? string.Empty;
+ bool allInSameDir = true;
+
+ foreach (var origAsm in originalAssemblies)
+ {
+ if (allInSameDir && Path.GetDirectoryName(origAsm.GetMetadata("FullPath")) != firstAsmDir)
+ allInSameDir = false;
+
+ if (ShouldSkip(origAsm))
+ {
+ if (parsedAotMode == MonoAotMode.LLVMOnly)
+ throw new LogAsErrorException($"Building in AOTMode=LLVMonly is not compatible with excluding any assemblies for AOT. Excluded assembly: {origAsm.ItemSpec}");
+
+ Log.LogMessage(MessageImportance.Low, $"Skipping {origAsm.ItemSpec} because it has %(AOT_InternalForceToInterpret)=true");
+ continue;
+ }
+
+ filteredAssemblies.Add(origAsm);
+ }
+
+ if (allInSameDir)
+ return filteredAssemblies;
+
+ // Copy to aot-in
+
+ string aotInPath = Path.Combine(IntermediateOutputPath, "aot-in");
+ Directory.CreateDirectory(aotInPath);
+
+ List newAssemblies = new();
+ foreach (var origAsm in originalAssemblies)
+ {
+ string asmPath = origAsm.GetMetadata("FullPath");
+ string newPath = Path.Combine(aotInPath, Path.GetFileName(asmPath));
+
+ // FIXME: delete files not in originalAssemblies though
+ // FIXME: or .. just delete the whole dir?
+ if (Utils.CopyIfDifferent(asmPath, newPath, useHash: true))
+ Log.LogMessage(MessageImportance.Low, $"Copying {asmPath} to {newPath}");
+
+ if (!ShouldSkip(origAsm))
+ {
+ ITaskItem newAsm = new TaskItem(newPath);
+ origAsm.CopyMetadataTo(newAsm);
+ newAssemblies.Add(newAsm);
+ }
+ }
+
+ return newAssemblies;
+
+ static bool ShouldSkip(ITaskItem asmItem)
+ => bool.TryParse(asmItem.GetMetadata("AOT_InternalForceToInterpret"), out bool skip) && skip;
+ }
+
private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
{
- string assembly = assemblyItem.ItemSpec;
+ string assembly = assemblyItem.GetMetadata("FullPath");
string assemblyDir = Path.GetDirectoryName(assembly)!;
var aotAssembly = new TaskItem(assembly);
var aotArgs = new List();
@@ -567,38 +650,20 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
// values, which wont work.
processArgs.Add($"\"--aot={string.Join(",", aotArgs)}\"");
- string paths = "";
if (isDedup)
{
- StringBuilder sb = new StringBuilder();
- HashSet allPaths = new HashSet();
- foreach (var aItem in Assemblies)
- {
- string filename = aItem.ItemSpec;
- processArgs.Add(filename);
- string dir = Path.GetDirectoryName(filename)!;
- if (!allPaths.Contains(dir))
- {
- allPaths.Add(dir);
- if (sb.Length > 0)
- sb.Append(Path.PathSeparator);
- sb.Append(dir);
- }
- }
- if (sb.Length > 0)
- sb.Append(Path.PathSeparator);
- sb.Append(monoPaths);
- paths = sb.ToString();
+ foreach (var aItem in _assembliesToCompile!)
+ processArgs.Add(aItem.ItemSpec);
}
else
{
- paths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}";
processArgs.Add('"' + assemblyFilename + '"');
}
+ monoPaths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}";
var envVariables = new Dictionary
{
- {"MONO_PATH", paths},
+ {"MONO_PATH", monoPaths },
{"MONO_ENV_OPTIONS", string.Empty} // we do not want options to be provided out of band to the cross compilers
};
@@ -713,7 +778,7 @@ private void PrecompileLibraryParallel(ITaskItem assemblyItem, string? monoPaths
state.Break();
}
- private bool GenerateAotModulesTable(ITaskItem[] assemblies, string[]? profilers, string outputFile)
+ private bool GenerateAotModulesTable(IEnumerable assemblies, string[]? profilers, string outputFile)
{
var symbols = new List();
foreach (var asm in assemblies)
@@ -831,12 +896,12 @@ private bool TryGetAssemblyName(string asmPath, [NotNullWhen(true)] out string?
}
}
- private IList ConvertAssembliesDictToOrderedList(ConcurrentDictionary dict, ITaskItem[] items)
+ private static IList ConvertAssembliesDictToOrderedList(ConcurrentDictionary dict, IList originalAssemblies)
{
- List outItems = new(items.Length);
- foreach (ITaskItem item in items)
+ List outItems = new(originalAssemblies.Count);
+ foreach (ITaskItem item in originalAssemblies)
{
- if (!dict.TryGetValue(item.ItemSpec, out ITaskItem? dictItem))
+ if (!dict.TryGetValue(item.GetMetadata("FullPath"), out ITaskItem? dictItem))
throw new LogAsErrorException($"Bug: Could not find item in the dict with key {item.ItemSpec}");
outItems.Add(dictItem);
@@ -931,23 +996,26 @@ public bool CopyOutputFileIfChanged()
if (!_cache.Enabled)
return true;
- if (!File.Exists(TempFile))
- throw new LogAsErrorException($"Could not find output file {TempFile}");
-
- if (!_cache.ShouldCopy(this, out string? cause))
+ try
{
- _cache.Log.LogMessage(MessageImportance.Low, $"Skipping copying over {TargetFile} as the contents are unchanged");
- return false;
- }
+ if (!_cache.ShouldCopy(this, out string? cause))
+ {
+ _cache.Log.LogMessage(MessageImportance.Low, $"Skipping copying over {TargetFile} as the contents are unchanged");
+ return false;
+ }
- if (File.Exists(TargetFile))
- File.Delete(TargetFile);
+ if (File.Exists(TargetFile))
+ File.Delete(TargetFile);
- File.Copy(TempFile, TargetFile);
- File.Delete(TempFile);
+ File.Copy(TempFile, TargetFile);
- _cache.Log.LogMessage(MessageImportance.Low, $"Copying {TempFile} to {TargetFile} because {cause}");
- return true;
+ _cache.Log.LogMessage(MessageImportance.Low, $"Copying {TempFile} to {TargetFile} because {cause}");
+ return true;
+ }
+ finally
+ {
+ File.Delete(TempFile);
+ }
}
}
diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs
index 2c1a8b49370c7a..2c624214937d66 100644
--- a/src/tasks/Common/Utils.cs
+++ b/src/tasks/Common/Utils.cs
@@ -212,7 +212,7 @@ public static bool CopyIfDifferent(string src, string dst, bool useHash)
throw new ArgumentException($"Cannot find {src} file to copy", nameof(src));
bool areDifferent = !File.Exists(dst) ||
- (useHash && Utils.ComputeHash(src) != Utils.ComputeHash(dst)) ||
+ (useHash && ComputeHash(src) != ComputeHash(dst)) ||
(File.ReadAllText(src) != File.ReadAllText(dst));
if (areDifferent)
diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs
index e869e98e8564ff..a5d6af6c3b473a 100644
--- a/src/tasks/WasmAppBuilder/EmccCompile.cs
+++ b/src/tasks/WasmAppBuilder/EmccCompile.cs
@@ -44,6 +44,19 @@ public class EmccCompile : Microsoft.Build.Utilities.Task
private int _numCompiled;
public override bool Execute()
+ {
+ try
+ {
+ return ExecuteActual();
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
+ }
+ }
+
+ private bool ExecuteActual()
{
if (SourceFiles.Length == 0)
{
@@ -133,7 +146,7 @@ public override bool Execute()
});
if (!result.IsCompleted && !Log.HasLoggedErrors)
- Log.LogError("Unknown failed occured while compiling");
+ Log.LogError("Unknown failure occured while compiling. Check logs to get more details.");
}
if (!Log.HasLoggedErrors)
@@ -211,7 +224,7 @@ bool ProcessSourceFile(string srcFile, string objFile)
private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, out string reason)
{
if (!File.Exists(srcFile))
- throw new ArgumentException($"Could not find source file {srcFile}");
+ throw new LogAsErrorException($"Could not find source file {srcFile}");
if (!File.Exists(objFile))
{
@@ -228,7 +241,7 @@ private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, ou
return true;
}
- reason = "everything is up-to-date.";
+ reason = "everything is up-to-date";
return false;
bool IsNewerThanOutput(string inFile, string outFile, out string reason)
diff --git a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs
index 13c75c39bc64cd..cc4d8af1ac7faa 100644
--- a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs
+++ b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs
@@ -23,6 +23,9 @@ public class IcallTableGenerator : Task
[Required, NotNull]
public string? OutputPath { get; set; }
+ [Output]
+ public string? FileWrites { get; private set; } = "";
+
private List _icalls = new List ();
private Dictionary _runtimeIcalls = new Dictionary ();
@@ -58,6 +61,7 @@ public void GenIcallTable(string runtimeIcallTableFile, string[] assemblies)
Log.LogMessage(MessageImportance.Low, $"Generating icall table to '{OutputPath}'.");
else
Log.LogMessage(MessageImportance.Low, $"Icall table in {OutputPath} is unchanged.");
+ FileWrites = OutputPath;
File.Delete(tmpFileName);
}
diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
index f6b89533f98632..825de67d6e2704 100644
--- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
+++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
@@ -16,20 +16,43 @@
public class PInvokeTableGenerator : Task
{
- [Required]
- public ITaskItem[]? Modules { get; set; }
- [Required]
- public ITaskItem[]? Assemblies { get; set; }
+ [Required, NotNull]
+ public string[]? Modules { get; set; }
+ [Required, NotNull]
+ public string[]? Assemblies { get; set; }
[Required, NotNull]
public string? OutputPath { get; set; }
+ [Output]
+ public string FileWrites { get; private set; } = string.Empty;
+
private static char[] s_charsToReplace = new[] { '.', '-', };
public override bool Execute()
{
- GenPInvokeTable(Modules!.Select(item => item.ItemSpec).ToArray(), Assemblies!.Select(item => item.ItemSpec).ToArray());
- return true;
+ if (Assemblies.Length == 0)
+ {
+ Log.LogError($"No assemblies given to scan for pinvokes");
+ return false;
+ }
+
+ if (Modules.Length == 0)
+ {
+ Log.LogError($"{nameof(PInvokeTableGenerator)}.{nameof(Modules)} cannot be empty");
+ return false;
+ }
+
+ try
+ {
+ GenPInvokeTable(Modules, Assemblies);
+ return !Log.HasLoggedErrors;
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
+ }
}
public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies)
@@ -61,6 +84,7 @@ public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies)
Log.LogMessage(MessageImportance.Low, $"Generating pinvoke table to '{OutputPath}'.");
else
Log.LogMessage(MessageImportance.Low, $"PInvoke table in {OutputPath} is unchanged.");
+ FileWrites = OutputPath;
File.Delete(tmpFileName);
}
@@ -339,11 +363,7 @@ private static bool IsBlittable (Type type)
return false;
}
- private static void Error (string msg)
- {
- // FIXME:
- throw new Exception(msg);
- }
+ private static void Error (string msg) => throw new LogAsErrorException(msg);
}
internal class PInvoke
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
index 57cb7c383f107a..58b4b9259e629c 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.cs
@@ -117,11 +117,24 @@ public IcuData(string name) : base(name, "icu") {}
}
public override bool Execute ()
+ {
+ try
+ {
+ return ExecuteInternal();
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
+ }
+ }
+
+ private bool ExecuteInternal ()
{
if (!File.Exists(MainJS))
- throw new ArgumentException($"File MainJS='{MainJS}' doesn't exist.");
+ throw new LogAsErrorException($"File MainJS='{MainJS}' doesn't exist.");
if (!InvariantGlobalization && string.IsNullOrEmpty(IcuDataFileName))
- throw new ArgumentException("IcuDataFileName property shouldn't be empty if InvariantGlobalization=false");
+ throw new LogAsErrorException("IcuDataFileName property shouldn't be empty if InvariantGlobalization=false");
if (Assemblies?.Length == 0)
{
@@ -162,8 +175,12 @@ public override bool Execute ()
}
FileCopyChecked(MainJS!, Path.Combine(AppDir, "runtime.js"), string.Empty);
- var html = @"";
- File.WriteAllText(Path.Combine(AppDir, "index.html"), html);
+ string indexHtmlPath = Path.Combine(AppDir, "index.html");
+ if (!File.Exists(indexHtmlPath))
+ {
+ var html = @"";
+ File.WriteAllText(indexHtmlPath, html);
+ }
foreach (var assembly in _assemblies)
{
diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj
index d7cb1b1f5b7d08..a487a4f270ab90 100644
--- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj
+++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj
@@ -15,6 +15,7 @@
+
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs
new file mode 100644
index 00000000000000..687429a3fca482
--- /dev/null
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs
@@ -0,0 +1,194 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Linq;
+using Xunit;
+using Xunit.Abstractions;
+
+#nullable enable
+
+namespace Wasm.Build.Tests
+{
+ public class BlazorWasmBuildPublishTests : BuildTestBase
+ {
+ public BlazorWasmBuildPublishTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ _enablePerTestCleanup = true;
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void DefaultTemplate_WithoutWorkload(string config)
+ {
+ string id = $"blz_no_workload_{config}";
+ CreateBlazorWasmTemplateProject(id);
+
+ // Build
+ BuildInternal(id, config, publish: false);
+ AssertBlazorBootJson(config, isPublish: false);
+
+ // Publish
+ BuildInternal(id, config, publish: true);
+ AssertBlazorBootJson(config, isPublish: true);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void DefaultTemplate_NoAOT_WithWorkload(string config)
+ {
+ string id = $"blz_no_aot_{config}";
+ CreateBlazorWasmTemplateProject(id);
+
+ BlazorBuild(id, config, NativeFilesType.FromRuntimePack);
+ if (config == "Release")
+ {
+ // relinking in publish for Release config
+ BlazorPublish(id, config, NativeFilesType.Relinked);
+ }
+ else
+ {
+ BlazorPublish(id, config, NativeFilesType.FromRuntimePack);
+ }
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void DefaultTemplate_AOT_InProjectFile(string config)
+ {
+ string id = $"blz_aot_prj_file_{config}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: "true");
+
+ // No relinking, no AOT
+ BlazorBuild(id, config, NativeFilesType.FromRuntimePack);
+
+ // will aot
+ BlazorPublish(id, config, NativeFilesType.AOT);
+
+ // build again
+ BlazorBuild(id, config, NativeFilesType.FromRuntimePack);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug", true)]
+ [InlineData("Debug", false)]
+ [InlineData("Release", true)]
+ [InlineData("Release", false)]
+ public void NativeBuild_WithDeployOnBuild_UsedByVS(string config, bool nativeRelink)
+ {
+ string id = $"blz_deploy_on_build_{config}_{nativeRelink}";
+ string projectFile = CreateProjectWithNativeReference(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: nativeRelink ? string.Empty : "true");
+
+ // build with -p:DeployOnBuild=true, and that will trigger a publish
+ (CommandResult res, _) = BuildInternal(id, config, publish: false, "-p:DeployOnBuild=true");
+
+ var expectedFileType = nativeRelink ? NativeFilesType.Relinked : NativeFilesType.AOT;
+
+ AssertDotNetNativeFiles(expectedFileType, config, forPublish: true);
+ AssertBlazorBundle(config, isPublish: true, dotnetWasmFromRuntimePack: false);
+
+ if (expectedFileType == NativeFilesType.AOT)
+ {
+ // check for this too, so we know the format is correct for the negative
+ // test for jsinterop.webassembly.dll
+ Assert.Contains("Microsoft.JSInterop.dll -> Microsoft.JSInterop.dll.bc", res.Output);
+
+ // make sure this assembly gets skipped
+ Assert.DoesNotContain("Microsoft.JSInterop.WebAssembly.dll -> Microsoft.JSInterop.WebAssembly.dll.bc", res.Output);
+ }
+
+ // Check that we linked only for publish
+ string objBuildDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", "for-build");
+ Assert.False(Directory.Exists(objBuildDir), $"Found unexpected {objBuildDir}, which gets creating when relinking during Build");
+
+ // double check!
+ int index = res.Output.IndexOf("pinvoke.c -> pinvoke.o");
+ Assert.NotEqual(-1, index);
+
+ // there should be only one instance of this string!
+ index = res.Output.IndexOf("pinvoke.c -> pinvoke.o", index + 1);
+ Assert.Equal(-1, index);
+ }
+
+ // Disabling for now - publish folder can have more than one dotnet*hash*js, and not sure
+ // how to pick which one to check, for the test
+ //[ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ //[InlineData("Debug")]
+ //[InlineData("Release")]
+ //public void DefaultTemplate_AOT_OnlyWithPublishCommandLine_Then_PublishNoAOT(string config)
+ //{
+ //string id = $"blz_aot_pub_{config}";
+ //CreateBlazorWasmTemplateProject(id);
+
+ //// No relinking, no AOT
+ //BlazorBuild(id, config, NativeFilesType.FromRuntimePack);
+
+ //// AOT=true only for the publish command line, similar to what
+ //// would happen when setting it in Publish dialog for VS
+ //BlazorPublish(id, config, expectedFileType: NativeFilesType.AOT, "-p:RunAOTCompilation=true");
+
+ //// publish again, no AOT
+ //BlazorPublish(id, config, NativeFilesType.Relinked);
+ //}
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void WithNativeReference_AOTInProjectFile(string config)
+ {
+ string id = $"blz_nativeref_aot_{config}";
+ string projectFile = CreateProjectWithNativeReference(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: "true");
+
+ BlazorBuild(id, config, expectedFileType: NativeFilesType.Relinked);
+
+ BlazorPublish(id, config, expectedFileType: NativeFilesType.AOT);
+
+ // will relink
+ BlazorBuild(id, config, expectedFileType: NativeFilesType.Relinked);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void WithNativeReference_AOTOnCommandLine(string config)
+ {
+ string id = $"blz_nativeref_aot_{config}";
+ CreateProjectWithNativeReference(id);
+
+ BlazorBuild(id, config, expectedFileType: NativeFilesType.Relinked);
+
+ BlazorPublish(id, config, expectedFileType: NativeFilesType.AOT, "-p:RunAOTCompilation=true");
+
+ // no aot!
+ BlazorPublish(id, config, expectedFileType: NativeFilesType.Relinked);
+ }
+
+ private string CreateProjectWithNativeReference(string id)
+ {
+ CreateBlazorWasmTemplateProject(id);
+
+ string extraItems = @$"
+
+
+
+
+
+ ";
+ string projectFile = Path.Combine(_projectDir!, $"{id}.csproj");
+ AddItemsPropertiesToProject(projectFile, extraItems: extraItems);
+
+ return projectFile;
+ }
+
+ }
+
+ public enum NativeFilesType { FromRuntimePack, Relinked, AOT };
+}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
index 9b7709098ed929..6c7e3ed788be56 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Collections.Generic;
using System.IO;
using Xunit;
using Xunit.Abstractions;
@@ -17,36 +16,6 @@ public BlazorWasmTests(ITestOutputHelper output, SharedBuildPerTestClassFixture
{
}
- // TODO: invariant case?
-
- [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
- [InlineData("Debug", false)]
- [InlineData("Debug", true)] // just aot
- [InlineData("Release", false)] // should re-link
- [InlineData("Release", true)]
- public void PublishTemplateProject(string config, bool aot)
- {
- string id = $"blazorwasm_{config}_aot_{aot}_{Path.GetRandomFileName()}";
- InitBlazorWasmProjectDir(id);
-
- new DotNetCommand(s_buildEnv, useDefaultArgs: false)
- .WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput("new blazorwasm")
- .EnsureSuccessful();
-
- string publishLogPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}.binlog");
- new DotNetCommand(s_buildEnv)
- .WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput("publish", $"-bl:{publishLogPath}", aot ? "-p:RunAOTCompilation=true" : "", $"-p:Configuration={config}")
- .EnsureSuccessful();
-
- //TODO: validate the build somehow?
- // compare dotnet.wasm?
- // relinking - dotnet.wasm should be smaller
- //
- // playwright?
- }
-
[ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))]
[InlineData("Debug")]
[InlineData("Release")]
@@ -54,6 +23,7 @@ public void NativeRef_EmitsWarningBecauseItRequiresWorkload(string config)
{
CommandResult res = PublishForRequiresWorkloadTest(config, extraItems: "");
res.EnsureSuccessful();
+ AssertBlazorBundle(config, isPublish: true, dotnetWasmFromRuntimePack: true);
Assert.Contains("but the native references won't be linked in", res.Output);
}
@@ -71,7 +41,7 @@ public void AOT_FailsBecauseItRequiresWorkload(string config)
[ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))]
[InlineData("Debug")]
[InlineData("Release")]
- public void AOT_And_NativeRef_FailsBecauseItRequireWorkload(string config)
+ public void AOT_And_NativeRef_FailBecauseTheyRequireWorkload(string config)
{
CommandResult res = PublishForRequiresWorkloadTest(config,
extraProperties: "true",
@@ -84,18 +54,13 @@ public void AOT_And_NativeRef_FailsBecauseItRequireWorkload(string config)
private CommandResult PublishForRequiresWorkloadTest(string config, string extraItems="", string extraProperties="")
{
string id = $"needs_workload_{config}_{Path.GetRandomFileName()}";
- InitBlazorWasmProjectDir(id);
-
- new DotNetCommand(s_buildEnv, useDefaultArgs: false)
- .WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput("new blazorwasm")
- .EnsureSuccessful();
+ CreateBlazorWasmTemplateProject(id);
if (IsNotUsingWorkloads)
{
// no packs installed, so no need to update the paths for runtime pack etc
- File.WriteAllText(Path.Combine(_projectDir!, "Directory.Build.props"), "");
- File.WriteAllText(Path.Combine(_projectDir!, "Directory.Build.targets"), "");
+ File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Local.Directory.Build.props"), Path.Combine(_projectDir!, "Directory.Build.props"), overwrite: true);
+ File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Local.Directory.Build.targets"), Path.Combine(_projectDir!, "Directory.Build.targets"), overwrite: true);
}
AddItemsPropertiesToProject(Path.Combine(_projectDir!, $"{id}.csproj"),
@@ -136,7 +101,7 @@ private void BuildNet50Project(string config, bool aot, bool expectError, string
InitBlazorWasmProjectDir(id);
string directoryBuildTargets = @"
-
+
";
@@ -152,11 +117,11 @@ private void BuildNet50Project(string config, bool aot, bool expectError, string
string publishLogPath = Path.Combine(logPath, $"{id}.binlog");
CommandResult result = new DotNetCommand(s_buildEnv)
- .WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput("publish",
- $"-bl:{publishLogPath}",
- (aot ? "-p:RunAOTCompilation=true" : ""),
- $"-p:Configuration={config}");
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("publish",
+ $"-bl:{publishLogPath}",
+ (aot ? "-p:RunAOTCompilation=true" : ""),
+ $"-p:Configuration={config}");
if (expectError)
{
@@ -167,6 +132,11 @@ private void BuildNet50Project(string config, bool aot, bool expectError, string
{
result.EnsureSuccessful();
Assert.Contains("** UsingBrowserRuntimeWorkload: 'false'", result.Output);
+
+ string binFrameworkDir = FindBlazorBinFrameworkDir(config, forPublish: true, framework: "net5.0");
+ AssertBlazorBootJson(config, isPublish: true, binFrameworkDir: binFrameworkDir);
+ // dotnet.wasm here would be from 5.0 nuget like:
+ // /Users/radical/.nuget/packages/microsoft.netcore.app.runtime.browser-wasm/5.0.9/runtimes/browser-wasm/native/dotnet.wasm
}
}
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildAndRunAttribute.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildAndRunAttribute.cs
index c49b19517b3f2a..8d6615332652c2 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildAndRunAttribute.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildAndRunAttribute.cs
@@ -34,9 +34,9 @@ public BuildAndRunAttribute(BuildArgs buildArgs, RunHost host = RunHost.All, par
.UnwrapItemsAsArrays().ToList().Dump();
}
- public BuildAndRunAttribute(bool aot=false, RunHost host = RunHost.All, params object?[] parameters)
+ public BuildAndRunAttribute(bool aot=false, RunHost host = RunHost.All, string? config=null, params object?[] parameters)
{
- _data = BuildTestBase.ConfigWithAOTData(aot)
+ _data = BuildTestBase.ConfigWithAOTData(aot, config)
.Multiply(parameters)
.WithRunHosts(host)
.UnwrapItemsAsArrays().ToList().Dump();
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs
index 8e9be9ee967c41..b1de19ba1690db 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs
@@ -135,5 +135,8 @@ public BuildEnvironment()
protected static string s_directoryBuildPropsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.props"));
protected static string s_directoryBuildTargetsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.targets"));
+
+ protected static string s_directoryBuildPropsForBlazorLocal = File.ReadAllText(Path.Combine(TestDataPath, "Blazor.Local.Directory.Build.props"));
+ protected static string s_directoryBuildTargetsForBlazorLocal = File.ReadAllText(Path.Combine(TestDataPath, "Blazor.Local.Directory.Build.targets"));
}
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildPublishTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildPublishTests.cs
new file mode 100644
index 00000000000000..42eb96633f9b2a
--- /dev/null
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildPublishTests.cs
@@ -0,0 +1,158 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Linq;
+using Wasm.Build.NativeRebuild.Tests;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+#nullable enable
+
+namespace Wasm.Build.Tests
+{
+ public class BuildPublishTests : NativeRebuildTestsBase
+ {
+ public BuildPublishTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [Theory]
+ [BuildAndRun(host: RunHost.V8, aot: false, config: "Release")]
+ [BuildAndRun(host: RunHost.V8, aot: false, config: "Debug")]
+ public void BuildThenPublishNoAOT(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string projectName = $"build_publish_{buildArgs.Config}";
+
+ buildArgs = buildArgs with { ProjectName = projectName };
+ buildArgs = ExpandBuildArgs(buildArgs);
+
+ // no relinking for build
+ bool relinked = false;
+ BuildProject(buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: !relinked,
+ id: id,
+ createProject: true,
+ publish: false);
+
+ Run();
+
+ if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product))
+ throw new XunitException($"Test bug: could not get the build product in the cache");
+
+ File.Move(product!.LogFile, Path.ChangeExtension(product.LogFile!, ".first.binlog"));
+
+ _testOutput.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}");
+ Console.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}");
+
+ // relink by default for Release+publish
+ relinked = buildArgs.Config == "Release";
+ BuildProject(buildArgs,
+ id: id,
+ dotnetWasmFromRuntimePack: !relinked,
+ createProject: false,
+ publish: true,
+ useCache: false);
+
+ Run();
+
+ void Run() => RunAndTestWasmApp(
+ buildArgs, buildDir: _projectDir, expectedExitCode: 42,
+ test: output => {},
+ host: host, id: id);
+ }
+
+ [Theory]
+ [BuildAndRun(host: RunHost.V8, aot: true, config: "Release")]
+ [BuildAndRun(host: RunHost.V8, aot: true, config: "Debug")]
+ public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string projectName = $"build_publish_{buildArgs.Config}";
+
+ buildArgs = buildArgs with { ProjectName = projectName };
+ buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<_WasmDevel>true");
+
+ // no relinking for build
+ bool relinked = false;
+ (_, string output) = BuildProject(buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: !relinked,
+ id: id,
+ createProject: true,
+ publish: false,
+ label: "first_build");
+
+ BuildPaths paths = GetBuildPaths(buildArgs);
+ var pathsDict = GetFilesTable(buildArgs, paths, unchanged: false);
+
+ string mainDll = $"{buildArgs.ProjectName}.dll";
+ var firstBuildStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+ Assert.False(firstBuildStat["pinvoke.o"].Exists);
+ Assert.False(firstBuildStat[$"{mainDll}.bc"].Exists);
+
+ CheckOutputForNativeBuild(expectAOT: false, expectRelinking: relinked, buildArgs, output);
+
+ Run(expectAOT: false);
+
+ if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product))
+ throw new XunitException($"Test bug: could not get the build product in the cache");
+
+ File.Move(product!.LogFile, Path.ChangeExtension(product.LogFile!, ".first.binlog"));
+
+ _testOutput.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}");
+ Console.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}");
+
+ // relink by default for Release+publish
+ (_, output) = BuildProject(buildArgs,
+ id: id,
+ dotnetWasmFromRuntimePack: false,
+ createProject: false,
+ publish: true,
+ useCache: false,
+ label: "first_publish");
+
+ var publishStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+ Assert.True(publishStat["pinvoke.o"].Exists);
+ Assert.True(publishStat[$"{mainDll}.bc"].Exists);
+ CheckOutputForNativeBuild(expectAOT: true, expectRelinking: false, buildArgs, output);
+ CompareStat(firstBuildStat, publishStat, pathsDict.Values);
+
+ Run(expectAOT: true);
+
+ // second build
+ (_, output) = BuildProject(buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: !relinked,
+ id: id,
+ createProject: true,
+ publish: false,
+ label: "second_build");
+ var secondBuildStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ // no relinking, or AOT
+ CheckOutputForNativeBuild(expectAOT: false, expectRelinking: false, buildArgs, output);
+
+ // no native files changed
+ pathsDict.UpdateTo(unchanged: true);
+ CompareStat(publishStat, secondBuildStat, pathsDict.Values);
+
+ void Run(bool expectAOT) => RunAndTestWasmApp(
+ buildArgs with { AOT = expectAOT },
+ buildDir: _projectDir, expectedExitCode: 42,
+ host: host, id: id);
+ }
+
+ void CheckOutputForNativeBuild(bool expectAOT, bool expectRelinking, BuildArgs buildArgs, string buildOutput)
+ {
+ AssertSubstring($"{buildArgs.ProjectName}.dll -> {buildArgs.ProjectName}.dll.bc", buildOutput, expectAOT);
+ AssertSubstring($"{buildArgs.ProjectName}.dll.bc -> {buildArgs.ProjectName}.dll.o", buildOutput, expectAOT);
+
+ AssertSubstring("pinvoke.c -> pinvoke.o", buildOutput, expectRelinking || expectAOT);
+ }
+
+ }
+}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
index 50425f9844cb8c..b6fab5c6c434b7 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs
@@ -10,6 +10,7 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
+using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using System.Xml;
using Xunit;
@@ -285,8 +286,11 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp
bool useCache = true,
bool expectSuccess = true,
bool createProject = true,
- string? verbosity=null)
+ bool publish = true,
+ string? verbosity=null,
+ string? label=null)
{
+ string msgPrefix = label != null ? $"[{label}] " : string.Empty;
if (useCache && _buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product))
{
Console.WriteLine ($"Using existing build found at {product.ProjectDir}, with build log at {product.LogFile}");
@@ -314,12 +318,13 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp
}
StringBuilder sb = new();
- sb.Append("publish");
+ sb.Append(publish ? "publish" : "build");
sb.Append($" {s_buildEnv.DefaultBuildArgs}");
sb.Append($" /p:Configuration={buildArgs.Config}");
- string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}.binlog");
+ string logFileSuffix = label == null ? string.Empty : label.Replace(' ', '_');
+ string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}{logFileSuffix}.binlog");
_testOutput.WriteLine($"-------- Building ---------");
_testOutput.WriteLine($"Binlog path: {logFilePath}");
Console.WriteLine($"Binlog path: {logFilePath}");
@@ -371,6 +376,100 @@ public void InitBlazorWasmProjectDir(string id)
File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.targets"), Path.Combine(_projectDir, "Directory.Build.targets"));
}
+ public string CreateBlazorWasmTemplateProject(string id)
+ {
+ InitBlazorWasmProjectDir(id);
+ new DotNetCommand(s_buildEnv, useDefaultArgs: false)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("new blazorwasm")
+ .EnsureSuccessful();
+
+ return Path.Combine(_projectDir!, $"{id}.csproj");
+ }
+
+ protected (CommandResult, string) BlazorBuild(string id, string config, NativeFilesType expectedFileType, params string[] extraArgs)
+ {
+ var res = BuildInternal(id, config, publish: false, extraArgs);
+ AssertDotNetNativeFiles(expectedFileType, config, forPublish: false);
+ AssertBlazorBundle(config, isPublish: false, dotnetWasmFromRuntimePack: expectedFileType == NativeFilesType.FromRuntimePack);
+
+ return res;
+ }
+
+ protected (CommandResult, string) BlazorPublish(string id, string config, NativeFilesType expectedFileType, params string[] extraArgs)
+ {
+ var res = BuildInternal(id, config, publish: true, extraArgs);
+ AssertDotNetNativeFiles(expectedFileType, config, forPublish: true);
+ AssertBlazorBundle(config, isPublish: true, dotnetWasmFromRuntimePack: expectedFileType == NativeFilesType.FromRuntimePack);
+
+ if (expectedFileType == NativeFilesType.AOT)
+ {
+ // check for this too, so we know the format is correct for the negative
+ // test for jsinterop.webassembly.dll
+ Assert.Contains("Microsoft.JSInterop.dll -> Microsoft.JSInterop.dll.bc", res.Item1.Output);
+
+ // make sure this assembly gets skipped
+ Assert.DoesNotContain("Microsoft.JSInterop.WebAssembly.dll -> Microsoft.JSInterop.WebAssembly.dll.bc", res.Item1.Output);
+ }
+ return res;
+ }
+
+ protected (CommandResult, string) BuildInternal(string id, string config, bool publish=false, params string[] extraArgs)
+ {
+ string label = publish ? "publish" : "build";
+ Console.WriteLine($"{Environment.NewLine}** {label} **{Environment.NewLine}");
+
+ string logPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-{label}.binlog");
+ string[] combinedArgs = new[]
+ {
+ label, // same as the command name
+ $"-bl:{logPath}",
+ $"-p:Configuration={config}",
+ "-p:BlazorEnableCompression=false",
+ "-p:_WasmDevel=true"
+ }.Concat(extraArgs).ToArray();
+
+ CommandResult res = new DotNetCommand(s_buildEnv)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput(combinedArgs)
+ .EnsureSuccessful();
+
+ return (res, logPath);
+ }
+
+ protected void AssertDotNetNativeFiles(NativeFilesType type, string config, bool forPublish)
+ {
+ string label = forPublish ? "publish" : "build";
+ string objBuildDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", forPublish ? "for-publish" : "for-build");
+ string binFrameworkDir = FindBlazorBinFrameworkDir(config, forPublish);
+
+ string srcDir = type switch
+ {
+ NativeFilesType.FromRuntimePack => s_buildEnv.RuntimeNativeDir,
+ NativeFilesType.Relinked => objBuildDir,
+ NativeFilesType.AOT => objBuildDir,
+ _ => throw new ArgumentOutOfRangeException(nameof(type))
+ };
+
+ AssertSameFile(Path.Combine(srcDir, "dotnet.wasm"), Path.Combine(binFrameworkDir, "dotnet.wasm"), label);
+
+ // find dotnet*js
+ string? dotnetJsPath = Directory.EnumerateFiles(binFrameworkDir)
+ .Where(p => Path.GetFileName(p).StartsWith("dotnet.", StringComparison.OrdinalIgnoreCase) &&
+ Path.GetFileName(p).EndsWith(".js", StringComparison.OrdinalIgnoreCase))
+ .SingleOrDefault();
+
+ Assert.True(!string.IsNullOrEmpty(dotnetJsPath), $"[{label}] Expected to find dotnet*js in {binFrameworkDir}");
+ AssertSameFile(Path.Combine(srcDir, "dotnet.js"), dotnetJsPath!, label);
+
+ if (type != NativeFilesType.FromRuntimePack)
+ {
+ // check that the files are *not* from runtime pack
+ AssertNotSameFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.wasm"), Path.Combine(binFrameworkDir, "dotnet.wasm"), label);
+ AssertNotSameFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.js"), dotnetJsPath!, label);
+ }
+ }
+
static void AssertRuntimePackPath(string buildOutput)
{
var match = s_runtimePackPathRegex.Match(buildOutput);
@@ -384,7 +483,6 @@ static void AssertRuntimePackPath(string buildOutput)
protected static void AssertBasicAppBundle(string bundleDir, string projectName, string config, bool hasIcudt=true, bool dotnetWasmFromRuntimePack=true)
{
- Console.WriteLine ($"AssertBasicAppBundle: {dotnetWasmFromRuntimePack}");
AssertFilesExist(bundleDir, new []
{
"index.html",
@@ -486,6 +584,66 @@ protected static void AssertFile(string file0, string file1, string? label=null,
return result;
}
+ protected void AssertBlazorBundle(string config, bool isPublish, bool dotnetWasmFromRuntimePack, string? binFrameworkDir=null)
+ {
+ binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish);
+
+ AssertBlazorBootJson(config, isPublish, binFrameworkDir: binFrameworkDir);
+ AssertFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.wasm"),
+ Path.Combine(binFrameworkDir, "dotnet.wasm"),
+ "Expected dotnet.wasm to be same as the runtime pack",
+ same: dotnetWasmFromRuntimePack);
+
+ string? dotnetJsPath = Directory.EnumerateFiles(binFrameworkDir, "dotnet.*.js").FirstOrDefault();
+ Assert.True(dotnetJsPath != null, $"Could not find blazor's dotnet*js in {binFrameworkDir}");
+
+ AssertFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.js"),
+ dotnetJsPath!,
+ "Expected dotnet.js to be same as the runtime pack",
+ same: dotnetWasmFromRuntimePack);
+ }
+
+ protected void AssertBlazorBootJson(string config, bool isPublish, string? binFrameworkDir=null)
+ {
+ binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish);
+
+ string bootJsonPath = Path.Combine(binFrameworkDir, "blazor.boot.json");
+ Assert.True(File.Exists(bootJsonPath), $"Expected to find {bootJsonPath}");
+
+ string bootJson = File.ReadAllText(bootJsonPath);
+ var bootJsonNode = JsonNode.Parse(bootJson);
+ var runtimeObj = bootJsonNode?["resources"]?["runtime"]?.AsObject();
+ Assert.NotNull(runtimeObj);
+
+ string msgPrefix=$"[{( isPublish ? "publish" : "build" )}]";
+ Assert.True(runtimeObj!.Where(kvp => kvp.Key == "dotnet.wasm").Any(), $"{msgPrefix} Could not find dotnet.wasm entry in blazor.boot.json");
+ Assert.True(runtimeObj!.Where(kvp => kvp.Key.StartsWith("dotnet.", StringComparison.OrdinalIgnoreCase) &&
+ kvp.Key.EndsWith(".js", StringComparison.OrdinalIgnoreCase)).Any(),
+ $"{msgPrefix} Could not find dotnet.*js in {bootJson}");
+ }
+
+ protected string FindBlazorBinFrameworkDir(string config, bool forPublish, string framework="net6.0")
+ {
+ string basePath = Path.Combine(_projectDir!, "bin", config, framework);
+ if (forPublish)
+ basePath = FindSubDirIgnoringCase(basePath, "publish");
+
+ return Path.Combine(basePath, "wwwroot", "_framework");
+ }
+
+ private string FindSubDirIgnoringCase(string parentDir, string dirName)
+ {
+ IEnumerable matchingDirs = Directory.EnumerateDirectories(parentDir,
+ dirName,
+ new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive });
+
+ string? first = matchingDirs.FirstOrDefault();
+ if (matchingDirs.Count() > 1)
+ throw new Exception($"Found multiple directories with names that differ only in case. {string.Join(", ", matchingDirs.ToArray())}");
+
+ return first ?? Path.Combine(parentDir, dirName);
+ }
+
protected string GetBinDir(string config, string targetFramework=s_targetFramework, string? baseDir=null)
{
var dir = baseDir ?? _projectDir;
@@ -615,26 +773,34 @@ void LogData(string label, string? message)
}
}
- public static string AddItemsPropertiesToProject(string projectFile, string? extraProperties=null, string? extraItems=null)
+ public static string AddItemsPropertiesToProject(string projectFile, string? extraProperties=null, string? extraItems=null, string? atTheEnd=null)
{
- if (extraProperties == null && extraItems == null)
+ if (extraProperties == null && extraItems == null && atTheEnd == null)
return projectFile;
XmlDocument doc = new();
doc.Load(projectFile);
+ XmlNode root = doc.DocumentElement ?? throw new Exception();
if (extraItems != null)
{
XmlNode node = doc.CreateNode(XmlNodeType.Element, "ItemGroup", null);
node.InnerXml = extraItems;
- doc.DocumentElement!.AppendChild(node);
+ root.AppendChild(node);
}
if (extraProperties != null)
{
XmlNode node = doc.CreateNode(XmlNodeType.Element, "PropertyGroup", null);
node.InnerXml = extraProperties;
- doc.DocumentElement!.AppendChild(node);
+ root.AppendChild(node);
+ }
+
+ if (atTheEnd != null)
+ {
+ XmlNode node = doc.CreateNode(XmlNodeType.DocumentFragment, "foo", null);
+ node.InnerXml = atTheEnd;
+ root.InsertAfter(node, root.LastChild);
}
doc.Save(projectFile);
@@ -654,6 +820,29 @@ private static string GetEnvironmentVariableOrDefault(string envVarName, string
return string.IsNullOrEmpty(value) ? defaultValue : value;
}
+ internal BuildPaths GetBuildPaths(BuildArgs buildArgs, bool forPublish=true)
+ {
+ string objDir = GetObjDir(buildArgs.Config);
+ string bundleDir = Path.Combine(GetBinDir(baseDir: _projectDir, config: buildArgs.Config), "AppBundle");
+ string wasmDir = Path.Combine(objDir, "wasm", forPublish ? "for-publish" : "for-build");
+
+ return new BuildPaths(wasmDir, objDir, GetBinDir(buildArgs.Config), bundleDir);
+ }
+
+ internal IDictionary StatFiles(IEnumerable fullpaths)
+ {
+ Dictionary table = new();
+ foreach (string file in fullpaths)
+ {
+ if (File.Exists(file))
+ table.Add(Path.GetFileName(file), new FileStat(FullPath: file, Exists: true, LastWriteTimeUtc: File.GetLastWriteTimeUtc(file), Length: new FileInfo(file).Length));
+ else
+ table.Add(Path.GetFileName(file), new FileStat(FullPath: file, Exists: false, LastWriteTimeUtc: DateTime.MinValue, Length: 0));
+ }
+
+ return table;
+ }
+
protected static string s_mainReturns42 = @"
public class TestClass {
public static int Main()
@@ -669,4 +858,6 @@ public record BuildArgs(string ProjectName,
string ProjectFileContents,
string? ExtraBuildArgs);
public record BuildProduct(string ProjectDir, string LogFile, bool Result);
+ internal record FileStat (bool Exists, DateTime LastWriteTimeUtc, long Length, string FullPath);
+ internal record BuildPaths(string ObjWasmDir, string ObjDir, string BinDir, string BundleDir);
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/CleanTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/CleanTests.cs
new file mode 100644
index 00000000000000..d2226d5caadab6
--- /dev/null
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/CleanTests.cs
@@ -0,0 +1,110 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Linq;
+using Wasm.Build.NativeRebuild.Tests;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+#nullable enable
+
+namespace Wasm.Build.Tests;
+
+public class CleanTests : NativeRebuildTestsBase
+{
+ public CleanTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void Blazor_BuildThenClean_NativeRelinking(string config)
+ {
+ string id = Path.GetRandomFileName();
+
+ InitBlazorWasmProjectDir(id);
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+
+ string extraProperties = @"<_WasmDevel>true
+ true";
+
+ AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
+ BlazorBuild(id, config, NativeFilesType.Relinked);
+
+ string relinkDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", "for-build");
+ Assert.True(Directory.Exists(relinkDir), $"Could not find expected relink dir: {relinkDir}");
+
+ string logPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-clean.binlog");
+ new DotNetCommand(s_buildEnv)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("build", "-t:Clean", $"-p:Configuration={config}", $"-bl:{logPath}")
+ .EnsureSuccessful();
+
+ AssertEmptyOrNonExistantDirectory(relinkDir);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void Blazor_BuildNoNative_ThenBuildNative_ThenClean(string config)
+ => Blazor_BuildNativeNonNative_ThenCleanTest(config, firstBuildNative: false);
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void Blazor_BuildNative_ThenBuildNonNative_ThenClean(string config)
+ => Blazor_BuildNativeNonNative_ThenCleanTest(config, firstBuildNative: true);
+
+ private void Blazor_BuildNativeNonNative_ThenCleanTest(string config, bool firstBuildNative)
+ {
+ string id = Path.GetRandomFileName();
+
+ InitBlazorWasmProjectDir(id);
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+
+ string extraProperties = @"<_WasmDevel>true";
+
+ AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
+
+ bool relink = firstBuildNative;
+ BuildInternal(id, config, publish: false,
+ extraArgs: relink ? "-p:WasmBuildNative=true" : string.Empty);
+
+ string relinkDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", "for-build");
+ if (relink)
+ Assert.True(Directory.Exists(relinkDir), $"Could not find expected relink dir: {relinkDir}");
+
+ relink = !firstBuildNative;
+ BuildInternal(id, config, publish: false,
+ extraArgs: relink ? "-p:WasmBuildNative=true" : string.Empty);
+
+ if (relink)
+ Assert.True(Directory.Exists(relinkDir), $"Could not find expected relink dir: {relinkDir}");
+
+ string logPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-clean.binlog");
+ new DotNetCommand(s_buildEnv)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("build", "-t:Clean", $"-p:Configuration={config}", $"-bl:{logPath}")
+ .EnsureSuccessful();
+
+ AssertEmptyOrNonExistantDirectory(relinkDir);
+ }
+ private void AssertEmptyOrNonExistantDirectory(string dirPath)
+ {
+ Console.WriteLine($"dirPath: {dirPath}");
+ if (!Directory.Exists(dirPath))
+ return;
+
+ var files = Directory.GetFileSystemEntries(dirPath);
+ if (files.Length == 0)
+ return;
+
+ string found = string.Join(',', files.Select(p => Path.GetFileName(p)));
+ throw new XunitException($"Expected dir {dirPath} to be empty, but found: {found}");
+ }
+}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs
index db95f8bab36c78..7372ef034bc084 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs
@@ -24,10 +24,10 @@ public CommandResult(ProcessStartInfo startInfo, int exitCode, string output)
Output = output;
}
- public void EnsureSuccessful(string messagePrefix = "", bool suppressOutput = false)
+ public CommandResult EnsureSuccessful(string messagePrefix = "", bool suppressOutput = false)
=> EnsureExitCode(0, messagePrefix, suppressOutput);
- public void EnsureExitCode(int expectedExitCode = 0, string messagePrefix = "", bool suppressOutput = false)
+ public CommandResult EnsureExitCode(int expectedExitCode = 0, string messagePrefix = "", bool suppressOutput = false)
{
if (ExitCode != expectedExitCode)
{
@@ -43,6 +43,8 @@ public void EnsureExitCode(int expectedExitCode = 0, string messagePrefix = "",
throw new XunitException(message.ToString());
}
+
+ return this;
}
}
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/HelperExtensions.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/HelperExtensions.cs
index a70a51bb10bbd8..bff462f1f5de8c 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/HelperExtensions.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/HelperExtensions.cs
@@ -6,6 +6,7 @@
using System.Linq;
using System.IO;
using System.Text;
+using System.Collections;
#nullable enable
@@ -96,7 +97,9 @@ public static class HelperExtensions
public static void UpdateTo(this IDictionary dict, bool unchanged, params string[] filenames)
{
- foreach (var filename in filenames)
+ IEnumerable keys = filenames.Length == 0 ? dict.Keys.ToList() : filenames;
+
+ foreach (var filename in keys)
{
if (!dict.TryGetValue(filename, out var oldValue))
{
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs
index 80c7ba5fe85b88..3453f9922dbbad 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs
@@ -25,17 +25,14 @@ public NativeBuildTests(ITestOutputHelper output, SharedBuildPerTestClassFixture
[Theory]
[BuildAndRun]
public void SimpleNativeBuild(BuildArgs buildArgs, RunHost host, string id)
- => NativeBuild("simple_native_build", s_mainReturns42, buildArgs, host, id);
-
- private void NativeBuild(string projectNamePrefix, string projectContents, BuildArgs buildArgs, RunHost host, string id)
{
- string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}";
+ string projectName = $"simple_native_build_{buildArgs.Config}_{buildArgs.AOT}";
buildArgs = buildArgs with { ProjectName = projectName };
buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true");
BuildProject(buildArgs,
- initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), projectContents),
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
dotnetWasmFromRuntimePack: false,
id: id);
@@ -44,21 +41,46 @@ private void NativeBuild(string projectNamePrefix, string projectContents, Build
host: host, id: id);
}
+ [Theory]
+ [BuildAndRun(aot: true, host: RunHost.None)]
+ public void MonoAOTCross_WorksWithNoTrimming(BuildArgs buildArgs, string id)
+ {
+ // stop once `mono-aot-cross` part of the build is done
+ string target = @"
+
+ ";
+
+ string projectName = $"mono_aot_cross_{buildArgs.Config}_{buildArgs.AOT}";
+
+ buildArgs = buildArgs with { ProjectName = projectName, ExtraBuildArgs = "-p:PublishTrimmed=false -v:n" };
+ buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true", insertAtEnd: target);
+
+ (_, string output) = BuildProject(
+ buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: false,
+ id: id,
+ expectSuccess: false);
+
+ Assert.Contains("Stopping after AOT", output);
+ }
+
[Theory]
[BuildAndRun(host: RunHost.None, aot: true)]
public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, string id)
{
string printFileTypeTarget = @"
-
+
-
+
";
string projectName = $"bc_to_o_{buildArgs.Config}";
@@ -71,9 +93,41 @@ public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, st
dotnetWasmFromRuntimePack: false,
id: id);
- if (!output.Contains("wasm-dis exit code: 0"))
+ if (!output.Contains("** wasm-dis exit code: 0"))
throw new XunitException($"Expected to successfully run wasm-dis on System.Private.CoreLib.dll.o ."
+ " It might fail if it was incorrectly compiled to a bitcode file, instead of wasm.");
}
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void BlazorWasm_CanRunMonoAOTCross_WithNoTrimming(string config)
+ {
+ string id = $"blazorwasm_{config}_aot";
+ CreateBlazorWasmTemplateProject(id);
+
+ // We don't want to emcc compile, and link ~180 assemblies!
+ // So, stop once `mono-aot-cross` part of the build is done
+ string target = @"
+
+
+ ";
+ AddItemsPropertiesToProject(Path.Combine(_projectDir!, $"{id}.csproj"),
+ extraItems: null,
+ extraProperties: null,
+ atTheEnd: target);
+
+ string publishLogPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}.binlog");
+ CommandResult res = new DotNetCommand(s_buildEnv)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("publish",
+ $"-bl:{publishLogPath}",
+ "-p:RunAOTCompilation=true",
+ "-p:PublishTrimmed=false",
+ $"-p:Configuration={config}");
+
+ Assert.True(res.ExitCode != 0, "Expected publish to fail");
+ Assert.Contains("Stopping after AOT", res.Output);
+ }
}
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
index eb306286ba5e0d..3ccf400ee92113 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.IO;
using Xunit;
using Xunit.Abstractions;
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs
index 7ebc2d05cdd955..b73e521f8ca206 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/FlagsChangeRebuildTest.cs
@@ -5,12 +5,13 @@
using System.IO;
using System.Collections.Generic;
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
public class FlagsChangeRebuildTest : NativeRebuildTestsBase
{
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
index 2256fa58386abc..9047fd062a82e4 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
@@ -5,6 +5,7 @@
using System.IO;
using System.Collections.Generic;
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
@@ -12,7 +13,7 @@
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
// TODO: test for runtime components
public class NativeRebuildTestsBase : BuildTestBase
@@ -136,29 +137,6 @@ internal void CompareStat(IDictionary oldStat, IDictionary StatFiles(IEnumerable fullpaths)
- {
- Dictionary table = new();
- foreach (string file in fullpaths)
- {
- if (File.Exists(file))
- table.Add(Path.GetFileName(file), new FileStat(FullPath: file, Exists: true, LastWriteTimeUtc: File.GetLastWriteTimeUtc(file), Length: new FileInfo(file).Length));
- else
- table.Add(Path.GetFileName(file), new FileStat(FullPath: file, Exists: false, LastWriteTimeUtc: DateTime.MinValue, Length: 0));
- }
-
- return table;
- }
-
- internal BuildPaths GetBuildPaths(BuildArgs buildArgs)
- {
- string objDir = GetObjDir(buildArgs.Config);
- string bundleDir = Path.Combine(GetBinDir(baseDir: _projectDir, config: buildArgs.Config), "AppBundle");
- string wasmDir = Path.Combine(objDir, "wasm");
-
- return new BuildPaths(wasmDir, objDir, GetBinDir(buildArgs.Config), bundleDir);
- }
-
internal IDictionary GetFilesTable(BuildArgs buildArgs, BuildPaths paths, bool unchanged)
{
List files = new()
@@ -203,7 +181,4 @@ protected void AssertSubstring(string substring, string full, bool contains)
Assert.DoesNotContain(substring, full);
}
}
-
- internal record FileStat (bool Exists, DateTime LastWriteTimeUtc, long Length, string FullPath);
- internal record BuildPaths(string ObjWasmDir, string ObjDir, string BinDir, string BundleDir);
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
index 1ddbd4467b6206..96f6d41fa10025 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
@@ -2,12 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
public class NoopNativeRebuildTest : NativeRebuildTestsBase
{
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/ReferenceNewAssemblyRebuildTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/ReferenceNewAssemblyRebuildTest.cs
index d908e287cf8bd1..5aa3d2916f36fe 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/ReferenceNewAssemblyRebuildTest.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/ReferenceNewAssemblyRebuildTest.cs
@@ -3,12 +3,13 @@
using System.IO;
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
public class ReferenceNewAssemblyRebuildTest : NativeRebuildTestsBase
{
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/SimpleSourceChangeRebuildTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/SimpleSourceChangeRebuildTest.cs
index 7f51447cb9e62a..bbe7d602216717 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/SimpleSourceChangeRebuildTest.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/SimpleSourceChangeRebuildTest.cs
@@ -3,12 +3,13 @@
using System.IO;
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
public class SimpleSourceChangeRebuildTest : NativeRebuildTestsBase
{
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs
index 45ce2b37ed2736..c3a40b470200a6 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs
@@ -40,12 +40,13 @@ public void ResourcesFromMainAssembly(BuildArgs buildArgs,
string id)
{
string projectName = $"sat_asm_from_main_asm";
- bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT;
+ // Release+publish defaults to native relinking
+ bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT && buildArgs.Config != "Release";
buildArgs = buildArgs with { ProjectName = projectName };
buildArgs = ExpandBuildArgs(buildArgs,
projectTemplate: s_resourcesProjectTemplate,
- extraProperties: $"{(nativeRelink ? "true" : "false")}");
+ extraProperties: nativeRelink ? $"true" : string.Empty);
BuildProject(buildArgs,
initProject: () =>
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs
index 03437e67302133..019391f3efaade 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs
@@ -16,15 +16,20 @@ public class SharedBuildPerTestClassFixture : IDisposable
public Dictionary _buildPaths = new();
public void CacheBuild(BuildArgs buildArgs, BuildProduct product)
- => _buildPaths.Add(buildArgs, product);
+ {
+ if (product == null)
+ throw new ArgumentNullException(nameof(product));
+ if (buildArgs == null)
+ throw new ArgumentNullException(nameof(buildArgs));
+ _buildPaths.Add(buildArgs, product);
+ }
public void RemoveFromCache(string buildPath, bool keepDir=true)
{
- KeyValuePair? foundKvp = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).SingleOrDefault();
- if (foundKvp == null)
- throw new Exception($"Could not find build path {buildPath} in cache to remove.");
+ BuildArgs? foundBuildArgs = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).Select(kvp => kvp.Key).SingleOrDefault();
+ if (foundBuildArgs is not null)
+ _buildPaths.Remove(foundBuildArgs);
- _buildPaths.Remove(foundKvp.Value.Key);
if (!keepDir)
RemoveDirectory(buildPath);
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj
index 7dacbc3ec26f7e..6c9a7037025f63 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj
@@ -7,7 +7,6 @@
true
BuildAndRun
xunit
- false
true
false
TEST_DEBUG_CONFIG_ALSO
@@ -18,7 +17,7 @@
true
-
+ false
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmNativeDefaultsTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmNativeDefaultsTests.cs
new file mode 100644
index 00000000000000..64d008549e8ec1
--- /dev/null
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmNativeDefaultsTests.cs
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+#nullable enable
+
+namespace Wasm.Build.Tests
+{
+ public class WasmNativeDefaultsTests : BuildTestBase
+ {
+ public WasmNativeDefaultsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [Theory]
+ /* relink by default for publish+Release */
+ [InlineData("Release", "", /*aot*/ false, /*build*/ false, /*publish*/ true)]
+ /* NO relink by default for publish+Release, even when not trimming */
+ [InlineData("Release", "false", /*aot*/ false, /*build*/ false, /*publish*/ false)]
+
+ [InlineData("Debug", "", /*aot*/ false, /*build*/ false, /*publish*/ false)]
+
+ /* AOT */
+ [InlineData("Release", "", /*aot*/ true, /*build*/ false, /*publish*/ true)]
+ [InlineData("Debug", "", /*aot*/ true, /*build*/ false, /*publish*/ true)]
+ // FIXME: separate test
+ // [InlineData("Release", "true",
+ // /*aot*/ true, /*build*/ true, /*publish*/ true)]
+
+ /* AOT not affected by trimming */
+ [InlineData("Release", "false", /*aot*/ true, /*build*/ false, /*publish*/ true)]
+ [InlineData("Debug", "false", /*aot*/ true, /*build*/ false, /*publish*/ true)]
+ public void Defaults(string config, string extraProperties, bool aot, bool buildValue, bool publishValue)
+ {
+ string output = CheckWasmNativeDefaultValue("native_defaults_publish", config, extraProperties, aot, dotnetWasmFromRuntimePack: !publishValue);
+
+ Assert.Contains($"** WasmBuildNative: '{buildValue.ToString().ToLower()}', WasmBuildingForNestedPublish: ''", output);
+ Assert.Contains($"** WasmBuildNative: '{publishValue.ToString().ToLower()}', WasmBuildingForNestedPublish: 'true'", output);
+ Assert.Contains("Stopping the build", output);
+ }
+
+ [Theory]
+ /* always relink */
+ [InlineData("Release", "", /*build*/ true, /*publish*/ true)]
+ [InlineData("Debug", "", /*build*/ true, /*publish*/ true)]
+ [InlineData("Release", "false", /*build*/ true, /*publish*/ true)]
+ public void WithNativeReference(string config, string extraProperties, bool buildValue, bool publishValue)
+ {
+ string nativeLibPath = Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", "native-lib.o");
+ string nativeRefItem = @$"";
+ string output = CheckWasmNativeDefaultValue("native_defaults_publish",
+ config,
+ extraProperties,
+ aot: false,
+ dotnetWasmFromRuntimePack: !publishValue,
+ extraItems: nativeRefItem);
+
+ Assert.Contains($"** WasmBuildNative: '{buildValue.ToString().ToLower()}', WasmBuildingForNestedPublish: ''", output);
+ Assert.Contains($"** WasmBuildNative: '{publishValue.ToString().ToLower()}', WasmBuildingForNestedPublish: 'true'", output);
+ Assert.Contains("Stopping the build", output);
+ }
+
+ private string CheckWasmNativeDefaultValue(string projectName,
+ string config,
+ string extraProperties,
+ bool aot,
+ bool dotnetWasmFromRuntimePack,
+ string extraItems = "")
+ {
+ // builds with -O0
+ extraProperties += "<_WasmDevel>true";
+
+ string printValueTarget = @"
+
+
+
+ ";
+
+ BuildArgs buildArgs = new(ProjectName: projectName, Config: config, AOT: aot, string.Empty, null);
+ buildArgs = ExpandBuildArgs(buildArgs,
+ extraProperties: extraProperties,
+ extraItems: extraItems,
+ insertAtEnd: printValueTarget);
+
+ (_, string output) = BuildProject(buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack,
+ id: Path.GetRandomFileName(),
+ expectSuccess: false,
+ useCache: false);
+
+ return output;
+ }
+ }
+}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets
index f77fc96c48d059..5cef9821d8f805 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets
@@ -10,7 +10,8 @@
+ AfterTargets="ProcessFrameworkReferences"
+ Condition="'$(WasmNativeWorkload)' == 'true'">
@@ -21,7 +22,8 @@
+ AfterTargets="ResolveFrameworkReferences"
+ Condition="'$(WasmNativeWorkload)' == 'true'">
+ AfterTargets="ResolveTargetingPackAssets"
+ Condition="'$(WasmNativeWorkload)' == 'true'">
@@ -56,7 +59,7 @@
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Local.Directory.Build.props b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Local.Directory.Build.props
new file mode 100644
index 00000000000000..909ea0382ba0ef
--- /dev/null
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Local.Directory.Build.props
@@ -0,0 +1,11 @@
+
+
+ $(RuntimeSrcDir)\artifacts\bin\
+ $([MSBuild]::NormalizeDirectory($(ArtifactsBinDir), 'microsoft.netcore.app.runtime.browser-wasm', $(RuntimeConfig)))
+
+
+
+ $(WasmBuildSupportDir)\
+ $([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'microsoft.netcore.app.runtime.browser-wasm'))
+
+
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Local.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Local.Directory.Build.targets
new file mode 100644
index 00000000000000..639a8413dc3f6a
--- /dev/null
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Local.Directory.Build.targets
@@ -0,0 +1,36 @@
+
+
+ <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props
index 1a9c112e747d9b..6d53e53b3bf551 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props
@@ -7,8 +7,4 @@
-
-
- PrepareForWasmBuild;$(WasmBuildAppDependsOn)
-
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets
index 65f76e4fc8582c..b327d1e91f6eb8 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets
@@ -16,12 +16,6 @@
Text="%24(WasmMainJS) is set when %24(WasmGenerateAppBundle) is not true: it won't be used because an app bundle is not being generated. Possible build authoring error" />
-
-
-
-
-
-
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets
index 105c0a74c08757..34eeeaeee7bd7c 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets
@@ -1,15 +1,8 @@
- PrepareForWasmBuild;$(WasmBuildAppDependsOn)
<_MicrosoftNetCoreAppRefDir>$(AppRefDir)\
-
-
-
-
-
-
diff --git a/src/tests/Common/wasm-test-runner/WasmTestRunner.proj b/src/tests/Common/wasm-test-runner/WasmTestRunner.proj
index 5f4d4f084d13be..e2eda44d7ffc02 100644
--- a/src/tests/Common/wasm-test-runner/WasmTestRunner.proj
+++ b/src/tests/Common/wasm-test-runner/WasmTestRunner.proj
@@ -10,14 +10,17 @@
$(MSBuildThisFileDirectory)\obj\$(Configuration)\wasm
$(TestBinDir)/WasmApp/
99.0
+ true
+ true
$(CORE_ROOT)\WasmAppBuilder\WasmAppBuilder.dll
$(CORE_ROOT)\MonoAOTCompiler\MonoAOTCompiler.dll
$(CORE_ROOT)\JsonToItemsTaskFactory\JsonToItemsTaskFactory.dll
$(CORE_ROOT)\RuntimeConfigParser\RuntimeConfigParser.dll
+ BuildApp;$(WasmBuildAppDependsOn)
-
+
$(TestAssemblyFileName)
$(AppDir)
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj
index 2ba05527c2a42d..d9dbaa2ebd4443 100644
--- a/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/HotReload/WebAssembly.Browser.HotReload.Test.csproj
@@ -16,13 +16,10 @@
Always
+
-
-
-
-
diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/RuntimeConfig/WebAssembly.Browser.RuntimeConfig.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/RuntimeConfig/WebAssembly.Browser.RuntimeConfig.Test.csproj
index 4efa17aab45d9b..35e9eec2cae877 100644
--- a/src/tests/FunctionalTests/WebAssembly/Browser/RuntimeConfig/WebAssembly.Browser.RuntimeConfig.Test.csproj
+++ b/src/tests/FunctionalTests/WebAssembly/Browser/RuntimeConfig/WebAssembly.Browser.RuntimeConfig.Test.csproj
@@ -5,13 +5,9 @@
42
runtime.js
-
+
+
-
-
-
-
-
diff --git a/src/tests/FunctionalTests/WebAssembly/Directory.Build.targets b/src/tests/FunctionalTests/WebAssembly/Directory.Build.targets
index b0d320fabe3776..802f2525e58808 100644
--- a/src/tests/FunctionalTests/WebAssembly/Directory.Build.targets
+++ b/src/tests/FunctionalTests/WebAssembly/Directory.Build.targets
@@ -2,13 +2,6 @@
- PrepareForWasmBuild;$(WasmBuildAppDependsOn)
$(OutputPath)\$(Configuration)\AppBundle\
-
-
-
-
-
-