diff --git a/Directory.Build.props b/Directory.Build.props index d957a798303ade..94fde902d806a5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -71,6 +71,7 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'AndroidAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WorkloadBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(NetCoreAppToolCurrent)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'RuntimeConfigParser', 'Debug', '$(NetCoreAppToolCurrent)')) @@ -82,6 +83,7 @@ $([MSBuild]::NormalizePath('$(AndroidAppBuilderDir)', 'AndroidAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) + $([MSBuild]::NormalizePath('$(WorkloadBuildTasksDir)', 'WorkloadBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) $([MSBuild]::NormalizePath('$(RuntimeConfigParserDir)', 'RuntimeConfigParser.dll')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(Configuration)')) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1a6424850bbcdf..b758afbe200d9b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -210,7 +210,7 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization b89374348ff2344a625677584be9dfc9bea2b971 - + https://github.com/dotnet/emsdk f5349765b7af1970c5b25cce4ed278544907cbe0 diff --git a/eng/Versions.props b/eng/Versions.props index d8104e89eb4447..43f671458a6ab3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -159,6 +159,7 @@ 2.0.4 4.12.0 2.14.3 + 6.0.100-preview.7.21326.4 5.0.0-preview-20201009.2 @@ -178,7 +179,7 @@ 11.1.0-alpha.1.21328.1 11.1.0-alpha.1.21328.1 - 6.0.0-preview.7.21330.1 - $(MicrosoftNETRuntimeEmscripten2023Nodewinx64Version) + 6.0.0-preview.7.21330.1 + $(MicrosoftNETWorkloadEmscriptenManifest60100) diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 666641354bf5a6..cf984c79847739 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -389,6 +389,7 @@ jobs: extraHelixArguments: /p:BrowserHost=windows scenarios: - normal + - buildwasmapps condition: >- or( eq(variables['librariesContainsChange'], true), diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 517ebf719850c1..c3107ec4a060a7 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -7,6 +7,10 @@ true BundleTestAppleApp;BundleTestAndroidApp + + + true @@ -291,5 +295,10 @@ AfterTargets="Build" DependsOnTargets="Publish;$(BundleTestAppTargets);ArchiveTests" /> + + diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index dd40856e629b1b..1e7ec3b173dc33 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -1,13 +1,15 @@ - + RunnerTemplate.cmd RunnerTemplate.sh AppleRunnerTemplate.sh AndroidRunnerTemplate.sh WasmRunnerTemplate.sh WasmRunnerTemplate.cmd + - $(MSBuildThisFileDirectory)$(RunScriptInputName) + + $(MSBuildThisFileDirectory)$(RunScriptInputName) RunTests.sh RunTests.cmd diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index aa4fb305dbd425..cd2fa3e4e131d1 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -60,6 +60,11 @@ $(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix + + + + + --interpreter + + $(ArtifactsBinDir)dotnet-workload\ + $([MSBuild]::NormalizeDirectory($(SdkPathForWorkloadTesting))) + + $(SdkPathForWorkloadTesting)version-$(SdkVersionForWorkloadTesting).stamp + + diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index b10705260f4fcf..8107182949bc38 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -7,6 +7,11 @@ $(TestStrongNameKeyId) + + true + $(SdkPathForWorkloadTesting)workload.stamp + + @@ -346,4 +351,87 @@ Text="Analyzers must only target netstandard2.0 since they run in the compiler which targets netstandard2.0. The following files were found to target '%(_AnalyzerPackFile.TargetFramework)': @(_AnalyzerPackFile)" /> + + + + + + + + + + <_DotNetInstallScriptPath Condition="!$([MSBuild]::IsOSPlatform('windows'))">$(DOTNET_INSTALL_DIR)/dotnet-install.sh + <_DotNetInstallScriptPath Condition=" $([MSBuild]::IsOSPlatform('windows'))">$(DOTNET_INSTALL_DIR)/dotnet-install.ps1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_NuGetSourceForWorkloads Include="dotnet6" Value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" /> + + + + + + + + $(SdkPathForWorkloadTesting)packs\Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier)\$(PackageVersion) + + + + + + + + <_SdkPropsProperties Condition="!$([MSBuild]::IsOsPlatform('Windows'))" Include="ExeSuffix" Value="" /> + <_SdkPropsProperties Condition="$([MSBuild]::IsOsPlatform('Windows'))" Include="ExeSuffix" Value=".exe" /> + <_SdkPropsProperties Include="TargetRid" Value="browser-wasm" /> + + + + + + + + diff --git a/src/libraries/pretest.proj b/src/libraries/pretest.proj index 3e924e083abd51..fc2fee13d5c33c 100644 --- a/src/libraries/pretest.proj +++ b/src/libraries/pretest.proj @@ -22,6 +22,9 @@ + + + - <_ProjectsToBuild Include="$(PerScenarioProjectFile)"> + <_BaseProjectsToBuild Include="$(PerScenarioProjectFile)" Condition="'%(_Scenarios.Identity)' != 'buildwasmapps'"> $(_PropertiesToPass);Scenario=%(_Scenarios.Identity);TestArchiveRuntimeFile=$(TestArchiveRuntimeFile) - %(_ProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix) - + %(_BaseProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix) + + + + + <_TestValues Include="true;false" /> + + <_OtherProjectsToBuild Include="$(PerScenarioProjectFile)"> + $(_PropertiesToPass);Scenario=BuildWasmApps;TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);TestUsingWorkloads=%(_TestValues.Identity) + %(_OtherProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix) + + + + + <_ProjectsToBuild Include="@(_BaseProjectsToBuild);@(_OtherProjectsToBuild)" /> diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index 12d402fcb7e397..49d45b7c6053a0 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -7,6 +7,7 @@ true + true $(BUILD_BUILDNUMBER) @@ -53,6 +54,9 @@ $(WaitForWorkItemCompletion) true + + true + true @@ -97,7 +101,7 @@ true - true + true @@ -105,11 +109,21 @@ - - true - true - true - + + + + false + true + + + + + true + true + true + + + - powershell -command "New-SelfSignedCertificate -FriendlyName 'ASP.NET Core HTTPS development certificate' -DnsName @('localhost') -Subject 'CN = localhost' -KeyAlgorithm RSA -KeyLength 2048 -HashAlgorithm sha256 -CertStoreLocation 'Cert:\CurrentUser\My' -TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.1','1.3.6.1.4.1.311.84.1.1={hex}02','2.5.29.19={text}') -KeyUsage DigitalSignature,KeyEncipherment" && - - - $(HelixCommand)call RunTests.cmd - $(HelixCommand) --runtime-path %HELIX_CORRELATION_PAYLOAD% - - $(HelixCommand)./RunTests.sh - $(HelixCommand) --runtime-path "$HELIX_CORRELATION_PAYLOAD" - - @@ -224,6 +228,34 @@ + + @(HelixPreCommand) + $(HelixCommandPrefix) @(HelixCommandPrefixItem -> 'set %(Identity)', ' & ') + $(HelixCommandPrefix) @(HelixCommandPrefixItem, ' ') + true + + + + $(HelixCommandPrefix) + $(HelixCommandPrefix) & + + $(HelixCommand) dotnet dev-certs https && + + + $(HelixCommand) powershell -command "New-SelfSignedCertificate -FriendlyName 'ASP.NET Core HTTPS development certificate' -DnsName @('localhost') -Subject 'CN = localhost' -KeyAlgorithm RSA -KeyLength 2048 -HashAlgorithm sha256 -CertStoreLocation 'Cert:\CurrentUser\My' -TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.1','1.3.6.1.4.1.311.84.1.1={hex}02','2.5.29.19={text}') -KeyUsage DigitalSignature,KeyEncipherment" && + + + $(HelixCommand)call RunTests.cmd + $(HelixCommand) --runtime-path %HELIX_CORRELATION_PAYLOAD% + + $(HelixCommand)./RunTests.sh + $(HelixCommand) --runtime-path "$HELIX_CORRELATION_PAYLOAD" + + @@ -302,7 +334,15 @@ - + + + + + + + + @@ -311,7 +351,7 @@ - + @@ -374,7 +414,7 @@ - + @@ -392,6 +432,7 @@ + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 34588fb46c28cc..35dd69f071a68f 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -349,9 +349,11 @@ Condition="'$(TestTrimming)' == 'true'" AdditionalProperties="%(AdditionalProperties);SkipTrimmingProjectsRestore=true" /> + + @@ -378,10 +380,11 @@ BuildInParallel="false" /> - + + + BuildInParallel="true" /> diff --git a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj index 874775fe7e47f2..3db38ddbbe86a2 100644 --- a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj +++ b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Microsoft.NET.Runtime.WebAssembly.Sdk.pkgproj @@ -3,6 +3,7 @@ Provides the tasks+targets, for consumption by wasm based workloads + $(IntermediateOutputPath)AutoImport.props @@ -10,12 +11,23 @@ - + + + + <_AutoImportValues Include="PackageVersion" Value="$(PackageVersion)" /> + + + + + diff --git a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/AutoImport.props b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/AutoImport.props deleted file mode 100644 index f29ea9ae7da307..00000000000000 --- a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/AutoImport.props +++ /dev/null @@ -1,5 +0,0 @@ - - - true - - \ No newline at end of file diff --git a/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/AutoImport.props.in b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/AutoImport.props.in new file mode 100644 index 00000000000000..f925992ce31e4f --- /dev/null +++ b/src/mono/nuget/Microsoft.NET.Runtime.WebAssembly.Sdk/Sdk/AutoImport.props.in @@ -0,0 +1,8 @@ + + + true + true + ${PackageVersion} + $([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory), '..', '..', '..')) + + diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj index 0f6c403f4104e4..747da02070f2f9 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/Microsoft.NET.Workload.Mono.Toolchain.Manifest.pkgproj @@ -13,11 +13,12 @@ $(IntermediateOutputPath)WorkloadManifest.json + $(IntermediateOutputPath)WorkloadManifest.targets - + @@ -37,6 +38,11 @@ TemplateFile="WorkloadManifest.json.in" Properties="@(_WorkloadManifestValues)" OutputPath="$(WorkloadManifestPath)" /> + + diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index 4c50a08f007c82..b6d9d465f8a53a 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -1,5 +1,8 @@ { "version": "${WorkloadVersion}", + "depends-on": { + "Microsoft.NET.Workload.Emscripten": "${EmscriptenVersion}" + } "workloads": { "microsoft-net-sdk-blazorwebassembly-aot": { "description": "Browser Runtime native performance tools", @@ -11,7 +14,7 @@ "Microsoft.NET.Runtime.Emscripten.Python", "Microsoft.NET.Runtime.Emscripten.Sdk" ], - "extends": [ "microsoft-net-runtime-mono-tooling" ], + "extends": [ "microsoft-net-runtime-mono-tooling", "microsoft-net-sdk-emscripten" ], "platforms": [ "win-x64", "linux-x64", "osx-x64", "osx-arm64" ] }, "microsoft-net-runtime-android": { @@ -154,13 +157,13 @@ "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.android-arm64" } }, - "Microsoft.NETCore.App.Runtime.Mono.maccatalyst": { + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-arm64": { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-x64": { "kind": "framework", "version": "${PackageVersion}", - "alias-to": { - "osx-arm64": "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-arm64", - "osx-x64": "Microsoft.NETCore.App.Runtime.Mono.maccatalyst-x64" - } }, "Microsoft.NETCore.App.Runtime.Mono.ios-arm" : { "kind": "framework", @@ -170,54 +173,67 @@ "kind": "framework", "version": "${PackageVersion}" }, - "Microsoft.NETCore.App.Runtime.Mono.iossimulator" : { + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-arm64" : { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x64" : { "kind": "framework", "version": "${PackageVersion}", - "alias-to": { - "osx-arm64": "Microsoft.NETCore.App.Runtime.Mono.iossimulator-arm64", - "osx-x64": "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x64" - } }, "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x86" : { "kind": "framework", "version": "${PackageVersion}", - "alias-to": { - "osx-x64": "Microsoft.NETCore.App.Runtime.Mono.iossimulator-x86" - } }, "Microsoft.NETCore.App.Runtime.AOT.Cross.tvos-arm64": { "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { - "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvos-arm64", "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvos-arm64", + "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvos-arm64", } }, "Microsoft.NETCore.App.Runtime.Mono.tvos-arm64" : { "kind": "framework", "version": "${PackageVersion}" }, - "Microsoft.NETCore.App.Runtime.Mono.tvossimulator" : { + "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-arm64" : { "kind": "framework", "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-x64" : { + "kind": "framework", + "version": "${PackageVersion}", + }, + "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-arm64": { + "kind": "Sdk", + "version": "${PackageVersion}", "alias-to": { - "osx-arm64": "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-arm64", - "osx-x64": "Microsoft.NETCore.App.Runtime.Mono.tvossimulator-x64" + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-arm64", + "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-arm64" } }, - "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst": { + "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-x64": { "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { - "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-arm64", + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-x64", "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.maccatalyst-x64" } }, - "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator": { + "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-arm64": { "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvossimulator-arm64", + "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvossimulator-arm64" + } + }, + "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-x64": { + "kind": "Sdk", + "version": "${PackageVersion}", + "alias-to": { + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvossimulator-x64", "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.tvossimulator-x64" } }, @@ -237,11 +253,19 @@ "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.ios-arm64", } }, - "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator": { + "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-arm64": { "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-arm64", + "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-arm64" + } + }, + "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-x64": { + "kind": "Sdk", + "version": "${PackageVersion}", + "alias-to": { + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-x64", "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-x64" } }, @@ -249,6 +273,7 @@ "kind": "Sdk", "version": "${PackageVersion}", "alias-to": { + "osx-arm64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-x86", "osx-x64": "Microsoft.NETCore.App.Runtime.AOT.osx-x64.Cross.iossimulator-x86" } }, @@ -266,34 +291,5 @@ "kind": "framework", "version": "${PackageVersion}" }, - "Microsoft.NET.Runtime.Emscripten.Node" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Node.osx-x64" - } - }, - "Microsoft.NET.Runtime.Emscripten.Python" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.win-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Python.osx-x64" - } - }, - "Microsoft.NET.Runtime.Emscripten.Sdk" : { - "kind": "Sdk", - "version": "${EmscriptenVersion}", - "alias-to": { - "win-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.win-x64", - "linux-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.linux-x64", - "osx-x64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64", - "osx-arm64": "Microsoft.NET.Runtime.Emscripten.2.0.23.Sdk.osx-x64" - } - } } -} \ No newline at end of file +} diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in similarity index 54% rename from src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets rename to src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in index 2102eabc36ce6d..c2cd58193ceb64 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.targets.in @@ -1,4 +1,9 @@ + + ${PackageVersion} + true + + true $(WasmNativeWorkload) @@ -20,27 +25,55 @@ + + - + + + - + + + + + + + + - - - - + + + <_MonoWorkloadTargetsMobile>true + <_MonoWorkloadRuntimePackPackageVersion>${PackageVersion} + + + + + + + + + + diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.props b/src/mono/wasm/build/WasmApp.LocalBuild.props index 01003f59172fa3..69a719a0e6d1e1 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.props +++ b/src/mono/wasm/build/WasmApp.LocalBuild.props @@ -22,6 +22,9 @@ + + true + <_NetCoreAppToolCurrent>net6.0 diff --git a/src/mono/wasm/build/WasmApp.LocalBuild.targets b/src/mono/wasm/build/WasmApp.LocalBuild.targets index 636f0b1d257b94..fac5252a4ef34a 100644 --- a/src/mono/wasm/build/WasmApp.LocalBuild.targets +++ b/src/mono/wasm/build/WasmApp.LocalBuild.targets @@ -37,14 +37,14 @@ false - - + $(MicrosoftNetCoreAppRuntimePackLocationToUse) + - + diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index e1de26e4a4cde8..218635961a4bc1 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -31,11 +31,6 @@ <_ExeExt Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">.exe true - $([MSBuild]::NormalizeDirectory($(NuGetPackageRoot), 'microsoft.netcore.app.runtime.mono.browser-wasm', '$(BundledNETCoreAppPackageVersion)')) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', 'browser-wasm')) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir))) - $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native')) - <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) <_EmccPropsPath>$(_WasmRuntimePackSrcDir)Emcc.props @@ -177,11 +172,6 @@ <_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" /> - - <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) - <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) - - <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> @@ -360,6 +350,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ + <_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm')) diff --git a/src/mono/wasm/build/WasmApp.targets b/src/mono/wasm/build/WasmApp.targets index 034287436892bf..73641884666315 100644 --- a/src/mono/wasm/build/WasmApp.targets +++ b/src/mono/wasm/build/WasmApp.targets @@ -81,7 +81,12 @@ false + + $([MSBuild]::NormalizeDirectory($(WorkloadPacksDir), 'Microsoft.NETCore.App.Runtime.Mono.browser-wasm', '$(RuntimePackInWorkloadVersion)')) + + $([MSBuild]::NormalizeDirectory($(NuGetPackageRoot), 'microsoft.netcore.app.runtime.mono.browser-wasm', '$(BundledNETCoreAppPackageVersion)')) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', 'browser-wasm')) $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir))) $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native')) diff --git a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj index cdd5e95a4e2108..3a7b66fac7389f 100644 --- a/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj +++ b/src/mono/wasm/data/aot-tests/ProxyProjectForAOTOnHelix.proj @@ -22,7 +22,7 @@ - diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index ffc2fb92825ba5..4c5983255908cc 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -107,7 +107,7 @@ <_EmccVersionRaw>%(_ReversedVersionLines.Identity) - <_EmccVersionRegexPattern>^ *emcc \([^\)]+\) *([^ \(]+) *\(([^\)]+)\)$ + <_EmccVersionRegexPattern>^ *emcc \([^\)]+\) *([0-9\.]+).*\(([^\)]+)\)$ <_EmccVersion>$([System.Text.RegularExpressions.Regex]::Match($(_EmccVersionRaw), $(_EmccVersionRegexPattern)).Groups[1].Value) <_EmccVersionHash>$([System.Text.RegularExpressions.Regex]::Match($(_EmccVersionRaw), $(_EmccVersionRegexPattern)).Groups[2].Value) diff --git a/src/tasks/Common/Utils.cs b/src/tasks/Common/Utils.cs index ea2aba607dd8e0..7b25107d56f983 100644 --- a/src/tasks/Common/Utils.cs +++ b/src/tasks/Common/Utils.cs @@ -185,12 +185,12 @@ internal static string CreateTemporaryBatchFile(string command) } #if NETCOREAPP - public static void DirectoryCopy(string sourceDir, string destDir, Func predicate) + public static void DirectoryCopy(string sourceDir, string destDir, Func? predicate=null) { string[] files = Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories); foreach (string file in files) { - if (!predicate(file)) + if (predicate != null && !predicate(file)) continue; string relativePath = Path.GetRelativePath(sourceDir, file); diff --git a/src/tasks/WorkloadBuildTasks/InstallWorkload.cs b/src/tasks/WorkloadBuildTasks/InstallWorkload.cs new file mode 100644 index 00000000000000..063862273ad053 --- /dev/null +++ b/src/tasks/WorkloadBuildTasks/InstallWorkload.cs @@ -0,0 +1,475 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +#nullable enable + +namespace Microsoft.Workload.Build.Tasks +{ + public class InstallWorkload : Task + { + [Required, NotNull] + public ITaskItem? WorkloadId { get; set; } + + [Required, NotNull] + public ITaskItem? ManifestPackage { get; set; } + + [Required, NotNull] + public ITaskItem? BuiltNuGetsPath { get; set; } + + [Required, NotNull] + public string? OutputDir { get; set; } + + public ITaskItem[]? ExtraNuGetSources { get; set; } + public string? Rid { get; set; } + + private readonly string _tempDir = Path.Combine(Path.GetTempPath(), "install-workload", Path.GetRandomFileName()); + private string? _packsDir; + private const string s_stampFileName = ".installed.stamp"; + + private static string? GetRid() + { + if (OperatingSystem.IsWindows()) + return Environment.Is64BitProcess ? "win-x64": "win-x86"; + else if (OperatingSystem.IsMacOS()) + return "osx-x64"; + else if (OperatingSystem.IsLinux()) + return "linux-x64"; + else + return null; + } + + public override bool Execute() + { + Utils.Logger = Log; + + if (!HasMetadata(ManifestPackage, nameof(ManifestPackage), "VersionBand") || + !HasMetadata(ManifestPackage, nameof(ManifestPackage), "Version") || + !HasMetadata(WorkloadId, nameof(WorkloadId), "Name")) + { + return false; + } + + if (!Directory.Exists(OutputDir)) + { + Log.LogError($"Cannot find OutputDir={OutputDir}"); + return false; + } + + _packsDir = Path.Combine(OutputDir, "packs"); + Rid ??= GetRid(); + if (Rid == null) + { + Log.LogError("Unsupported platform"); + return false; + } + + if (!InstallWorkloadManifest(out ManifestInformation? manifest, out string? manifestNupkgPath)) + return false; + + IEnumerable references = GetPackageReferencesForWorkload(manifest, WorkloadId.ItemSpec); + IEnumerable remaining = LayoutPacksFromBuiltNuGets(references); + if (!remaining.Any()) + return !Log.HasLoggedErrors; + + if (!InstallPacksWithNuGetRestore(remaining)) + return false; + + return !Log.HasLoggedErrors; + } + + private bool InstallWorkloadManifest([NotNullWhen(true)] out ManifestInformation? manifest, [NotNullWhen(true)] out string? manifestNupkgPath) + { + manifest = null; + manifestNupkgPath = null; + + string builtNuGetsFullPath = BuiltNuGetsPath.GetMetadata("FullPath"); + string pkgName = ManifestPackage.ItemSpec; + string pkgVersion = ManifestPackage.GetMetadata("Version"); + + var nupkgFileName = $"{pkgName}.{pkgVersion}.nupkg"; + var nupkgPath = Path.Combine(builtNuGetsFullPath, nupkgFileName); + if (!File.Exists(nupkgPath)) + { + Log.LogError($"Could not find nupkg for the manifest at {nupkgPath}"); + return false; + } + + string baseManifestDir = Path.Combine(OutputDir, "sdk-manifests"); + + string tmpManifestDir = Path.Combine(_tempDir, "manifest"); + ZipFile.ExtractToDirectory(nupkgPath, tmpManifestDir); + + var sourceManifestDirectory = Path.Combine(tmpManifestDir, "data"); + var targetManifestDirectory = Path.Combine(baseManifestDir, ManifestPackage.GetMetadata("VersionBand"), WorkloadId.GetMetadata("Name")); + if (!CopyDirectory(sourceManifestDirectory, targetManifestDirectory)) + return false; + + string jsonPath = Path.Combine(targetManifestDirectory, "WorkloadManifest.json"); + if (!File.Exists(jsonPath)) + { + Log.LogError($"Could not find WorkloadManifest.json at {jsonPath}"); + return false; + } + + manifest = JsonSerializer.Deserialize( + File.ReadAllBytes(jsonPath), + new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip + }); + + if (manifest == null) + { + Log.LogError($"Could not parse manifest from {jsonPath}."); + return false; + } + + manifestNupkgPath = nupkgPath; + return true; + } + + private IEnumerable LayoutPacksFromBuiltNuGets(IEnumerable references) + { + string builtNuGetsFullPath = BuiltNuGetsPath.GetMetadata("FullPath"); + + var allFiles = string.Join($"{Environment.NewLine} ", Directory.EnumerateFiles(builtNuGetsFullPath, "*", new EnumerationOptions { RecurseSubdirectories = true })); + Log.LogMessage(MessageImportance.Low, $"Files in {builtNuGetsFullPath}: {allFiles}"); + + List remaining = new(references.Count()); + foreach (var reference in references) + { + var nupkgFileName = $"{reference.Name}.{reference.Version}.nupkg"; + var nupkgPath = Path.Combine(builtNuGetsFullPath, nupkgFileName); + if (!File.Exists(nupkgPath)) + { + string[] found = Directory.GetFiles(builtNuGetsFullPath, nupkgFileName, new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true + }); + + if (found.Length == 0) + { + remaining.Add(reference); + continue; + } + + nupkgPath = found[0]; + nupkgFileName = Path.GetFileName(nupkgPath); + } + + string installedPackDir = Path.Combine(_packsDir!, reference.Name, reference.Version); + string stampFilePath = Path.Combine(installedPackDir, s_stampFileName); + if (!IsFileNewer(nupkgPath, stampFilePath)) + { + Log.LogMessage(MessageImportance.Normal, $"Skipping {reference.Name}/{reference.Version} as it is already installed in {installedPackDir}.{Environment.NewLine} {nupkgPath} is older than {stampFilePath}"); + continue; + } + + if (Directory.Exists(installedPackDir)) + { + Log.LogMessage(MessageImportance.Normal, $"Deleting {installedPackDir}"); + Directory.Delete(installedPackDir, recursive: true); + } + + Log.LogMessage(MessageImportance.High, $"Extracting {nupkgPath} => {installedPackDir}"); + ZipFile.ExtractToDirectory(nupkgPath, installedPackDir); + + // Add .nupkg.sha512, so it gets picked up when resolving nugets + File.WriteAllText(Path.Combine(installedPackDir, $"{nupkgFileName}.sha512"), string.Empty); + + File.WriteAllText(stampFilePath, string.Empty); + } + + return remaining; + } + + private IEnumerable GetPackageReferencesForWorkload(ManifestInformation manifest, string workloadId) + { + var workload = manifest.Workloads[workloadId]; + var subset = workload.Packs; + if (workload.Extends.Count > 0) + { + subset = new List(subset); + //FIXME: use exceptions! + if (!ProcessWorkload(workload)) + throw new KeyNotFoundException(); + } + + List references = new(); + foreach (var item in manifest.Packs) + { + if (subset != null && !subset.Contains(item.Key)) + { + Log.LogMessage(MessageImportance.Low, $"Ignoring pack {item.Key} as it is not in the workload"); + continue; + } + + var packageName = item.Key; + if (item.Value.AliasTo is Dictionary alias) + { + alias.TryGetValue(Rid!, out packageName); + } + + if (!string.IsNullOrEmpty(packageName) && !packageName.Contains("cross", StringComparison.InvariantCultureIgnoreCase)) + references.Add(new PackageReference(packageName, item.Value.Version)); + } + + return references; + + bool ProcessWorkload(WorkloadInformation workload) + { + if (workload.Extends == null || workload.Extends.Count == 0) + return true; + + foreach (var w in workload.Extends) + { + if (!manifest.Workloads.TryGetValue(w, out WorkloadInformation? depWorkload)) + { + Log.LogError($"Could not find workload {w} needed by {workload.Description} in the manifest"); + return false; + } + //FIXME: + + if (!ProcessWorkload(depWorkload)) + return false; + + subset.AddRange(depWorkload.Packs); + } + + return true; + } + } + + private bool InstallPacksWithNuGetRestore(IEnumerable references) + { + var remaining = SkipInstalledPacks(references); + if (!remaining.Any()) + return true; + + return TryRestorePackages(remaining, out PackageReference[]? restored) && + LayoutPacksFromRestoredNuGets(restored); + + IEnumerable SkipInstalledPacks(IEnumerable candidates) + { + List needed = new List(candidates.Count()); + foreach (PackageReference pr in candidates) + { + var installedPackDir = Path.Combine(_packsDir!, pr.Name, pr.Version); + var packStampFile = Path.Combine(installedPackDir, s_stampFileName); + + if (File.Exists(packStampFile)) + { + Log.LogMessage(MessageImportance.Normal, $"Skipping {pr.Name}/{pr.Version} as it is already installed in {installedPackDir}.{Environment.NewLine} {packStampFile} exists."); + continue; + } + + needed.Add(pr); + } + + return needed; + } + + bool LayoutPacksFromRestoredNuGets(IEnumerable restored) + { + foreach (var pkgRef in restored) + { + if (pkgRef.RestoredPath == null) + { + Log.LogError($"Failed to restore {pkgRef.Name}/{pkgRef.Version}"); + return false; + } + + var source = pkgRef.RestoredPath; + var destDir = Path.Combine(_packsDir!, pkgRef.Name, pkgRef.Version); + if (!CopyDirectory(source, destDir)) + return false; + } + + return true; + } + } + + private bool TryRestorePackages(IEnumerable references, [NotNullWhen(true)]out PackageReference[]? restoredPackages) + { + if (!references.Any()) + { + restoredPackages = Array.Empty(); + return true; + } + + restoredPackages = null; + + var restoreProject = Path.Combine(_tempDir, "restore", "Restore.csproj"); + var restoreProjectDirectory = Directory.CreateDirectory(Path.GetDirectoryName(restoreProject)!); + + File.WriteAllText(Path.Combine(restoreProjectDirectory.FullName, "Directory.Build.props"), ""); + File.WriteAllText(Path.Combine(restoreProjectDirectory.FullName, "Directory.Build.targets"), ""); + + StringBuilder projectFileBuilder = new(); + projectFileBuilder.Append(@" + + + net6.0 + $(NoWarn);NU1213 + + +"); + + foreach (var reference in references) + { + string itemName = "PackageReference"; + projectFileBuilder.AppendLine($"<{itemName} Include=\"{reference.Name}\" Version=\"{reference.Version}\" />"); + } + + projectFileBuilder.Append(@" + + +"); + File.WriteAllText(restoreProject, projectFileBuilder.ToString()); + + if (ExtraNuGetSources?.Length > 0) + { + StringBuilder nugetConfigBuilder = new(); + nugetConfigBuilder.AppendLine($"{Environment.NewLine}"); + + foreach (ITaskItem source in ExtraNuGetSources) + { + string key = source.ItemSpec; + string value = source.GetMetadata("Value"); + if (string.IsNullOrEmpty(value)) + { + Log.LogError($"ExtraNuGetSource {key} is missing Value metadata"); + return false; + } + + nugetConfigBuilder.AppendLine($@""); + } + + nugetConfigBuilder.AppendLine($"{Environment.NewLine}"); + + File.WriteAllText(Path.Combine(restoreProjectDirectory.FullName, "nuget.config"), nugetConfigBuilder.ToString()); + } + + string restoreDir = Path.Combine(_tempDir, "nuget-packages"); + if (Directory.Exists(restoreDir)) + { + Log.LogMessage(MessageImportance.Low, $"Deleting {restoreDir}"); + Directory.Delete(restoreDir, recursive: true); + } + + Log.LogMessage(MessageImportance.High, $"Restoring packages: {string.Join(", ", references.Select(r => $"{r.Name}/{r.Version}"))}"); + + string args = $"restore {restoreProject} /p:RestorePackagesPath={restoreDir}"; + (int exitCode, string output) = Utils.TryRunProcess("dotnet", args, silent: false, debugMessageImportance: MessageImportance.Normal); + if (exitCode != 0) + { + Log.LogError($"Restoring packages returned exit code: {exitCode}. Output:{Environment.NewLine}{output}"); + return false; + } + + restoredPackages = references.Select(reference => + { + var expectedPath = Path.Combine(restoreDir, reference.Name.ToLower(), reference.Version); + if (Directory.Exists(expectedPath)) + { + reference = reference with { RestoredPath = expectedPath }; + File.WriteAllText(Path.Combine(reference.RestoredPath, s_stampFileName), string.Empty); + } + return reference; + }).ToArray(); + + return true; + } + + private bool CopyDirectory(string srcDir, string destDir) + { + try + { + Log.LogMessage(MessageImportance.Low, $"Copying {srcDir} to {destDir}"); + if (Directory.Exists(destDir)) + { + Log.LogMessage(MessageImportance.Normal, $"Deleting {destDir}"); + Directory.Delete(destDir, recursive: true); + } + + Directory.CreateDirectory(destDir); + Utils.DirectoryCopy(srcDir, destDir); + + return true; + } + catch (Exception ex) + { + Log.LogError($"Failed while copying {srcDir} => {destDir}: {ex.Message}"); + if (ex is IOException) + return false; + + throw; + } + } + + private bool HasMetadata(ITaskItem item, string itemName, string metadataName) + { + if (!string.IsNullOrEmpty(item.GetMetadata(metadataName))) + return true; + + Log.LogError($"{itemName} item ({item.ItemSpec}) is missing Name metadata"); + return false; + } + + private static bool IsFileNewer(string sourceFile, string stampFile) + { + if (!File.Exists(sourceFile)) + return true; + + if (!File.Exists(stampFile)) + return true; + + return File.GetLastWriteTimeUtc(sourceFile) > File.GetLastWriteTimeUtc(stampFile); + } + + private record PackageReference(string Name, string Version, string? RestoredPath=null); + + private record ManifestInformation( + object Version, + string Description, + + [property: JsonPropertyName("depends-on")] + IDictionary DependsOn, + IDictionary Workloads, + IDictionary Packs, + object Data + ); + + private record WorkloadInformation( + bool Abstract, + string Kind, + string Description, + + List Packs, + List Extends, + List Platforms + ); + + private record PackVersionInformation( + string Kind, + string Version, + [property: JsonPropertyName("alias-to")] + Dictionary AliasTo + ); + } +} diff --git a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj index 328672b45145bc..537418ef31dc6d 100644 --- a/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj +++ b/src/tasks/WorkloadBuildTasks/WorkloadBuildTasks.csproj @@ -6,6 +6,8 @@ $(NoWarn),CA1050 + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs new file mode 100644 index 00000000000000..891386e2e1e17d --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs @@ -0,0 +1,49 @@ +// 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 FluentAssertions; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class BlazorWasmTests : BuildTestBase + { + public BlazorWasmTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))] + public void PublishTemplateProject() + { + InitPaths("id"); + if (Directory.Exists(_projectDir)) + Directory.Delete(_projectDir, recursive: true); + Directory.CreateDirectory(_projectDir); + Directory.CreateDirectory(Path.Combine(_projectDir, ".nuget")); + + File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "nuget6.config"), Path.Combine(_projectDir, "nuget.config")); + File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.props"), Path.Combine(_projectDir, "Directory.Build.props")); + File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.targets"), Path.Combine(_projectDir, "Directory.Build.targets")); + + new DotNetCommand(s_buildEnv) + .WithWorkingDirectory(_projectDir) + .ExecuteWithCapturedOutput("new blazorwasm") + .EnsureSuccessful(); + + new DotNetCommand(s_buildEnv) + .WithWorkingDirectory(_projectDir) + .ExecuteWithCapturedOutput("publish -bl -p:RunAOTCompilation=true") + .EnsureSuccessful(); + + //TODO: validate the build somehow? + // compare dotnet.wasm? + // playwright? + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs new file mode 100644 index 00000000000000..0b9d74868e9861 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildEnvironment.cs @@ -0,0 +1,132 @@ + +using System; +using System.Collections.Generic; +using System.IO; + +namespace Wasm.Build.Tests +{ + public class BuildEnvironment + { + public string DotNet { get; init; } + public string RuntimePackDir { get; init; } + public bool IsWorkload { get; init; } + public string DefaultBuildArgs { get; init; } + public IDictionary EnvVars { get; init; } + public string DirectoryBuildPropsContents { get; init; } + public string DirectoryBuildTargetsContents { get; init; } + public string RuntimeNativeDir { get; init; } + public string LogRootPath { get; init; } + + public static readonly string RelativeTestAssetsPath = @"..\testassets\"; + public static readonly string TestAssetsPath = Path.Combine(AppContext.BaseDirectory, "testassets"); + public static readonly string TestDataPath = Path.Combine(AppContext.BaseDirectory, "data"); + + private static string s_runtimeConfig = "Release"; + private const string s_testLogPathEnvVar = "TEST_LOG_PATH"; + + public BuildEnvironment() + { + DirectoryInfo? solutionRoot = new (AppContext.BaseDirectory); + while (solutionRoot != null) + { + if (File.Exists(Path.Combine(solutionRoot.FullName, "NuGet.config"))) + { + break; + } + + solutionRoot = solutionRoot.Parent; + } + + string? sdkForWorkloadPath = Environment.GetEnvironmentVariable("SDK_FOR_WORKLOAD_TESTING_PATH"); + if (!string.IsNullOrEmpty(sdkForWorkloadPath)) + { + + DotNet = Path.Combine(sdkForWorkloadPath, "dotnet"); + var workloadPacksVersion = Environment.GetEnvironmentVariable("WORKLOAD_PACKS_VER"); + if (string.IsNullOrEmpty(workloadPacksVersion)) + throw new Exception($"Cannot test with workloads without WORKLOAD_PACKS_VER environment variable being set"); + + RuntimePackDir = Path.Combine(sdkForWorkloadPath, "packs", "Microsoft.NETCore.App.Runtime.Mono.browser-wasm", workloadPacksVersion); + DirectoryBuildPropsContents = s_directoryBuildPropsForWorkloads; + DirectoryBuildTargetsContents = s_directoryBuildTargetsForWorkloads; + EnvVars = new Dictionary() + { + // `runtime` repo's build environment sets these, and they + // mess up the build for the test project, which is using a different + // dotnet + ["DOTNET_INSTALL_DIR"] = sdkForWorkloadPath, + ["DOTNET_MULTILEVEL_LOOKUP"] = "0", + ["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1", + ["MSBuildSDKsPath"] = string.Empty, + ["PATH"] = $"{sdkForWorkloadPath}{Path.PathSeparator}{Environment.GetEnvironmentVariable("PATH")}" + }; + + var appRefDir = Environment.GetEnvironmentVariable("AppRefDir"); + if (string.IsNullOrEmpty(appRefDir)) + throw new Exception($"Cannot test with workloads without AppRefDir environment variable being set"); + + DefaultBuildArgs = $" /p:AppRefDir={appRefDir}"; + IsWorkload = true; + } + else + { + string emsdkPath; + if (solutionRoot == null) + { + string? buildDir = Environment.GetEnvironmentVariable("WasmBuildSupportDir"); + + if (buildDir == null || !Directory.Exists(buildDir)) + throw new Exception($"Could not find the solution root, or a build dir: {buildDir}"); + + emsdkPath = Path.Combine(buildDir, "emsdk"); + RuntimePackDir = Path.Combine(buildDir, "microsoft.netcore.app.runtime.browser-wasm"); + DefaultBuildArgs = $" /p:WasmBuildSupportDir={buildDir} /p:EMSDK_PATH={emsdkPath} "; + } + else + { + string artifactsBinDir = Path.Combine(solutionRoot.FullName, "artifacts", "bin"); + RuntimePackDir = Path.Combine(artifactsBinDir, "microsoft.netcore.app.runtime.browser-wasm", s_runtimeConfig); + + string? emsdkEnvValue = Environment.GetEnvironmentVariable("EMSDK_PATH"); + if (string.IsNullOrEmpty(emsdkEnvValue)) + emsdkPath = Path.Combine(solutionRoot.FullName, "src", "mono", "wasm", "emsdk"); + else + emsdkPath = emsdkEnvValue; + + DefaultBuildArgs = $" /p:RuntimeSrcDir={solutionRoot.FullName} /p:RuntimeConfig={s_runtimeConfig} /p:EMSDK_PATH={emsdkPath} "; + } + + IsWorkload = false; + DotNet = "dotnet"; + EnvVars = new Dictionary() + { + ["EMSDK_PATH"] = emsdkPath + }; + + DirectoryBuildPropsContents = s_directoryBuildPropsForLocal; + DirectoryBuildTargetsContents = s_directoryBuildTargetsForLocal; + } + + RuntimeNativeDir = Path.Combine(RuntimePackDir, "runtimes", "browser-wasm", "native"); + + string? logPathEnvVar = Environment.GetEnvironmentVariable(s_testLogPathEnvVar); + if (!string.IsNullOrEmpty(logPathEnvVar)) + { + LogRootPath = logPathEnvVar; + if (!Directory.Exists(LogRootPath)) + { + Directory.CreateDirectory(LogRootPath); + } + } + else + { + LogRootPath = Environment.CurrentDirectory; + } + } + + protected static string s_directoryBuildPropsForWorkloads = File.ReadAllText(Path.Combine(TestDataPath, "Workloads.Directory.Build.props")); + protected static string s_directoryBuildTargetsForWorkloads = File.ReadAllText(Path.Combine(TestDataPath, "Workloads.Directory.Build.targets")); + 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")); + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BuildTestBase.cs index b87ff5c70bfc24..021ce531b17b32 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.Reflection; using System.Text; +using System.Text.RegularExpressions; using Xunit; using Xunit.Abstractions; using Xunit.Sdk; @@ -20,15 +21,9 @@ namespace Wasm.Build.Tests { public abstract class BuildTestBase : IClassFixture, IDisposable { - protected const string TestLogPathEnvVar = "TEST_LOG_PATH"; protected const string SkipProjectCleanupEnvVar = "SKIP_PROJECT_CLEANUP"; protected const string XHarnessRunnerCommandEnvVar = "XHARNESS_CLI_PATH"; - protected const string s_targetFramework = "net5.0"; - protected static string s_runtimeConfig = "Release"; - protected static string s_runtimePackDir; - protected static string s_defaultBuildArgs; - protected static readonly string s_logRoot; - protected static readonly string s_emsdkPath; + protected const string s_targetFramework = "net6.0"; protected static readonly bool s_skipProjectCleanup; protected static readonly string s_xharnessRunnerCommand; protected string? _projectDir; @@ -37,76 +32,53 @@ public abstract class BuildTestBase : IClassFixture s_buildEnv.IsWorkload; + public static bool IsNotUsingWorkloads => !s_buildEnv.IsWorkload; - if (buildDir == null || !Directory.Exists(buildDir)) - throw new Exception($"Could not find the solution root, or a build dir: {buildDir}"); - - s_emsdkPath = Path.Combine(buildDir, "emsdk"); - s_runtimePackDir = Path.Combine(buildDir, "microsoft.netcore.app.runtime.browser-wasm"); - s_defaultBuildArgs = $" /p:WasmBuildSupportDir={buildDir} /p:EMSDK_PATH={s_emsdkPath} "; - } - else - { - string artifactsBinDir = Path.Combine(solutionRoot.FullName, "artifacts", "bin"); - s_runtimePackDir = Path.Combine(artifactsBinDir, "microsoft.netcore.app.runtime.browser-wasm", s_runtimeConfig); + static BuildTestBase() + { + s_buildEnv = new BuildEnvironment(); + s_runtimePackPathRegex = new Regex(s_runtimePackPathPattern); - string? emsdk = Environment.GetEnvironmentVariable("EMSDK_PATH"); - if (string.IsNullOrEmpty(emsdk)) - emsdk = Path.Combine(solutionRoot.FullName, "src", "mono", "wasm", "emsdk"); - s_emsdkPath = emsdk; + string? cleanupVar = Environment.GetEnvironmentVariable(SkipProjectCleanupEnvVar); + s_skipProjectCleanup = !string.IsNullOrEmpty(cleanupVar) && cleanupVar == "1"; - s_defaultBuildArgs = $" /p:RuntimeSrcDir={solutionRoot.FullName} /p:RuntimeConfig={s_runtimeConfig} /p:EMSDK_PATH={s_emsdkPath} "; - } + s_xharnessRunnerCommand = GetEnvironmentVariableOrDefault(XHarnessRunnerCommandEnvVar, "xharness"); - string? logPathEnvVar = Environment.GetEnvironmentVariable(TestLogPathEnvVar); - if (!string.IsNullOrEmpty(logPathEnvVar)) + string? nugetPackagesPath = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); + if (!string.IsNullOrEmpty(nugetPackagesPath)) { - s_logRoot = logPathEnvVar; - if (!Directory.Exists(s_logRoot)) + if (!Directory.Exists(nugetPackagesPath)) + { + Directory.CreateDirectory(nugetPackagesPath); + Console.WriteLine ($"-- Created {nugetPackagesPath}"); + } + else { - Directory.CreateDirectory(s_logRoot); + Console.WriteLine ($"-- already exists {nugetPackagesPath}"); } } else { - s_logRoot = Environment.CurrentDirectory; + Console.WriteLine ($"-- NUGET_PACKAGES envvar was empty"); } - string? cleanupVar = Environment.GetEnvironmentVariable(SkipProjectCleanupEnvVar); - s_skipProjectCleanup = !string.IsNullOrEmpty(cleanupVar) && cleanupVar == "1"; - - string? harnessVar = Environment.GetEnvironmentVariable(XHarnessRunnerCommandEnvVar); - if (string.IsNullOrEmpty(harnessVar)) - { - s_xharnessRunnerCommand = "xharness"; - } - else - { - s_xharnessRunnerCommand = $"exec {harnessVar}"; - } + Console.WriteLine (""); + Console.WriteLine ($"=============================================================================================="); + Console.WriteLine ($"=============== Running with {(s_buildEnv.IsWorkload ? "Workloads" : "EMSDK")} ==============="); + Console.WriteLine ($"=============================================================================================="); + Console.WriteLine (""); } public BuildTestBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) { _buildContext = buildContext; _testOutput = output; - _logPath = s_logRoot; // FIXME: + _logPath = s_buildEnv.LogRootPath; // FIXME: } /* @@ -154,11 +126,16 @@ protected string RunAndTestWasmApp(BuildArgs buildArgs, envVars["XHARNESS_DISABLE_COLORED_OUTPUT"] = "true"; if (buildArgs.AOT) { - envVars["EMSDK_PATH"] = s_emsdkPath; envVars["MONO_LOG_LEVEL"] = "debug"; envVars["MONO_LOG_MASK"] = "aot"; } + if (s_buildEnv.EnvVars != null) + { + foreach (var kvp in s_buildEnv.EnvVars) + envVars[kvp.Key] = kvp.Value; + } + string bundleDir = Path.Combine(GetBinDir(baseDir: buildDir, config: buildArgs.Config), "AppBundle"); (string testCommand, string extraXHarnessArgs) = host switch { @@ -215,7 +192,7 @@ protected static string RunWithXHarness(string testCommand, string testLogPath, // App arguments if (envVars != null) { - var setenv = string.Join(' ', envVars.Select(kvp => $"--setenv={kvp.Key}={kvp.Value}").ToArray()); + var setenv = string.Join(' ', envVars.Select(kvp => $"\"--setenv={kvp.Key}={kvp.Value}\"").ToArray()); args.Append($" {setenv}"); } @@ -224,7 +201,7 @@ protected static string RunWithXHarness(string testCommand, string testLogPath, _testOutput.WriteLine(string.Empty); _testOutput.WriteLine($"---------- Running with {testCommand} ---------"); - var (exitCode, output) = RunProcess("dotnet", _testOutput, + var (exitCode, output) = RunProcess(s_buildEnv.DotNet, _testOutput, args: args.ToString(), workingDir: bundleDir, envVars: envVars, @@ -235,7 +212,8 @@ protected static string RunWithXHarness(string testCommand, string testLogPath, if (exitCode != xharnessExitCode) { _testOutput.WriteLine($"Exit code: {exitCode}"); - Assert.True(exitCode == expectedAppExitCode, $"[{testCommand}] Exit code, expected {expectedAppExitCode} but got {exitCode}"); + if (exitCode != expectedAppExitCode) + throw new XunitException($"[{testCommand}] Exit code, expected {expectedAppExitCode} but got {exitCode} for command: {testCommand} {args}"); } return output; @@ -245,7 +223,7 @@ protected static string RunWithXHarness(string testCommand, string testLogPath, protected void InitPaths(string id) { _projectDir = Path.Combine(AppContext.BaseDirectory, id); - _logPath = Path.Combine(s_logRoot, id); + _logPath = Path.Combine(s_buildEnv.LogRootPath, id); Directory.CreateDirectory(_logPath); } @@ -253,8 +231,11 @@ protected void InitPaths(string id) protected static void InitProjectDir(string dir) { Directory.CreateDirectory(dir); - File.WriteAllText(Path.Combine(dir, "Directory.Build.props"), s_directoryBuildProps); - File.WriteAllText(Path.Combine(dir, "Directory.Build.targets"), s_directoryBuildTargets); + File.WriteAllText(Path.Combine(dir, "Directory.Build.props"), s_buildEnv.DirectoryBuildPropsContents); + File.WriteAllText(Path.Combine(dir, "Directory.Build.targets"), s_buildEnv.DirectoryBuildTargetsContents); + + File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "nuget6.config"), Path.Combine(dir, "nuget.config")); + Directory.CreateDirectory(Path.Combine(dir, ".nuget")); } protected const string SimpleProjectTemplate = @@ -285,8 +266,8 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp } public (string projectDir, string buildOutput) BuildProject(BuildArgs buildArgs, - Action initProject, string id, + Action? initProject = null, bool? dotnetWasmFromRuntimePack = null, bool hasIcudt = true, bool useCache = true, @@ -301,7 +282,7 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp _projectDir = product.ProjectDir; // use this test's id for the run logs - _logPath = Path.Combine(s_logRoot, id); + _logPath = Path.Combine(s_buildEnv.LogRootPath, id); return (_projectDir, "FIXME"); } @@ -321,13 +302,14 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp StringBuilder sb = new(); sb.Append("publish"); - sb.Append(s_defaultBuildArgs); + sb.Append($" {s_buildEnv.DefaultBuildArgs}"); sb.Append($" /p:Configuration={buildArgs.Config}"); string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}.binlog"); _testOutput.WriteLine($"-------- Building ---------"); _testOutput.WriteLine($"Binlog path: {logFilePath}"); + Console.WriteLine($"Binlog path: {logFilePath}"); sb.Append($" /bl:\"{logFilePath}\" /v:minimal /nologo"); if (buildArgs.ExtraBuildArgs != null) sb.Append($" {buildArgs.ExtraBuildArgs} "); @@ -337,12 +319,16 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp (int exitCode, string buildOutput) result; try { - result = AssertBuild(sb.ToString(), id, expectSuccess: expectSuccess); + result = AssertBuild(sb.ToString(), id, expectSuccess: expectSuccess, envVars: s_buildEnv.EnvVars); + + //AssertRuntimePackPath(result.buildOutput); + + // check that we are using the correct runtime pack! + if (expectSuccess) { string bundleDir = Path.Combine(GetBinDir(config: buildArgs.Config), "AppBundle"); - dotnetWasmFromRuntimePack ??= !buildArgs.AOT; - AssertBasicAppBundle(bundleDir, buildArgs.ProjectName, buildArgs.Config, hasIcudt, dotnetWasmFromRuntimePack.Value); + AssertBasicAppBundle(bundleDir, buildArgs.ProjectName, buildArgs.Config, hasIcudt, dotnetWasmFromRuntimePack ?? !buildArgs.AOT); } if (useCache) @@ -361,8 +347,20 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp } } + static void AssertRuntimePackPath(string buildOutput) + { + var match = s_runtimePackPathRegex.Match(buildOutput); + if (!match.Success || match.Groups.Count != 2) + throw new XunitException($"Could not find the pattern in the build output: '{s_runtimePackPathPattern}'.{Environment.NewLine}Build output: {buildOutput}"); + + string actualPath = match.Groups[1].Value; + if (string.Compare(actualPath, s_buildEnv.RuntimePackDir) != 0) + throw new XunitException($"Runtime pack path doesn't match.{Environment.NewLine}Expected: {s_buildEnv.RuntimePackDir}{Environment.NewLine}Actual: {actualPath}"); + } + 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", @@ -398,16 +396,15 @@ protected static void AssertBasicAppBundle(string bundleDir, string projectName, protected static void AssertDotNetWasmJs(string bundleDir, bool fromRuntimePack) { - string nativeDir = GetRuntimeNativeDir(); - - AssertNativeFile("dotnet.wasm"); - AssertNativeFile("dotnet.js"); - - void AssertNativeFile(string file) - => AssertFile(Path.Combine(nativeDir, file), - Path.Combine(bundleDir, file), - $"Expected {file} to be {(fromRuntimePack ? "the same as" : "different from")} the runtime pack", - same: fromRuntimePack); + AssertFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.wasm"), + Path.Combine(bundleDir, "dotnet.wasm"), + "Expected dotnet.wasm to be same as the runtime pack", + same: fromRuntimePack); + + AssertFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.js"), + Path.Combine(bundleDir, "dotnet.js"), + "Expected dotnet.js to be same as the runtime pack", + same: fromRuntimePack); } protected static void AssertFilesDontExist(string dir, string[] filenames, string? label = null) @@ -454,9 +451,9 @@ protected static void AssertFile(string file0, string file1, string? label=null, Assert.True(finfo0.Length != finfo1.Length, $"{label}: File sizes should not match for {file0} ({finfo0.Length}), and {file1} ({finfo1.Length})"); } - protected (int exitCode, string buildOutput) AssertBuild(string args, string label="build", bool expectSuccess=true) + protected (int exitCode, string buildOutput) AssertBuild(string args, string label="build", bool expectSuccess=true, IDictionary? envVars=null) { - var result = RunProcess("dotnet", _testOutput, args, workingDir: _projectDir, label: label); + var result = RunProcess(s_buildEnv.DotNet, _testOutput, args, workingDir: _projectDir, label: label, envVars: envVars); if (expectSuccess) Assert.True(0 == result.exitCode, $"Build process exited with non-zero exit code: {result.exitCode}"); else @@ -465,9 +462,6 @@ protected static void AssertFile(string file0, string file1, string? label=null, return result; } - // protected string GetObjDir(string targetFramework=s_targetFramework, string? baseDir=null, string config="Debug") - // => Path.Combine(baseDir ?? _projectDir, "obj", config, targetFramework, "browser-wasm", "wasm"); - protected string GetBinDir(string config, string targetFramework=s_targetFramework, string? baseDir=null) { var dir = baseDir ?? _projectDir; @@ -475,12 +469,6 @@ protected string GetBinDir(string config, string targetFramework=s_targetFramewo return Path.Combine(dir!, "bin", config, targetFramework, "browser-wasm"); } - protected static string GetRuntimePackDir() => s_runtimePackDir; - - protected static string GetRuntimeNativeDir() - => Path.Combine(GetRuntimePackDir(), "runtimes", "browser-wasm", "native"); - - public static (int exitCode, string buildOutput) RunProcess(string path, ITestOutputHelper _testOutput, string args = "", @@ -570,6 +558,12 @@ public void Dispose() _buildContext.RemoveFromCache(_projectDir); } + private static string GetEnvironmentVariableOrDefault(string envVarName, string defaultValue) + { + string? value = Environment.GetEnvironmentVariable(envVarName); + return string.IsNullOrEmpty(value) ? defaultValue : value; + } + protected static string s_mainReturns42 = @" public class TestClass { public static int Main() @@ -577,49 +571,6 @@ public static int Main() return 42; } }"; - - protected static string s_directoryBuildProps = @" - - <_WasmTargetsDir Condition=""'$(RuntimeSrcDir)' != ''"">$(RuntimeSrcDir)\src\mono\wasm\build\ - <_WasmTargetsDir Condition=""'$(WasmBuildSupportDir)' != ''"">$(WasmBuildSupportDir)\wasm\ - $(WasmBuildSupportDir)\emsdk\ - - - - - - - PrepareForWasmBuild;$(WasmBuildAppDependsOn) - -"; - - protected static string s_directoryBuildTargets = @" - - - - - - - - - - - - - - - - - - -"; - } public record BuildArgs(string ProjectName, string Config, bool AOT, string ProjectFileContents, string? ExtraBuildArgs); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandBase.cs new file mode 100644 index 00000000000000..96304f335fb322 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandBase.cs @@ -0,0 +1,200 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class Command + { + private StringBuilder _extraArgsBuilder = new(); + private string _label; + + protected string _command; + + public Process? CurrentProcess { get; private set; } + + public Dictionary Environment { get; } = new Dictionary(); + + public event DataReceivedEventHandler? ErrorDataReceived; + + public event DataReceivedEventHandler? OutputDataReceived; + + public string? WorkingDirectory { get; set; } + + public Command(string command, string label="") + { + _command = command; + _label = label; + } + + public Command WithWorkingDirectory(string dir) + { + WorkingDirectory = dir; + return this; + } + + public Command WithEnvironmentVariable(string key, string value) + { + Environment[key] = value; + return this; + } + + public Command WithEnvironmentVariables(IDictionary? extraEnvVars) + { + if (extraEnvVars != null) + { + foreach ((string key, string value) in extraEnvVars) + Environment[key] = value; + } + + return this; + } + + public virtual CommandResult Execute(string args = "") + { + return Task.Run(async () => await ExecuteAsync(args)).Result; + } + + public async virtual Task ExecuteAsync(string args = "") + { + var resolvedCommand = _command; + string fullArgs = GetFullArgs(args); + Console.WriteLine($"[{_label}] Executing - {resolvedCommand} {fullArgs} - {WorkingDirectoryInfo()}"); + return await ExecuteAsyncInternal(resolvedCommand, fullArgs); + } + + public virtual CommandResult ExecuteWithCapturedOutput(string args = "") + { + var resolvedCommand = _command; + string fullArgs = GetFullArgs(args); + Console.WriteLine($"[{_label}] Executing (Captured Output) - {resolvedCommand} {fullArgs} - {WorkingDirectoryInfo()}"); + return Task.Run(async () => await ExecuteAsyncInternal(resolvedCommand, fullArgs)).Result; + } + + protected virtual string GetFullArgs(string args) => $"{args} {_extraArgsBuilder}"; + + private async Task ExecuteAsyncInternal(string executable, string args) + { + var output = new List(); + CurrentProcess = CreateProcess(executable, args); + CurrentProcess.ErrorDataReceived += (s, e) => + { + if (e.Data == null) + return; + + output.Add($"[{_label}] {e.Data}"); + var handler = ErrorDataReceived; + if (handler != null) + { + handler(s, e); + } + }; + + CurrentProcess.OutputDataReceived += (s, e) => + { + if (e.Data == null) + return; + + output.Add($"[{_label}] {e.Data}"); + var handler = OutputDataReceived; + if (handler != null) + { + handler(s, e); + } + }; + + var completionTask = CurrentProcess.StartAndWaitForExitAsync(); + CurrentProcess.BeginOutputReadLine(); + CurrentProcess.BeginErrorReadLine(); + await completionTask; + + CurrentProcess.WaitForExit(); + RemoveNullTerminator(output); + + return new CommandResult( + CurrentProcess.StartInfo, + CurrentProcess.ExitCode, + string.Join(System.Environment.NewLine, output)); + } + + private Process CreateProcess(string executable, string args) + { + var psi = new ProcessStartInfo + { + FileName = executable, + Arguments = args, + RedirectStandardError = true, + RedirectStandardOutput = true, + RedirectStandardInput = true, + UseShellExecute = false + }; + + psi.Environment["DOTNET_MULTILEVEL_LOOKUP"] = "0"; + psi.Environment["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1"; + + AddEnvironmentVariablesTo(psi); + AddWorkingDirectoryTo(psi); + var process = new Process + { + StartInfo = psi + }; + + process.EnableRaisingEvents = true; + return process; + } + + public Command WithExtraArgs(string extraArgs) + { + _extraArgsBuilder.Append($" {extraArgs}"); + return this; + } + + private string WorkingDirectoryInfo() + { + if (WorkingDirectory == null) + { + return ""; + } + + return $" in pwd {WorkingDirectory}"; + } + + private void RemoveNullTerminator(List strings) + { + var count = strings.Count; + + if (count < 1) + { + return; + } + + if (strings[count - 1] == null) + { + strings.RemoveAt(count - 1); + } + } + + private void AddEnvironmentVariablesTo(ProcessStartInfo psi) + { + foreach (var item in Environment) + { + psi.Environment[item.Key] = item.Value; + } + } + + private void AddWorkingDirectoryTo(ProcessStartInfo psi) + { + if (!string.IsNullOrWhiteSpace(WorkingDirectory)) + { + psi.WorkingDirectory = WorkingDirectory; + } + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs new file mode 100644 index 00000000000000..b20b81fe796a3a --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/CommandResult.cs @@ -0,0 +1,42 @@ +using System; +using System.Diagnostics; +using System.Text; +using Xunit.Sdk; + +namespace Wasm.Build.Tests +{ + // taken from https://github.com/dotnet/arcade/blob/main/src/Common/Microsoft.Arcade.Common/CommandResult.cs + public struct CommandResult + { + public static readonly CommandResult Empty = new CommandResult(); + + public ProcessStartInfo StartInfo { get; } + public int ExitCode { get; } + public string Output { get; } + + public CommandResult(ProcessStartInfo startInfo, int exitCode, string output) + { + StartInfo = startInfo; + ExitCode = exitCode; + Output = output; + } + + public void EnsureSuccessful(string messagePrefix = "", bool suppressOutput = false) + { + if (ExitCode != 0) + { + StringBuilder message = new StringBuilder($"{messagePrefix} Command failed with exit code {ExitCode}: {StartInfo.FileName} {StartInfo.Arguments}"); + + if (!suppressOutput) + { + if (!string.IsNullOrEmpty(Output)) + { + message.AppendLine($"{Environment.NewLine}Standard Output:{Environment.NewLine}{Output}"); + } + } + + throw new XunitException(message.ToString()); + } + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs new file mode 100644 index 00000000000000..329bc9a9954c26 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/DotNetCommand.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace Wasm.Build.Tests +{ + public class DotNetCommand : Command + { + private BuildEnvironment _buildEnvironment; + public DotNetCommand(BuildEnvironment buildEnv) : base(buildEnv.DotNet) + { + _buildEnvironment = buildEnv; + WithEnvironmentVariables(buildEnv.EnvVars); + WithExtraArgs(buildEnv.DefaultBuildArgs); + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs new file mode 100644 index 00000000000000..30ed330bf17910 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/LocalEMSDKTests.cs @@ -0,0 +1,62 @@ +// 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; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class LocalEMSDKTests : BuildTestBase + { + public LocalEMSDKTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) : base(output, buildContext) + {} + + [ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))] + [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] + { "", "error :.*emscripten.*required for AOT" })] + [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] + { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for AOT" })] + public void AOT_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) + { + string projectName = $"missing_emsdk"; + buildArgs = buildArgs with { + ProjectName = projectName, + ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" + }; + buildArgs = ExpandBuildArgs(buildArgs); + + (_, string buildOutput) = BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + id: id, + expectSuccess: false); + + Assert.Matches(errorPattern, buildOutput); + } + + [ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))] + [BuildAndRun(host: RunHost.None, parameters: new object[] + { "", "error.*emscripten.*required for building native files" })] + [BuildAndRun(host: RunHost.None, parameters: new object[] + { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for building native files" })] + public void Relinking_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) + { + string projectName = $"simple_native_build"; + buildArgs = buildArgs with { + ProjectName = projectName, + ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" + }; + buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); + + (_, string buildOutput) = BuildProject(buildArgs, + initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), + id: id, + expectSuccess: false); + + Assert.Matches(errorPattern, buildOutput); + } + } + } diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs index 9f87757175c970..130e59e0ea2a9c 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeBuildTests.cs @@ -27,27 +27,7 @@ public NativeBuildTests(ITestOutputHelper output, SharedBuildPerTestClassFixture public void SimpleNativeBuild(BuildArgs buildArgs, RunHost host, string id) => NativeBuild("simple_native_build", s_mainReturns42, buildArgs, host, id); - [Theory] - [BuildAndRun(host: RunHost.None, parameters: new object[] - { "", "error.*emscripten.*required for building native files" })] - [BuildAndRun(host: RunHost.None, parameters: new object[] - { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for building native files" })] - public void Relinking_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) - { - string projectName = $"simple_native_build"; - buildArgs = buildArgs with { - ProjectName = projectName, - ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" - }; - buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "true"); - (_, string buildOutput) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - id: id, - expectSuccess: false); - - Assert.Matches(errorPattern, buildOutput); - } private void NativeBuild(string projectNamePrefix, string projectContents, BuildArgs buildArgs, RunHost host, string id) { diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs new file mode 100644 index 00000000000000..ca60d32d22a07d --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/ProcessExtensions.cs @@ -0,0 +1,136 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Wasm.Build.Tests +{ + internal static class ProcessExtensions + { +#if NET451 + private static readonly bool _isWindows = true; +#else + private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif + private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); + + public static void KillTree(this Process process) + { + process.KillTree(_defaultTimeout); + } + + public static void KillTree(this Process process, TimeSpan timeout) + { + string stdout; + if (_isWindows) + { + RunProcessAndWaitForExit( + "taskkill", + $"/T /F /PID {process.Id}", + timeout, + out stdout); + } + else + { + var children = new HashSet(); + GetAllChildIdsUnix(process.Id, children, timeout); + foreach (var childId in children) + { + KillProcessUnix(childId, timeout); + } + KillProcessUnix(process.Id, timeout); + } + } + + private static void GetAllChildIdsUnix(int parentId, ISet children, TimeSpan timeout) + { + string stdout; + var exitCode = RunProcessAndWaitForExit( + "pgrep", + $"-P {parentId}", + timeout, + out stdout); + + if (exitCode == 0 && !string.IsNullOrEmpty(stdout)) + { + using (var reader = new StringReader(stdout)) + { + while (true) + { + var text = reader.ReadLine(); + if (text == null) + { + return; + } + + int id; + if (int.TryParse(text, out id)) + { + children.Add(id); + // Recursively get the children + GetAllChildIdsUnix(id, children, timeout); + } + } + } + } + } + + private static void KillProcessUnix(int processId, TimeSpan timeout) + { + string stdout; + RunProcessAndWaitForExit( + "kill", + $"-TERM {processId}", + timeout, + out stdout); + } + + private static int RunProcessAndWaitForExit(string fileName, string arguments, TimeSpan timeout, out string stdout) + { + var startInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + RedirectStandardOutput = true, + UseShellExecute = false + }; + + var process = Process.Start(startInfo); + + stdout = null; + if (process.WaitForExit((int)timeout.TotalMilliseconds)) + { + stdout = process.StandardOutput.ReadToEnd(); + } + else + { + process.Kill(); + } + + return process.ExitCode; + } + + public static Task StartAndWaitForExitAsync(this Process subject) + { + var taskCompletionSource = new TaskCompletionSource(); + + subject.EnableRaisingEvents = true; + + subject.Exited += (s, a) => + { + taskCompletionSource.SetResult(null); + + subject.Dispose(); + }; + + subject.Start(); + + return taskCompletionSource.Task; + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/ProjectBuilder.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/ProjectBuilder.cs new file mode 100644 index 00000000000000..f2baf46f689f4b --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/ProjectBuilder.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Wasm.Build.Tests +{ + public class ProjectBuilder + { + List _properties = new(); + List _items = new(); + string _directoryPropsContents = ""; + string _directoryTargetsContents = ""; + string _prefixText = string.Empty; + string _suffixText = string.Empty; + private string _projectTemplate; + + private const string s_simpleProjectTemplate = + @$" + ##INSERT_AT_START## + + net6.0 + Exe + true + runtime-test.js + ##EXTRA_PROPERTIES## + + + ##EXTRA_ITEMS## + + ##INSERT_AT_END## + "; + + public ProjectBuilder(string template=s_simpleProjectTemplate) + { + _projectTemplate = template; + } + + public ProjectBuilder WithProperties(string propertyElement) + { + _properties.Add(propertyElement); + return this; + } + + public ProjectBuilder WithItems(string itemElement) + { + _items.Add(itemElement); + return this; + } + + public ProjectBuilder WithDirectoryBuildProps(string propsContents) + { + _directoryPropsContents = propsContents; + return this; + } + + public ProjectBuilder WithDirectoryBuildTargets(string propsContents) + { + _directoryTargetsContents = propsContents; + return this; + } + + public ProjectBuilder WithPrefixText(string prefix) + { + _prefixText = prefix; + return this; + } + + public ProjectBuilder WithSuffixText(string suffix) + { + _suffixText = suffix; + return this; + } + + public BuildArgs Generate(string dir, BuildArgs buildArgs) + { + Directory.CreateDirectory(dir); + + File.WriteAllText(Path.Combine(dir, "Directory.Build.props"), _directoryPropsContents); + File.WriteAllText(Path.Combine(dir, "Directory.Build.targets"), _directoryTargetsContents); + + if (buildArgs.AOT) + { + _properties.Add("$true"); + _properties.Add("false\n"); + } + + string projectContents = _projectTemplate + .Replace("##EXTRA_PROPERTIES##", string.Join(Environment.NewLine, _properties)) + .Replace("##EXTRA_ITEMS##", string.Join(Environment.NewLine, _items)) + .Replace("##INSERT_AT_START##", _prefixText) + .Replace("##INSERT_AT_END##", _suffixText); + + buildArgs = buildArgs with { ProjectFileContents = projectContents }; + File.WriteAllText(Path.Combine(dir, $"{buildArgs.ProjectName}.csproj"), buildArgs.ProjectFileContents); + + return buildArgs; + } + } +} diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs index ecbcbdd75c05c2..769d22fa660dc9 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/RebuildTests.cs @@ -5,6 +5,7 @@ using System.IO; using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; #nullable enable @@ -38,7 +39,7 @@ public void NoOpRebuild(BuildArgs buildArgs, bool nativeRelink, RunHost host, st Run(); if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product)) - Assert.True(false, $"Test bug: could not get the build product in the cache"); + throw new XunitException($"Test bug: could not get the build product in the cache"); File.Move(product!.LogFile, Path.ChangeExtension(product.LogFile!, ".first.binlog")); @@ -46,9 +47,8 @@ public void NoOpRebuild(BuildArgs buildArgs, bool nativeRelink, RunHost host, st // no-op Rebuild BuildProject(buildArgs, - () => {}, - dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, id: id, + dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, createProject: false, useCache: false); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs index e9d973650dcae6..45ce2b37ed2736 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SatelliteAssembliesTests.cs @@ -1,6 +1,7 @@ // 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.Collections.Generic; using System.IO; using System.Linq; @@ -44,11 +45,14 @@ public void ResourcesFromMainAssembly(BuildArgs buildArgs, buildArgs = buildArgs with { ProjectName = projectName }; buildArgs = ExpandBuildArgs(buildArgs, projectTemplate: s_resourcesProjectTemplate, - extraProperties: $"{(nativeRelink ? "true" : "false")}", - extraItems: $""); + extraProperties: $"{(nativeRelink ? "true" : "false")}"); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), + initProject: () => + { + Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "resx"), Path.Combine(_projectDir!, "resx")); + CreateProgramForCultureTest(_projectDir!, $"{projectName}.resx.words", "TestClass"); + }, dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, id: id); @@ -71,7 +75,7 @@ public void ResourcesFromProjectReference(BuildArgs buildArgs, RunHost host, string id) { - string projectName = $"sat_asm_proj_ref"; + string projectName = $"SatelliteAssemblyFromProjectRef"; bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT; buildArgs = buildArgs with { ProjectName = projectName }; @@ -81,9 +85,17 @@ public void ResourcesFromProjectReference(BuildArgs buildArgs, extraItems: $""); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest("LibraryWithResources.words", "LibraryWithResources.Class1"), dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack, - id: id); + id: id, + initProject: () => + { + string rootDir = _projectDir!; + _projectDir = Path.Combine(rootDir, projectName); + + Directory.CreateDirectory(_projectDir); + Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, "SatelliteAssemblyFromProjectRef"), rootDir); + CreateProgramForCultureTest(_projectDir, "LibraryWithResources.resx.words", "LibraryWithResources.Class1"); + }); string output = RunAndTestWasmApp(buildArgs, expectedExitCode: 42, @@ -105,11 +117,10 @@ public void CheckThatSatelliteAssembliesAreNotAOTed(BuildArgs buildArgs, string extraProperties: $@" -O0 -O0", - extraItems: $""); + extraItems: $""); - System.Console.WriteLine ($"--- aot: {buildArgs.AOT}"); BuildProject(buildArgs, - initProject: () => CreateProgramForCultureTest($"{projectName}.words", "TestClass"), + initProject: () => CreateProgramForCultureTest(_projectDir!, $"{projectName}.words", "TestClass"), dotnetWasmFromRuntimePack: false, id: id); @@ -124,8 +135,8 @@ public void CheckThatSatelliteAssembliesAreNotAOTed(BuildArgs buildArgs, string } #pragma warning restore xUnit1026 - private void CreateProgramForCultureTest(string resourceName, string typeName) - => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), + private void CreateProgramForCultureTest(string dir, string resourceName, string typeName) + => File.WriteAllText(Path.Combine(dir, "Program.cs"), s_cultureResourceTestProgram .Replace("##RESOURCE_NAME##", resourceName) .Replace("##TYPE_NAME##", typeName)); diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs index e84a151bd5b02d..2c43614ea41699 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/SharedBuildPerTestClassFixture.cs @@ -44,7 +44,7 @@ private void RemoveDirectory(string path) { try { - Directory.Delete(path, recursive: true); + Directory.Delete(path, recursive: true); } catch (Exception ex) { diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/Utils.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/Utils.cs new file mode 100644 index 00000000000000..048368cc430040 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Utils.cs @@ -0,0 +1,58 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +internal static class Utils +{ + public static void DirectoryCopy(string sourceDirName, string destDirName, Func? predicate=null, bool copySubDirs=true, bool silent=false) + { + // Get the subdirectories for the specified directory. + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + + if (!dir.Exists) + { + throw new DirectoryNotFoundException( + "Source directory does not exist or could not be found: " + + sourceDirName); + } + + DirectoryInfo[] dirs = dir.GetDirectories(); + + // If the destination directory doesn't exist, create it. + Directory.CreateDirectory(destDirName); + + // Get the files in the directory and copy them to the new location. + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string fullPath = file.ToString(); + if (predicate != null && !predicate(fullPath)) + { + // if (!silent) + // e(MessageImportance.Low, $"Skipping {fullPath}"); + continue; + } + + string tempPath = Path.Combine(destDirName, file.Name); + // if (!silent) + // Logger?.LogMessage(MessageImportance.Low, $"Copying {fullPath} to {tempPath}"); + file.CopyTo(tempPath, false); + } + + // If copying subdirectories, copy them and their contents to new location. + if (copySubDirs) + { + foreach (DirectoryInfo subdir in dirs) + { + string tempPath = Path.Combine(destDirName, subdir.Name); + DirectoryCopy(subdir.FullName, tempPath, predicate, copySubDirs, silent); + } + } + } + +} 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 20a1972430746f..236a2483614900 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -13,31 +13,52 @@ TEST_DEBUG_CONFIG_ALSO false + true - - - - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">WasmBuildSupportDir=%24{HELIX_CORRELATION_PAYLOAD}/build - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">set WasmBuildSupportDir=%HELIX_CORRELATION_PAYLOAD%/build & - - - - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">$(_PreCommand) TEST_LOG_PATH=%24{XHARNESS_OUT}/logs - <_PreCommand Condition="'$(OS)' != 'Windows_NT'">$(_PreCommand) HARNESS_RUNNER=%24{HARNESS_RUNNER} - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">$(_PreCommand) set TEST_LOG_PATH=%XHARNESS_OUT%\logs & - <_PreCommand Condition="'$(OS)' == 'Windows_NT'">$(_PreCommand) set HARNESS_RUNNER=%HARNESS_RUNNER% & - - $(_PreCommand) dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml - $(_PreCommand) dotnet.exe exec xunit.console.dll $(AssemblyName).dll -xml %XHARNESS_OUT%\testResults.xml - $(RunScriptCommand) -nocolor - $(RunScriptCommand) -verbose + RunScriptTemplate.sh + $(MSBuildThisFileDirectory)data\$(RunScriptInputName) - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dotnet exec xunit.console.dll $(AssemblyName).dll -xml %24XHARNESS_OUT/testResults.xml + dotnet.exe exec xunit.console.dll $(AssemblyName).dll -xml %XHARNESS_OUT%\testResults.xml + $(RunScriptCommand) -nocolor + $(RunScriptCommand) -verbose + + $(RunScriptCommand) -method $(XUnitMethodName) + $(RunScriptCommand) -class $(XUnitClassName) + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs index e426c9523d9f79..59b82318a12424 100644 --- a/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WasmBuildAppTest.cs @@ -60,28 +60,6 @@ public static int Main() } }", buildArgs, host, id); - [Theory] - [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] - { "", "error :.*emscripten.*required for AOT" })] - [BuildAndRun(aot: true, host: RunHost.None, parameters: new object[] - { "/non-existant/foo", "error.*\\(EMSDK_PATH\\)=/non-existant/foo.*required for AOT" })] - public void AOT_ErrorWhenMissingEMSDK(BuildArgs buildArgs, string emsdkPath, string errorPattern, string id) - { - string projectName = $"missing_emsdk"; - buildArgs = buildArgs with { - ProjectName = projectName, - ExtraBuildArgs = $"/p:EMSDK_PATH={emsdkPath}" - }; - buildArgs = ExpandBuildArgs(buildArgs); - - (_, string buildOutput) = BuildProject(buildArgs, - initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42), - id: id, - expectSuccess: false); - - Assert.Matches(errorPattern, buildOutput); - } - private static string s_bug49588_ProgramCS = @" using System; public class TestClass { diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs new file mode 100644 index 00000000000000..e50d88db04a8a5 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/WorkloadTests.cs @@ -0,0 +1,118 @@ +// 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 System.Runtime.InteropServices; +using System.Xml; +using System.Xml.Serialization; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +#nullable enable + +namespace Wasm.Build.Tests +{ + public class WorkloadTests : BuildTestBase + { + public WorkloadTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + [ConditionalFact(typeof(BuildTestBase), nameof(IsUsingWorkloads))] + public void FilesInUnixFilesPermissionsXmlExist() + { + // not doing any project generation here + _enablePerTestCleanup = false; + + // find all the UnixFilePermissions .. + string packsDir = Path.Combine(Path.GetDirectoryName(s_buildEnv.DotNet)!, "packs"); + Assert.True(Directory.Exists(packsDir), $"Could not find packs directory {packsDir}"); + + var unixPermFiles = Directory.EnumerateFiles(packsDir, "UnixFilePermissions.xml", new EnumerationOptions { RecurseSubdirectories = true }); + foreach (string unixPermFile in unixPermFiles) + { + Assert.True(File.Exists(unixPermFile), $"Could not find {unixPermFile}"); + FileList? list = FileList.Deserialize(unixPermFile); + if (list == null) + throw new Exception($"Could not read unix permissions file {unixPermFile}"); + + // File is in ///data/UnixFilePermissions.xml + // and + string thisPackDir = Path.Combine(Path.GetDirectoryName(unixPermFile)!, ".."); + foreach (FileListFile flf in list.File) + { + if (flf.Path == null) + throw new Exception($"Path for FileListFile should not be null. xml: {unixPermFile}"); + + var targetFile = Path.Combine(thisPackDir, flf.Path); + Assert.True(File.Exists(targetFile), $"Expected file {targetFile} to exist in the pack, as it is referenced in {unixPermFile}"); + } + } + + // We don't install the cross compiler pack from nupkg, so we don't + // have the unixFilePermissions for that + // Expect just the emscripten ones here for now + + // linux doesn't have Emscripten.Python package, so only 2 there + int expectedPermFileCount = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 2 : 3; + + int permFileCount = unixPermFiles.Count(); + if (permFileCount != expectedPermFileCount) + throw new XunitException($"Expected to find 3 UnixFilePermissions.xml files, from emscripten packages. But got {permFileCount}." + + $"{Environment.NewLine}Files: {string.Join(", ", unixPermFiles)}"); + } + } + + [Serializable] + [XmlType(AnonymousType = true)] + [XmlRoot(Namespace = "", IsNullable = false)] + public class FileList + { + private FileListFile[]? fileField; + + [XmlElement("File")] + public FileListFile[] File + { + get => fileField ?? Array.Empty(); + set => fileField = value; + } + + public static FileList? Deserialize(string pathToXml) + { + var serializer = new XmlSerializer(typeof(FileList)); + + using var fs = new FileStream(pathToXml, FileMode.Open, FileAccess.Read); + var reader = XmlReader.Create(fs); + FileList? fileList = (FileList?)serializer.Deserialize(reader); + return fileList; + } + } + + // From https://github.com/dotnet/sdk/blob/main/src/Cli/dotnet/NugetPackageDownloader/WorkloadUnixFilePermissionsFileList.cs + [Serializable] + [XmlType(AnonymousType = true)] + public class FileListFile + { + private string? pathField; + + private string? permissionField; + + [XmlAttribute] + public string? Path + { + get => pathField; + set => pathField = value; + } + + [XmlAttribute] + public string? Permission + { + get => permissionField; + set => permissionField = value; + } + } +} diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.props similarity index 100% rename from src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.props rename to src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.props 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 new file mode 100644 index 00000000000000..1ae8c2cd6ff9fa --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Blazor.Directory.Build.targets @@ -0,0 +1,77 @@ + + + <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\ + Microsoft.NETCore.App + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_targetingPackReferenceExclusion Include="$(TargetName)" /> + <_targetingPackReferenceExclusion Include="@(_ResolvedProjectReferencePaths->'%(Filename)')" /> + <_targetingPackReferenceExclusion Include="@(DefaultReferenceExclusion)" /> + + + + <_targetingPackReferenceWithExclusion Include="@(Reference)"> + %(_targetingPackReferenceExclusion.Identity) + + + + + 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 new file mode 100644 index 00000000000000..1a9c112e747d9b --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.props @@ -0,0 +1,14 @@ + + + <_WasmTargetsDir Condition="'$(RuntimeSrcDir)' != ''">$(RuntimeSrcDir)\src\mono\wasm\build\ + <_WasmTargetsDir Condition="'$(WasmBuildSupportDir)' != ''">$(WasmBuildSupportDir)\wasm\ + $(WasmBuildSupportDir)\emsdk\ + true + + + + + + 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 new file mode 100644 index 00000000000000..8c0b1e7e46ee71 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Local.Directory.Build.targets @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.sh b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.sh new file mode 100644 index 00000000000000..d210c70ad068b1 --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/RunScriptTemplate.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +EXECUTION_DIR=$(dirname $0) + +cd $EXECUTION_DIR + +if [ -z "$HELIX_WORKITEM_UPLOAD_ROOT" ]; then + XHARNESS_OUT="$EXECUTION_DIR/xharness-output" +else + XHARNESS_OUT="$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output" +fi + +if [ ! -z "$XHARNESS_CLI_PATH" ]; then + # When running in CI, we only have the .NET runtime available + # We need to call the XHarness CLI DLL directly via dotnet exec + HARNESS_RUNNER="dotnet exec $XHARNESS_CLI_PATH" +else + HARNESS_RUNNER="dotnet xharness" +fi + +function set_env_vars() +{ + if [ "x$TEST_USING_WORKLOADS" = "xtrue" ]; then + export PATH=$BASE_DIR/dotnet-workload:$PATH + export SDK_FOR_WORKLOAD_TESTING_PATH=$BASE_DIR/dotnet-workload + export AppRefDir=$BASE_DIR/microsoft.netcore.app.ref + elif [ ! -z "$HELIX_WORKITEM_UPLOAD_ROOT" ]; then + export WasmBuildSupportDir=$BASE_DIR/build + fi +} + +export TEST_LOG_PATH=${XHARNESS_OUT}/logs + +[[RunCommands]] + +_exitCode=$? + +echo "XHarness artifacts: $XHARNESS_OUT" + +exit $_exitCode diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.props b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.props new file mode 100644 index 00000000000000..6d8504088b955a --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.props @@ -0,0 +1,7 @@ + + + browser-wasm + true + true + + 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 new file mode 100644 index 00000000000000..76e0b3ea82eaff --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/Workloads.Directory.Build.targets @@ -0,0 +1,95 @@ + + + PrepareForWasmBuild;$(WasmBuildAppDependsOn) + <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\ + Microsoft.NETCore.App + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_targetingPackReferenceExclusion Include="$(TargetName)" /> + <_targetingPackReferenceExclusion Include="@(_ResolvedProjectReferencePaths->'%(Filename)')" /> + <_targetingPackReferenceExclusion Include="@(DefaultReferenceExclusion)" /> + + + + <_targetingPackReferenceWithExclusion Include="@(Reference)"> + %(_targetingPackReferenceExclusion.Identity) + + + + + diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/data/nuget6.config b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/nuget6.config new file mode 100644 index 00000000000000..434eac8408326f --- /dev/null +++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/data/nuget6.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets b/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets deleted file mode 100644 index 058246e4086204..00000000000000 --- a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Directory.Build.targets +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj b/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj deleted file mode 100644 index 91961dd7d6b422..00000000000000 --- a/src/tests/BuildWasmApps/testassets/LibraryWithResources/LibraryWithResources.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - net5.0 - - - - - - diff --git a/src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/Class1.cs similarity index 100% rename from src/tests/BuildWasmApps/testassets/LibraryWithResources/Class1.cs rename to src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/Class1.cs diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/LibraryWithResources.csproj b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/LibraryWithResources.csproj new file mode 100644 index 00000000000000..5198d9c2109d4e --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/LibraryWithResources.csproj @@ -0,0 +1,5 @@ + + + net6.0 + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.es-ES.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.es-ES.resx new file mode 100644 index 00000000000000..775397b15a2b9f --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.es-ES.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ciao + + + + hola + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.ja-JP.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.ja-JP.resx new file mode 100644 index 00000000000000..c843811244c256 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.ja-JP.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + さようなら + + + + こんにちは + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.resx new file mode 100644 index 00000000000000..c3d5a787420866 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources/resx/words.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bye + + + + hello + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.es-ES.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.es-ES.resx new file mode 100644 index 00000000000000..775397b15a2b9f --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.es-ES.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ciao + + + + hola + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.ja-JP.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.ja-JP.resx new file mode 100644 index 00000000000000..c843811244c256 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.ja-JP.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + さようなら + + + + こんにちは + + diff --git a/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.resx b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.resx new file mode 100644 index 00000000000000..c3d5a787420866 --- /dev/null +++ b/src/tests/BuildWasmApps/testassets/SatelliteAssemblyInMain/resx/words.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + bye + + + + hello + + diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/Program.cs b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/Program.cs deleted file mode 100644 index 743f481896fcb5..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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.Runtime.CompilerServices; - -namespace Sample -{ - public class Test - { - public static void Main(string[] args) - { - Console.WriteLine ("Hello, World!"); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int TestMeaning() - { - return 42; - } - } -} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/WebAssembly.Browser.AOT.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/WebAssembly.Browser.AOT.Test.csproj deleted file mode 100644 index 06ffa371ace9a9..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/WebAssembly.Browser.AOT.Test.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - true - WasmTestOnBrowser - 42 - true - runtime.js - - - - - - Always - - - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html deleted file mode 100644 index 642987d23c5e2b..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - TESTS - - - - - - Result from Sample.Test.TestMeaning: - - - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js deleted file mode 100644 index 11f8d64f60c514..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/AOT/runtime.js +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -var Module = { - - config: null, - - preInit: async function() { - await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly - }, - - onRuntimeInitialized: function () { - if (!Module.config || Module.config.error) { - console.log("No config found"); - test_exit(1); - throw(Module.config.error); - } - - Module.config.loaded_cb = function () { - try { - App.init (); - } catch (error) { - test_exit(1); - throw (error); - } - }; - Module.config.fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); - } - - try - { - MONO.mono_load_runtime_and_bcl_args (Module.config); - } catch (error) { - test_exit(1); - throw(error); - } - }, -}; diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/Program.cs b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/Program.cs deleted file mode 100644 index 743f481896fcb5..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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.Runtime.CompilerServices; - -namespace Sample -{ - public class Test - { - public static void Main(string[] args) - { - Console.WriteLine ("Hello, World!"); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - public static int TestMeaning() - { - return 42; - } - } -} diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/WebAssembly.Browser.NormalInterp.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/WebAssembly.Browser.NormalInterp.Test.csproj deleted file mode 100644 index db384e014d64af..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/WebAssembly.Browser.NormalInterp.Test.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - true - false - true - WasmTestOnBrowser - 42 - runtime.js - - - - - Always - - - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html deleted file mode 100644 index 9de05f5031b325..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - TESTS - - - - - - Result from Sample.Test.TestMeaning: - - - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js b/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js deleted file mode 100644 index b5227472674c6b..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Browser/NormalInterp/runtime.js +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -var Module = { - - config: null, - - preInit: async function() { - await MONO.mono_wasm_load_config("./mono-config.json"); // sets Module.config implicitly - }, - - onRuntimeInitialized: function () { - if (!Module.config || Module.config.error) { - console.log("No config found"); - test_exit(1); - throw(Module.config.error); - } - - Module.config.loaded_cb = function () { - try { - App.init (); - } catch (error) { - test_exit(1); - throw (error); - } - }; - Module.config.fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); - } - - try - { - MONO.mono_load_runtime_and_bcl_args (Module.config); - } catch (error) { - test_exit(1); - throw(error); - } - }, -}; diff --git a/src/tests/FunctionalTests/WebAssembly/Console/AOT/Program.cs b/src/tests/FunctionalTests/WebAssembly/Console/AOT/Program.cs deleted file mode 100644 index 20df95f3a578b5..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Console/AOT/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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.Threading.Tasks; - -public class Test -{ - public static async Task Main(string[] args) - { - await Task.Delay(1); - Console.WriteLine("Hello Wasm AOT Functional Test!"); - return 42; - } -} \ No newline at end of file diff --git a/src/tests/FunctionalTests/WebAssembly/Console/AOT/WebAssembly.Console.AOT.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Console/AOT/WebAssembly.Console.AOT.Test.csproj deleted file mode 100644 index 60f8759a10ed16..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Console/AOT/WebAssembly.Console.AOT.Test.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - true - true - 42 - true - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/Program.cs b/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/Program.cs deleted file mode 100644 index 5d2a7280f1493f..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -// 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.Threading.Tasks; - -public class Test -{ - public static async Task Main(string[] args) - { - await Task.Delay(1); - Console.WriteLine("Hello From Wasm!"); - return 42; - } -} \ No newline at end of file diff --git a/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/WebAssembly.Console.NormalInterp.Test.csproj b/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/WebAssembly.Console.NormalInterp.Test.csproj deleted file mode 100644 index 407030c25ac41c..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/Console/NormalInterp/WebAssembly.Console.NormalInterp.Test.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - false - true - 42 - - - - - diff --git a/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/Program.cs b/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/Program.cs deleted file mode 100644 index 787fe939765d16..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/Program.cs +++ /dev/null @@ -1,9 +0,0 @@ -// 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.Threading.Tasks; - -await Task.Delay(1); -Console.WriteLine("Hello From Wasm!"); -return 42; diff --git a/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/WebAssembly.TopLevel.NormalInterp.Test.csproj b/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/WebAssembly.TopLevel.NormalInterp.Test.csproj deleted file mode 100644 index 407030c25ac41c..00000000000000 --- a/src/tests/FunctionalTests/WebAssembly/TopLevel/NormalInterp/WebAssembly.TopLevel.NormalInterp.Test.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - false - true - 42 - - - - -