diff --git a/Directory.Build.props b/Directory.Build.props index 76b4d1aa9dd77..e0a179b742317 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,6 +28,8 @@ <_hostArch>$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant) $(_hostArch) + wasm + wasm arm armv6 armel @@ -35,8 +37,6 @@ loongarch64 s390x ppc64le - wasm - wasm x64 x64 $(TargetArchitecture) diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 9f29ab2f4849d..5df409f12a259 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -221,6 +221,18 @@ IsNative="true" /> + + + + + + diff --git a/eng/native/gen-buildsys.cmd b/eng/native/gen-buildsys.cmd index 8e635f3537ac9..78bf2b4e9a027 100644 --- a/eng/native/gen-buildsys.cmd +++ b/eng/native/gen-buildsys.cmd @@ -66,7 +66,7 @@ if /i "%__Arch%" == "wasm" ( exit /B 1 ) - set "WASI_SDK_PATH=%__repoRoot%src\mono\wasi\wasi-sdk" + set "WASI_SDK_PATH=%__repoRoot%\src\mono\wasi\wasi-sdk" ) :: replace backslash with forward slash and append last slash set "WASI_SDK_PATH=!WASI_SDK_PATH:\=/!" diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml index c41d31f65aa30..49a64403a2f38 100644 --- a/eng/pipelines/common/global-build-job.yml +++ b/eng/pipelines/common/global-build-job.yml @@ -171,14 +171,13 @@ jobs: df -h displayName: Disk Usage before Build - - ${{ if eq(parameters.platform, 'browser_wasm_win') }}: + - ${{ if in(parameters.platform, 'browser_wasm_win', 'wasi_wasm_win') }}: # Update machine certs - task: PowerShell@2 displayName: Update machine certs inputs: filePath: $(Build.SourcesDirectory)/eng/pipelines/mono/update-machine-certs.ps1 - # Build - ${{ if eq(parameters.isSourceBuild, false) }}: - ${{ if eq(parameters.isManualCodeQLBuild, true) }}: diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index d240154ed814a..ff5333e089cbf 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -379,6 +379,26 @@ jobs: buildConfig: ${{ parameters.buildConfig }} ${{ insert }}: ${{ parameters.jobParameters }} +# WASI WebAssembly windows + +- ${{ if containsValue(parameters.platforms, 'wasi_wasm_win') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: wasi + archType: wasm + targetRid: wasi-wasm + platform: wasi_wasm_win + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + jobParameters: + hostedOs: windows + runtimeFlavor: ${{ parameters.runtimeFlavor }} + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ insert }}: ${{ parameters.jobParameters }} + # Browser WebAssembly - ${{ if containsValue(parameters.platforms, 'browser_wasm') }}: diff --git a/eng/pipelines/common/templates/wasm-library-tests.yml b/eng/pipelines/common/templates/wasm-library-tests.yml index dfc447572863f..380a217e1b7e5 100644 --- a/eng/pipelines/common/templates/wasm-library-tests.yml +++ b/eng/pipelines/common/templates/wasm-library-tests.yml @@ -1,6 +1,5 @@ parameters: alwaysRun: false - buildAndRunWasi: false extraBuildArgs: '' extraHelixArgs: '' isExtraPlatformsBuild: false diff --git a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml index 1fff116909b81..29ae803f2bb80 100644 --- a/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml +++ b/eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml @@ -9,6 +9,7 @@ parameters: isRollingBuild: false excludeLibTests: false excludeNonLibTests: false + excludeOptional: true debuggerTestsOnly: false jobs: @@ -230,3 +231,13 @@ jobs: #parameters: #runProfile: 'v8' #onlySanityCheck: true + +- ${{ if and(ne(parameters.isRollingBuild, true), ne(parameters.excludeOptional, true)) }}: + - template: /eng/pipelines/common/templates/wasm-library-tests.yml + parameters: + platforms: + - wasi_wasm + extraBuildArgs: /p:EnableAggressiveTrimming=true + alwaysRun: ${{ parameters.isWasmOnlyBuild }} + scenarios: + - normal diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 734530b9b6889..505d78f0a8cd6 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -196,7 +196,7 @@ jobs: - (Ubuntu.1804.Amd64)Ubuntu.1804.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-webassembly # Browser WebAssembly windows - - ${{ if eq(parameters.platform, 'browser_wasm_win') }}: + - ${{ if in(parameters.platform, 'browser_wasm_win', 'wasi_wasm_win') }}: - (Windows.Amd64.Server2022.Open)windows.amd64.server2022.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-ltsc2022-helix-webassembly ${{ insert }}: ${{ parameters.jobParameters }} diff --git a/eng/pipelines/runtime-wasm-optional.yml b/eng/pipelines/runtime-wasm-optional.yml new file mode 100644 index 0000000000000..86840d3e35db9 --- /dev/null +++ b/eng/pipelines/runtime-wasm-optional.yml @@ -0,0 +1,29 @@ +# This is a wrapper yml for `extra-platforms/runtime-extra-platforms-wasm.yml`, which +# has all the wasm jobs. This file is essentially so we can have point +# the pipeline in azdo UI to this, and thus avoid any scheduled triggers + +trigger: none + +variables: + - template: /eng/pipelines/common/variables.yml + +extends: + template: /eng/pipelines/common/templates/pipeline-with-resources.yml + parameters: + stages: + - stage: Build + jobs: + + # + # Evaluate paths + # + - template: /eng/pipelines/common/evaluate-default-paths.yml + + - template: /eng/pipelines/extra-platforms/runtime-extra-platforms-wasm.yml + parameters: + isExtraPlatformsBuild: ${{ variables.isExtraPlatformsBuild }} + isWasmOnlyBuild: ${{ variables.isWasmOnlyBuild }} + isRollingBuild: ${{ variables.isRollingBuild }} + excludeLibTests: true + excludeNonLibTests: true + excludeOptional: false diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 7e2690fc82fd8..d37e3641de44a 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -373,7 +373,6 @@ extends: parameters: platforms: - browser_wasm - buildAndRunWasi: true alwaysRun: ${{ variables.isRollingBuild }} scenarios: - normal @@ -456,12 +455,19 @@ extends: extraBuildArgs: /p:MonoWasmBuildVariant=perftrace alwaysRun: ${{ variables.isRollingBuild }} - # BUILD ONLY - WASI/Wasm - - template: /eng/pipelines/common/templates/wasi-build-only.yml + # WASI/WASM + + - template: /eng/pipelines/common/templates/wasm-library-tests.yml parameters: platforms: - wasi_wasm + - wasi_wasm_win + nameSuffix: '_Smoke' + extraBuildArgs: /p:EnableAggressiveTrimming=true /p:RunWasmSamples=true + shouldRunSmokeOnly: true alwaysRun: ${{ variables.isRollingBuild }} + scenarios: + - normal # # iOS/tvOS devices - Full AOT + AggressiveTrimming to reduce size diff --git a/eng/testing/WasiRunnerTemplate.cmd b/eng/testing/WasiRunnerTemplate.cmd new file mode 100644 index 0000000000000..9020bad62bad9 --- /dev/null +++ b/eng/testing/WasiRunnerTemplate.cmd @@ -0,0 +1,69 @@ +@echo off +setlocal enabledelayedexpansion + +:: SetCommands defined in eng\testing\tests.wasi.targets +[[SetCommands]] +[[SetCommandsEcho]] + +set EXECUTION_DIR=%~dp0 +if [%3] NEQ [] ( + set SCENARIO=%3 +) + +if [%HELIX_WORKITEM_UPLOAD_ROOT%] == [] ( + set "XHARNESS_OUT=%EXECUTION_DIR%xharness-output" +) else ( + set "XHARNESS_OUT=%HELIX_WORKITEM_UPLOAD_ROOT%\xharness-output" +) + +if [%XHARNESS_CLI_PATH%] NEQ [] ( + :: When running in CI, we only have the .NET runtime available + :: We need to call the XHarness CLI DLL directly via dotnet exec + set HARNESS_RUNNER=dotnet.exe exec "%XHARNESS_CLI_PATH%" +) else ( + set HARNESS_RUNNER=dotnet.exe xharness +) + +if [%XHARNESS_COMMAND%] == [] ( + set XHARNESS_COMMAND=test +) + +if [%XHARNESS_ARGS%] == [] ( + set "XHARNESS_ARGS=%ENGINE_ARGS%" +) + +if [%PREPEND_PATH%] NEQ [] ( + set "PATH=%PREPEND_PATH%:%PATH%" +) + +if [%XUNIT_RANDOM_ORDER_SEED%] NEQ [] ( + set "WasmXHarnessMonoArgs=%WasmXHarnessMonoArgs% --setenv=XUNIT_RANDOM_ORDER_SEED=%XUNIT_RANDOM_ORDER_SEED%" +) + +echo EXECUTION_DIR=%EXECUTION_DIR% +echo SCENARIO=%SCENARIO% +echo XHARNESS_OUT=%XHARNESS_OUT% +echo XHARNESS_CLI_PATH=%XHARNESS_CLI_PATH% +echo HARNESS_RUNNER=%HARNESS_RUNNER% +echo XHARNESS_COMMAND=%XHARNESS_COMMAND% +echo XHARNESS_ARGS=%XHARNESS_ARGS% + +:: ========================= BEGIN Test Execution ============================= +echo ----- start %DATE% %TIME% =============== To repro directly: ===================================================== +echo pushd %EXECUTION_DIR% +:: RunCommands defined in eng\testing\tests.wasi.targets +[[RunCommandsEcho]] +echo popd +echo =========================================================================================================== +pushd %EXECUTION_DIR% +@echo on +:: RunCommands defined in eng\testing\tests.wasi.targets +[[RunCommands]] +set EXIT_CODE=%ERRORLEVEL% +@echo off +popd +echo ----- end %DATE% %TIME% ----- exit code %EXIT_CODE% ---------------------------------------------------------- + +echo XHarness artifacts: %XHARNESS_OUT% + +exit /b %EXIT_CODE% diff --git a/eng/testing/WasiRunnerTemplate.sh b/eng/testing/WasiRunnerTemplate.sh new file mode 100644 index 0000000000000..b2b9240221031 --- /dev/null +++ b/eng/testing/WasiRunnerTemplate.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +# SetCommands defined in eng\testing\tests.wasi.targets +[[SetCommands]] +[[SetCommandsEcho]] + +EXECUTION_DIR=$(dirname $0) +if [[ -n "$3" ]]; then + SCENARIO=$3 +fi + +if [[ -z "$HELIX_WORKITEM_UPLOAD_ROOT" ]]; then + XHARNESS_OUT="$EXECUTION_DIR/xharness-output" +else + XHARNESS_OUT="$HELIX_WORKITEM_UPLOAD_ROOT/xharness-output" +fi + +if [[ -n "$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 + +if [[ -z "$XHARNESS_COMMAND" ]]; then + XHARNESS_COMMAND="test" +fi + +if [[ -z "$XHARNESS_ARGS" ]]; then + XHARNESS_ARGS="$ENGINE_ARGS" +fi + +if [[ -n "$PREPEND_PATH" ]]; then + export PATH=$PREPEND_PATH:$PATH +fi + +if [[ -n "$XUNIT_RANDOM_ORDER_SEED" ]]; then + WasmXHarnessMonoArgs="${WasmXHarnessMonoArgs} --setenv=XUNIT_RANDOM_ORDER_SEED=${XUNIT_RANDOM_ORDER_SEED}" +fi + +echo EXECUTION_DIR=$EXECUTION_DIR +echo SCENARIO=$SCENARIO +echo XHARNESS_OUT=$XHARNESS_OUT +echo XHARNESS_CLI_PATH=$XHARNESS_CLI_PATH +echo HARNESS_RUNNER=$HARNESS_RUNNER +echo XHARNESS_COMMAND=$XHARNESS_COMMAND +echo XHARNESS_ARGS=$XHARNESS_ARGS + +pushd $EXECUTION_DIR + +# ========================= BEGIN Test Execution ============================= +echo ----- start $(date) =============== To repro directly: ===================================================== +echo pushd $EXECUTION_DIR +# RunCommands defined in eng\testing\tests.wasi.targets +[[RunCommandsEcho]] +echo popd +echo =========================================================================================================== +pushd $EXECUTION_DIR +# RunCommands defined in eng\testing\tests.wasi.targets +[[RunCommands]] +_exitCode=$? +popd +echo ----- end $(date) ----- exit code $_exitCode ---------------------------------------------------------- + +echo "XHarness artifacts: $XHARNESS_OUT" + +exit $_exitCode diff --git a/eng/testing/tests.browser.targets b/eng/testing/tests.browser.targets new file mode 100644 index 0000000000000..80cd51f997008 --- /dev/null +++ b/eng/testing/tests.browser.targets @@ -0,0 +1,344 @@ + + + + + true + true + false + $(BundleTestAppTargets);BundleTestWasmApp + true + + + $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm', 'emsdk')) + + + true + + + false + true + + + <_ShellCommandSeparator Condition="'$(OS)' == 'Windows_NT'">&& + <_ShellCommandSeparator Condition="'$(OS)' != 'Windows_NT'">&& + <_WasmMainJSFileName Condition="'$(WasmMainJSPath)' != ''">$([System.IO.Path]::GetFileName('$(WasmMainJSPath)')) + <_WasmStrictVersionMatch Condition="'$(ContinuousIntegrationBuild)' == 'true'">true + true + <_UseWasmSymbolicator Condition="'$(TestTrimming)' != 'true'">true + true + false + true + <_XHarnessTestsTimeout>00:30:00 + + + + + + + + true + + $(NoWarn);IL2118 + + $(NoWarn);IL2121 + + + + helix + helix + local + + <_WasmBrowserPathForTests Condition="'$(BROWSER_PATH_FOR_TESTS)' != ''">$(BROWSER_PATH_FOR_TESTS) + <_WasmBrowserPathForTests Condition="'$(_WasmBrowserPathForTests)' == '' and '$(InstallChromeForTests)' == 'true'">$(ChromeBinaryPath) + + + + <_AOT_InternalForceInterpretAssemblies Include="@(HighAotMemoryUsageAssembly)" /> + + + + + + + + + + + + + + + + + + + + + + <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(Scenario)' != 'BuildWasmApps' and '$(WasmMainAssemblyFileName)' == ''">--run WasmTestRunner.dll $(AssemblyName).dll + <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(WasmMainAssemblyFileName)' != ''">--run $(WasmMainAssemblyFileName) + <_AppArgs Condition="'$(IsFunctionalTest)' == 'true'">--run $(AssemblyName).dll + + <_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) $(WasmTestAppArgs) + + $(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=1 + + + + <_XHarnessArgs Condition="'$(OS)' != 'Windows_NT'">wasm $XHARNESS_COMMAND --app=. --output-directory=$XHARNESS_OUT + <_XHarnessArgs Condition="'$(OS)' == 'Windows_NT'">wasm %XHARNESS_COMMAND% --app=. --output-directory=%XHARNESS_OUT% + + <_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode) + <_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs) + <_XHarnessArgs Condition="('$(WasmEnableThreads)' == 'true' or '$(WasmEnablePerfTracing)' == 'true') and '$(_XHarnessArs.Contains("--web-server-use-cop")' != 'true'">$(_XHarnessArgs) --web-server-use-cop + <_XHarnessArgs >$(_XHarnessArgs) -s dotnet.js.symbols + <_XHarnessArgs Condition="'$(_UseWasmSymbolicator)' == 'true'" >$(_XHarnessArgs) --symbol-patterns wasm-symbol-patterns.txt + <_XHarnessArgs Condition="'$(_UseWasmSymbolicator)' == 'true'" >$(_XHarnessArgs) --symbolicator WasmSymbolicator.dll,Microsoft.WebAssembly.Internal.SymbolicatorWrapperForXHarness + <_XHarnessArgs Condition="'$(_WasmBrowserPathForTests)' != ''" >$(_XHarnessArgs) "--browser-path=$(_WasmBrowserPathForTests)" + <_XHarnessArgs Condition="'$(_XHarnessTestsTimeout)' != '' " >$(_XHarnessArgs) "--timeout=$(_XHarnessTestsTimeout)" + <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) + + + $HARNESS_RUNNER $(_XHarnessArgs) %24XHARNESS_ARGS %24WasmXHarnessArgs -- $(WasmXHarnessMonoArgs) %24WasmXHarnessMonoArgs $(_AppArgs) %24WasmTestAppArgs + %HARNESS_RUNNER% $(_XHarnessArgs) %XHARNESS_ARGS% %WasmXHarnessArgs% -- $(WasmXHarnessMonoArgs) %WasmXHarnessMonoArgs% $(_AppArgs) %WasmTestAppArgs% + + + + <_AOTBuildCommand Condition="'$(BrowserHost)' != 'windows'">_buildAOTFunc publish/ProxyProjectForAOTOnHelix.proj $XHARNESS_OUT/AOTBuild.binlog + <_AOTBuildCommand Condition="'$(BrowserHost)' == 'windows'">dotnet msbuild publish/ProxyProjectForAOTOnHelix.proj /bl:%XHARNESS_OUT%/AOTBuild.binlog + + <_AOTBuildCommand Condition="'$(BrowserHost)' == 'windows'">$(_AOTBuildCommand) "/p:WasmCachePath=%USERPROFILE%\.emscripten-cache" + + + <_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration) + + <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation) + <_AOTBuildCommand>$(_AOTBuildCommand) $(_ShellCommandSeparator) cd wasm_build/AppBundle + + $(_AOTBuildCommand) + $(_AOTBuildCommand) $(_ShellCommandSeparator) $(RunScriptCommand) + + + + + + + + true + + + + PrepareForWasmBuildApp;$(WasmNestedPublishAppDependsOn) + + + + + WasmTriggerPublishApp + $(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix + + + <_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(BundleDir), 'WasmTestRunner.runtimeconfig.json')) + exec "$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))" --runtime-config "$(_RuntimeConfigJsonPath)" $(WasmHostArguments) $(StartArguments) $(WasmXHarnessMonoArgs) $(_AppArgs) + + + + + + + + true + + + + PrepareForWasmBuildApp;$(WasmNestedPublishAppDependsOn) + + + + + + + + + + + + + + true + -O2 + + + + <_MainAssemblyPath Condition="'%(WasmAssembliesToBundle.FileName)' == $(AssemblyName) and '%(WasmAssembliesToBundle.Extension)' == '.dll'">%(WasmAssembliesToBundle.Identity) + $([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json')) + -Oz -Wl,-O0 -Wl,-lto-O0 + + + + + + + + + + + + + + + <_BundlePdbFiles Include="$([System.IO.Path]::ChangeExtension('%(WasmAssembliesToBundle.Identity)', '.pdb'))" /> + + + + + + <_WasmPropertyNames Include="AOTMode" /> + <_WasmPropertyNames Include="AssemblyName" /> + <_WasmPropertyNames Include="DisableParallelAot" /> + <_WasmPropertyNames Include="DisableParallelEmccCompile" /> + <_WasmPropertyNames Include="EmccCompileOptimizationFlag" /> + <_WasmPropertyNames Include="EmccLinkOptimizationFlag" /> + <_WasmPropertyNames Include="IncludeSatelliteAssembliesInVFS" /> + <_WasmPropertyNames Include="InvariantGlobalization" /> + <_WasmPropertyNames Include="WasmBuildNative" /> + <_WasmPropertyNames Include="WasmDebugLevel" /> + <_WasmPropertyNames Include="WasmDedup" /> + <_WasmPropertyNames Include="WasmLinkIcalls" /> + <_WasmPropertyNames Include="WasmNativeStrip" /> + <_WasmPropertyNames Include="_WasmDevel" /> + <_WasmPropertyNames Include="_WasmStrictVersionMatch" /> + <_WasmPropertyNames Include="WasmEmitSymbolMap" /> + + <_WasmPropertiesToPass + Include="$(%(_WasmPropertyNames.Identity))" + Name="%(_WasmPropertyNames.Identity)" + ConditionToUse__="%(_WasmPropertyNames.ConditionToUse__)" /> + + <_WasmVFSFilesToCopy Include="@(WasmFilesToIncludeInFileSystem)" /> + <_WasmVFSFilesToCopy TargetPath="%(FileName)%(Extension)" Condition="'%(_WasmVFSFilesToCopy.TargetPath)' == ''" /> + + <_WasmExtraFilesToCopy Include="@(WasmExtraFilesToDeploy)" /> + <_WasmExtraFilesToCopy TargetPath="%(FileName)%(Extension)" Condition="'%(_WasmExtraFilesToCopy.TargetPath)' == ''" /> + + + + <_WasmItemsToPass Include="@(_AOT_InternalForceInterpretAssemblies)" OriginalItemName__="_AOT_InternalForceInterpretAssemblies" /> + + + + + + + + + + + + + + $(BundleDir) + WasmTestRunner.dll + $(MonoProjectRoot)\wasm\test-main.js + $(InvariantGlobalization) + true + + true + -1 + + + + <_SatelliteAssemblies Include="$(PublishDir)*\*.resources.dll" /> + <_SatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> + <_SatelliteAssemblies TargetPath="%(CultureName)\%(FileName)%(Extension)" /> + + + + + + + + + <_CopyLocalPaths + Include="@(PublishItemsOutputGroupOutputs)" + Condition="'%(PublishItemsOutputGroupOutputs.BuildReference)' == 'true' and + !$([System.String]::new('%(PublishItemsOutputGroupOutputs.Identity)').EndsWith('.resources.dll'))" /> + + <_CopyLocalPaths TargetPath="%(_CopyLocalPaths.RelativePath)" Condition="'%(_CopyLocalPaths.RelativePath)' != ''" /> + <_CopyLocalPaths TargetPath="%(FileName)%(Extension)" Condition="'%(_CopyLocalPaths.RelativePath)' == ''" /> + + + + + + + + + + + <_PdbFilesToCheck Include="$([System.IO.Path]::ChangeExtension('%(ResolvedFileToPublish.Identity)', '.pdb'))" + Condition="'%(ResolvedFileToPublish.Extension)' == '.dll'" /> + + + + + + + + + + <_NodeNpmModuleString Include="%(NodeNpmModule.Identity):%(NodeNpmModule.Alias)" /> + <_NodeNpmModuleStringTrimmed Include="@(_NodeNpmModuleString->Trim(':'))" /> + + + @(_NodeNpmModuleStringTrimmed, ',') + + + + + + + + + + + + + + + + + + + + diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 01cd9b32fc898..a5c04745736d9 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -78,8 +78,10 @@ Condition="'$(TargetOS)' == 'android'" /> - + <_MobileIntermediateOutputPath>$(IntermediateOutputPath)mobile @@ -133,7 +135,7 @@ <_runnerFilesToPublish Include="$(AndroidTestRunnerDir)*" Condition="'$(TargetOS)' == 'android'" /> <_runnerFilesToPublish Include="$(AppleTestRunnerDir)*" Condition="'$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'ios' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'tvossimulator'" /> - <_runnerFilesToPublish Include="$(WasmTestRunnerDir)*" Condition="'$(TargetOS)' == 'browser'" /> + <_runnerFilesToPublish Include="$(WasmTestRunnerDir)*" Condition="'$(TargetOS)' == 'Browser' or '$(TargetOS)' == 'wasi'" /> <_resolvedFilesToPublishToFileName Include="@(ResolvedFileToPublish -> '%(FileName)%(Extension)')" /> diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index 0d1bb2c063722..1677d0f4c328d 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -9,6 +9,9 @@ AppleHelixRunnerTemplate.sh AppleRunnerTemplate.sh AndroidRunnerTemplate.sh + WasiRunnerAOTTemplate.sh + WasiRunnerTemplate.sh + WasiRunnerTemplate.cmd WasmRunnerAOTTemplate.sh WasmRunnerTemplate.sh WasmRunnerTemplate.cmd @@ -23,7 +26,7 @@ RunTests.sh build-apple-app.sh - RunTests.cmd + RunTests.cmd $(AssemblyName).sh $([MSBuild]::NormalizePath('$(OutDir)', '$(RunScriptOutputName)')) $([MSBuild]::NormalizePath('$(OutDir)', '$(InnerRunScriptOutputName)')) @@ -63,12 +66,12 @@ - + @@ -78,7 +81,7 @@ <_ZipSourceDirectory>$(OutDir) - <_ZipSourceDirectory Condition="'$(TargetOS)' == 'browser' or '$(TestSingleFile)' == 'true'">$(BundleDir) + <_ZipSourceDirectory Condition="'$(TargetOS)' == 'browser' or '$(TargetOS)' == 'wasi' or '$(TestSingleFile)' == 'true'">$(BundleDir) @@ -94,8 +97,8 @@ - $(RunScriptCommand) $RSP_FILE - $(RunScriptCommand) %RSP_FILE% + $(RunScriptCommand) $RSP_FILE + $(RunScriptCommand) %RSP_FILE% $([MSBuild]::Escape('$(RunScriptCommand)')) diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets new file mode 100644 index 0000000000000..8f9a08b576899 --- /dev/null +++ b/eng/testing/tests.wasi.targets @@ -0,0 +1,293 @@ + + + + + + true + true + false + $(BundleTestAppTargets);BundleTestWasmApp + true + + + true + + + false + true + true + + WasmTestRunner.dll + + + <_ShellCommandSeparator Condition="'$(OS)' == 'Windows_NT'">&& + <_ShellCommandSeparator Condition="'$(OS)' != 'Windows_NT'">&& + true + <_UseWasmSymbolicator Condition="'$(TestTrimming)' != 'true'">true + true + false + _GetWorkloadsToInstall;$(InstallWorkloadUsingArtifactsDependsOn) + <_XHarnessTestsTimeout>00:30:00 + + $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasi', 'wasi-sdk')) + + + + + + + + true + + $(NoWarn);IL2118 + + $(NoWarn);IL2121 + + + + helix + helix + local + + + + <_AOT_InternalForceInterpretAssemblies Include="@(HighAotMemoryUsageAssembly)" /> + + + + <_AppArgs Condition="'$(WasmSingleFileBundle)' == 'true'">$([System.IO.Path]::GetFileNameWithoutExtension('$(WasmMainAssemblyFileName)')).wasm + <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true'">dotnet.wasm + + <_AppArgs Condition="'$(IsFunctionalTest)' != 'true'">$(_AppArgs) $(AssemblyName).dll + <_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) -- $(WasmTestAppArgs) + + + $(WasmXHarnessMonoArgs) --env=XHARNESS_LOG_TEST_START=1 + + + + <_XHarnessArgs Condition="'$(OS)' != 'Windows_NT'">wasi $XHARNESS_COMMAND --app=. --output-directory=$XHARNESS_OUT + <_XHarnessArgs Condition="'$(OS)' == 'Windows_NT'">wasi %XHARNESS_COMMAND% --app=. --output-directory=%XHARNESS_OUT% + + + <_XHarnessArgs Condition="'$(OS)' == 'Windows_NT' and '$(ContinuousIntegrationBuild)' != 'true'">$(_XHarnessArgs) --wasm-engine-path=$(WasmtimeDir)wasmtime.exe + <_XHarnessArgs Condition="'$(OS)' == 'Windows_NT' and '$(ContinuousIntegrationBuild)' == 'true'">$(_XHarnessArgs) --wasm-engine-path=%HELIX_CORRELATION_PAYLOAD%\wasmtime\wasmtime.exe + + <_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode) + <_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs) + <_XHarnessArgs Condition="'$(_XHarnessTestsTimeout)' != '' " >$(_XHarnessArgs) "--timeout=$(_XHarnessTestsTimeout)" + <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) + + + $HARNESS_RUNNER $(_XHarnessArgs) %24XHARNESS_ARGS %24WasmXHarnessArgs -- $(WasmXHarnessMonoArgs) %24WasmXHarnessMonoArgs $(_AppArgs) %24WasmTestAppArgs + %HARNESS_RUNNER% $(_XHarnessArgs) %XHARNESS_ARGS% %WasmXHarnessArgs% -- $(WasmXHarnessMonoArgs) %WasmXHarnessMonoArgs% $(_AppArgs) %WasmTestAppArgs% + + + + <_AOTBuildCommand Condition="'$(BrowserHost)' != 'windows'">_buildAOTFunc publish/ProxyProjectForAOTOnHelix.proj $XHARNESS_OUT/AOTBuild.binlog + <_AOTBuildCommand Condition="'$(BrowserHost)' == 'windows'">dotnet msbuild publish/ProxyProjectForAOTOnHelix.proj /bl:%XHARNESS_OUT%/AOTBuild.binlog + + <_AOTBuildCommand Condition="'$(BrowserHost)' == 'windows'">$(_AOTBuildCommand) "/p:WasmCachePath=%USERPROFILE%\.emscripten-cache" + + + <_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration) + + <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation) + <_AOTBuildCommand>$(_AOTBuildCommand) $(_ShellCommandSeparator) cd wasm_build/AppBundle + + $(_AOTBuildCommand) + $(_AOTBuildCommand) $(_ShellCommandSeparator) $(RunScriptCommand) + + + + + + + + + WasmTriggerPublishApp + $(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix + + + <_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(BundleDir), 'WasmTestRunner.runtimeconfig.json')) + exec "$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))" --runtime-config "$(_RuntimeConfigJsonPath)" $(WasmHostArguments) $(StartArguments) $(WasmXHarnessMonoArgs) $(_AppArgs) + + + + + + + + true + + + + PrepareForWasmBuildApp;$(WasmNestedPublishAppDependsOn) + + + + + + + + + + + + + + true + -O2 + + + + <_MainAssemblyPath Condition="'%(WasmAssembliesToBundle.FileName)' == $(AssemblyName) and '%(WasmAssembliesToBundle.Extension)' == '.dll'">%(WasmAssembliesToBundle.Identity) + $([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json')) + -Oz -Wl,-O0 -Wl,-lto-O0 + + + + + + + + + + + + + + + <_BundlePdbFiles Include="$([System.IO.Path]::ChangeExtension('%(WasmAssembliesToBundle.Identity)', '.pdb'))" /> + + + + + + <_WasmPropertyNames Include="AOTMode" /> + <_WasmPropertyNames Include="AssemblyName" /> + <_WasmPropertyNames Include="DisableParallelAot" /> + <_WasmPropertyNames Include="DisableParallelEmccCompile" /> + <_WasmPropertyNames Include="EmccCompileOptimizationFlag" /> + <_WasmPropertyNames Include="EmccLinkOptimizationFlag" /> + <_WasmPropertyNames Include="IncludeSatelliteAssembliesInVFS" /> + <_WasmPropertyNames Include="InvariantGlobalization" /> + <_WasmPropertyNames Include="WasmBuildNative" /> + <_WasmPropertyNames Include="WasmDebugLevel" /> + <_WasmPropertyNames Include="WasmDedup" /> + <_WasmPropertyNames Include="WasmLinkIcalls" /> + <_WasmPropertyNames Include="WasmNativeStrip" /> + <_WasmPropertyNames Include="_WasmDevel" /> + <_WasmPropertyNames Include="_WasmStrictVersionMatch" /> + <_WasmPropertyNames Include="WasmEmitSymbolMap" /> + + <_WasmPropertiesToPass + Include="$(%(_WasmPropertyNames.Identity))" + Name="%(_WasmPropertyNames.Identity)" + ConditionToUse__="%(_WasmPropertyNames.ConditionToUse__)" /> + + <_WasmVFSFilesToCopy Include="@(WasmFilesToIncludeInFileSystem)" /> + <_WasmVFSFilesToCopy TargetPath="%(FileName)%(Extension)" Condition="'%(_WasmVFSFilesToCopy.TargetPath)' == ''" /> + + <_WasmExtraFilesToCopy Include="@(WasmExtraFilesToDeploy)" /> + <_WasmExtraFilesToCopy TargetPath="%(FileName)%(Extension)" Condition="'%(_WasmExtraFilesToCopy.TargetPath)' == ''" /> + + + + <_WasmItemsToPass Include="@(_AOT_InternalForceInterpretAssemblies)" OriginalItemName__="_AOT_InternalForceInterpretAssemblies" /> + + + + + + + + + + + + + + $(BundleDir) + $(MonoProjectRoot)\wasm\test-main.js + $(InvariantGlobalization) + true + + true + -1 + + + + <_SatelliteAssemblies Include="$(PublishDir)*\*.resources.dll" /> + <_SatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" /> + <_SatelliteAssemblies TargetPath="%(CultureName)\%(FileName)%(Extension)" /> + + + + + + + + + <_CopyLocalPaths + Include="@(PublishItemsOutputGroupOutputs)" + Condition="'%(PublishItemsOutputGroupOutputs.BuildReference)' == 'true' and + !$([System.String]::new('%(PublishItemsOutputGroupOutputs.Identity)').EndsWith('.resources.dll'))" /> + + <_CopyLocalPaths TargetPath="%(_CopyLocalPaths.RelativePath)" Condition="'%(_CopyLocalPaths.RelativePath)' != ''" /> + <_CopyLocalPaths TargetPath="%(FileName)%(Extension)" Condition="'%(_CopyLocalPaths.RelativePath)' == ''" /> + + + + + + + + + + + <_PdbFilesToCheck Include="$([System.IO.Path]::ChangeExtension('%(ResolvedFileToPublish.Identity)', '.pdb'))" + Condition="'%(ResolvedFileToPublish.Extension)' == '.dll'" /> + + + + + + + + + + + + + + + + + + + diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets index f382ff6a13771..6516302093b32 100644 --- a/eng/testing/tests.wasm.targets +++ b/eng/testing/tests.wasm.targets @@ -1,16 +1,13 @@ - true + true true false $(BundleTestAppTargets);BundleTestWasmApp true full - - $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm', 'emsdk')) - true @@ -39,11 +36,6 @@ $(BundleDir) - - - true @@ -57,111 +49,43 @@ helix helix local - - <_WasmBrowserPathForTests Condition="'$(BROWSER_PATH_FOR_TESTS)' != ''">$(BROWSER_PATH_FOR_TESTS) - <_WasmBrowserPathForTests Condition="'$(_WasmBrowserPathForTests)' == '' and '$(InstallChromeForTests)' == 'true'">$(ChromeBinaryPath) <_AOT_InternalForceInterpretAssemblies Include="@(HighAotMemoryUsageAssembly)" /> - - - - - - - - - - - - - - - - <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(Scenario)' != 'BuildWasmApps' and '$(WasmMainAssemblyFileName)' == ''">--run WasmTestRunner.dll $(AssemblyName).dll - <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(WasmMainAssemblyFileName)' != ''">--run $(WasmMainAssemblyFileName) - <_AppArgs Condition="'$(IsFunctionalTest)' == 'true'">--run $(AssemblyName).dll - - <_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) $(WasmTestAppArgs) + + + - $(WasmXHarnessMonoArgs) --setenv=XHARNESS_LOG_TEST_START=1 - + + - - <_XHarnessArgs Condition="'$(OS)' != 'Windows_NT'">wasm $XHARNESS_COMMAND --app=. --output-directory=$XHARNESS_OUT - <_XHarnessArgs Condition="'$(OS)' == 'Windows_NT'">wasm %XHARNESS_COMMAND% --app=. --output-directory=%XHARNESS_OUT% - - <_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode) - <_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs) - <_XHarnessArgs Condition="('$(MonoWasmBuildVariant)' == 'multithread' or '$(MonoWasmBuildVariant)' == 'perftrace') and '$(_XHarnessArs.Contains("--web-server-use-cop")' != 'true'">$(_XHarnessArgs) --web-server-use-cop - <_XHarnessArgs >$(_XHarnessArgs) -s dotnet.js.symbols - <_XHarnessArgs Condition="'$(_UseWasmSymbolicator)' == 'true'" >$(_XHarnessArgs) --symbol-patterns wasm-symbol-patterns.txt - <_XHarnessArgs Condition="'$(_UseWasmSymbolicator)' == 'true'" >$(_XHarnessArgs) --symbolicator WasmSymbolicator.dll,Microsoft.WebAssembly.Internal.SymbolicatorWrapperForXHarness - <_XHarnessArgs Condition="'$(_WasmBrowserPathForTests)' != ''" >$(_XHarnessArgs) "--browser-path=$(_WasmBrowserPathForTests)" - <_XHarnessArgs Condition="'$(_XHarnessTestsTimeout)' != '' " >$(_XHarnessArgs) "--timeout=$(_XHarnessTestsTimeout)" - <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) - - - $HARNESS_RUNNER $(_XHarnessArgs) %24XHARNESS_ARGS %24WasmXHarnessArgs -- $(WasmXHarnessMonoArgs) %24WasmXHarnessMonoArgs $(_AppArgs) %24WasmTestAppArgs - %HARNESS_RUNNER% $(_XHarnessArgs) %XHARNESS_ARGS% %WasmXHarnessArgs% -- $(WasmXHarnessMonoArgs) %WasmXHarnessMonoArgs% $(_AppArgs) %WasmTestAppArgs% - - - - <_AOTBuildCommand Condition="'$(BrowserHost)' != 'windows'">_buildAOTFunc publish/ProxyProjectForAOTOnHelix.proj $XHARNESS_OUT/AOTBuild.binlog - <_AOTBuildCommand Condition="'$(BrowserHost)' == 'windows'">dotnet msbuild publish/ProxyProjectForAOTOnHelix.proj /bl:%XHARNESS_OUT%/AOTBuild.binlog - - <_AOTBuildCommand Condition="'$(BrowserHost)' == 'windows'">$(_AOTBuildCommand) "/p:WasmCachePath=%USERPROFILE%\.emscripten-cache" - - - <_AOTBuildCommand Condition="'$(ContinuousIntegrationBuild)' != 'true'">$(_AOTBuildCommand) /p:RuntimeSrcDir=$(RepoRoot) /p:RuntimeConfig=$(Configuration) - - <_AOTBuildCommand>$(_AOTBuildCommand) /p:RunAOTCompilation=$(RunAOTCompilation) - <_AOTBuildCommand>$(_AOTBuildCommand) $(_ShellCommandSeparator) cd wasm_build/AppBundle - - $(_AOTBuildCommand) - $(_AOTBuildCommand) $(_ShellCommandSeparator) $(RunScriptCommand) - + + + - - + + + + - WasmTriggerPublishApp - $(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix - - - <_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(BundleDir), 'WasmTestRunner.runtimeconfig.json')) - exec "$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))" --runtime-config "$(_RuntimeConfigJsonPath)" $(WasmHostArguments) $(StartArguments) $(WasmXHarnessMonoArgs) $(_AppArgs) + + - - true - - - - PrepareForWasmBuildApp;$(WasmNestedPublishAppDependsOn) - - - - - - + + + + @@ -170,11 +94,6 @@ AssemblyFile="$(WasmBuildTasksAssemblyPath)" /> - - true - -O2 - - <_MainAssemblyPath Condition="'%(WasmAssembliesToBundle.FileName)' == $(AssemblyName) and '%(WasmAssembliesToBundle.Extension)' == '.dll'">%(WasmAssembliesToBundle.Identity) $([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json')) @@ -204,9 +123,6 @@ <_WasmPropertyNames Include="AOTMode" /> <_WasmPropertyNames Include="AssemblyName" /> <_WasmPropertyNames Include="DisableParallelAot" /> - <_WasmPropertyNames Include="DisableParallelEmccCompile" /> - <_WasmPropertyNames Include="EmccCompileOptimizationFlag" /> - <_WasmPropertyNames Include="EmccLinkOptimizationFlag" /> <_WasmPropertyNames Include="IncludeSatelliteAssembliesInVFS" /> <_WasmPropertyNames Include="InvariantGlobalization" /> <_WasmPropertyNames Include="WasmBuildNative" /> @@ -253,7 +169,6 @@ $(BundleDir) - WasmTestRunner.dll $(MonoProjectRoot)\wasm\test-main.js $(InvariantGlobalization) true @@ -304,41 +219,12 @@ - - - - - <_NodeNpmModuleString Include="%(NodeNpmModule.Identity):%(NodeNpmModule.Alias)" /> - <_NodeNpmModuleStringTrimmed Include="@(_NodeNpmModuleString->Trim(':'))" /> - - - @(_NodeNpmModuleStringTrimmed, ',') - - - - - - - - - - - - - - - - - - - - - + --> + + + + + diff --git a/eng/testing/wasi-provisioning.targets b/eng/testing/wasi-provisioning.targets index ff5c5e02d5efd..29c82eadd4c92 100644 --- a/eng/testing/wasi-provisioning.targets +++ b/eng/testing/wasi-provisioning.targets @@ -1,3 +1,29 @@ - + + <_WasmtimeVersionFile>$(MonoProjectRoot)wasi/wasmtime-version.txt + true + + + + + + + + + + %(_VersionLines.Identity) + https://github.com/bytecodealliance/wasmtime/releases/download/v$(WasmtimeVersion)/wasmtime-v$(WasmtimeVersion)-x86_64-linux.tar.xz + https://github.com/bytecodealliance/wasmtime/releases/download/v$(WasmtimeVersion)/wasmtime-v$(WasmtimeVersion)-x86_64-macos.tar.xz + https://github.com/bytecodealliance/wasmtime/releases/download/v$(WasmtimeVersion)/wasmtime-v$(WasmtimeVersion)-x86_64-windows.zip + + + + + + + + + + + diff --git a/eng/testing/workloads-testing.targets b/eng/testing/workloads-testing.targets index cc7c243c6ba3b..068ed599884a2 100644 --- a/eng/testing/workloads-testing.targets +++ b/eng/testing/workloads-testing.targets @@ -23,8 +23,8 @@ <_DefaultPropsForNuGetBuild Include="Configuration=$(Configuration)" /> - <_DefaultPropsForNuGetBuild Include="TargetOS=browser" /> - <_DefaultPropsForNuGetBuild Include="TargetArchitecture=wasm" /> + <_DefaultPropsForNuGetBuild Include="TargetOS=$(TargetOS)" /> + <_DefaultPropsForNuGetBuild Include="TargetArchitecture=$(TargetArchitecture)" /> <_DefaultPropsForNuGetBuild Include="ContinuousIntegrationBuild=$(ContinuousIntegrationBuild)" /> @@ -157,7 +157,7 @@ DependsOnTargets="$(InstallWorkloadUsingArtifactsDependsOn)" Condition="'$(InstallWorkloadForTesting)' == 'true'" /> - + <_AOTCrossNuGetPath>$(LibrariesShippingPackagesDir)Microsoft.NETCore.App.Runtime.AOT.$(NETCoreSdkRuntimeIdentifier).Cross.$(RuntimeIdentifier).$(PackageVersionForWorkloadManifests).nupkg diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index ec204db5d1df4..5f56e0eb0bbea 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -252,6 +252,11 @@ + + + + + diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 9d81146e977cb..79356fc5306d3 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -47,8 +47,10 @@ public static partial class PlatformDetection public static bool Isillumos => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ILLUMOS")); public static bool IsSolaris => RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS")); public static bool IsBrowser => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")); + public static bool IsWasi => RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI")); public static bool IsNotBrowser => !IsBrowser; - public static bool IsMobile => IsBrowser || IsAppleMobile || IsAndroid; + public static bool IsNotWasi => !IsWasi; + public static bool IsMobile => IsBrowser || IsWasi || IsAppleMobile || IsAndroid; public static bool IsNotMobile => !IsMobile; public static bool IsAppleMobile => IsMacCatalyst || IsiOS || IstvOS; public static bool IsNotAppleMobile => !IsAppleMobile; @@ -114,13 +116,13 @@ public static bool IsPrivilegedProcess public static bool FileCreateCaseSensitive => IsCaseSensitiveOS; #endif - public static bool IsThreadingSupported => !IsBrowser; + public static bool IsThreadingSupported => !IsBrowser && !IsWasi; public static bool IsBinaryFormatterSupported => IsNotMobile && !IsNativeAot; public static bool IsStartingProcessesSupported => !IsiOS && !IstvOS; public static bool IsSpeedOptimized => !IsSizeOptimized; - public static bool IsSizeOptimized => IsBrowser || IsAndroid || IsAppleMobile; + public static bool IsSizeOptimized => IsBrowser || IsWasi || IsAndroid || IsAppleMobile; public static bool IsBrowserDomSupported => IsEnvironmentVariableTrue("IsBrowserDomSupported"); public static bool IsBrowserDomSupportedOrNotBrowser => IsNotBrowser || IsBrowserDomSupported; @@ -147,7 +149,7 @@ private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() // Drawing is not supported on non windows platforms in .NET 7.0+. public static bool IsDrawingSupported => IsWindows && IsNotWindowsNanoServer && IsNotWindowsServerCore; - public static bool IsAsyncFileIOSupported => !IsBrowser; + public static bool IsAsyncFileIOSupported => !IsBrowser && !IsWasi; public static bool IsLineNumbersSupported => !IsNativeAot; diff --git a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs index 46a942a0f9cde..1307a18a9eb51 100644 --- a/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs +++ b/src/libraries/Common/tests/WasmTestRunner/WasmTestRunner.cs @@ -11,6 +11,12 @@ public class SimpleWasmTestRunner : WasmApplicationEntryPoint { public static async Task Main(string[] args) { + if (args.Length == 0) + { + Console.WriteLine ($"No args given"); + return -1; + } + var testAssembly = args[0]; var excludedTraits = new List(); var includedTraits = new List(); diff --git a/src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj b/src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj index 370dc77ce5c03..2ec882a897084 100644 --- a/src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj +++ b/src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj @@ -8,7 +8,7 @@ --> $(Features.Replace('strict', '') - true + true diff --git a/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj b/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj index 3a838f8a275d1..de57ee0ab75e0 100644 --- a/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj +++ b/src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj @@ -2,7 +2,7 @@ 0436 $(NetCoreAppCurrent);$(NetFrameworkMinimum) - true + true diff --git a/src/libraries/System.Collections/tests/System.Collections.Tests.csproj b/src/libraries/System.Collections/tests/System.Collections.Tests.csproj index b728dc258b5f7..3d64df4f13ee5 100644 --- a/src/libraries/System.Collections/tests/System.Collections.Tests.csproj +++ b/src/libraries/System.Collections/tests/System.Collections.Tests.csproj @@ -2,7 +2,7 @@ $(NetCoreAppCurrent) true - true + true diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/AssemblyInfo.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/AssemblyInfo.cs index 9659dd75903a7..d3aede3cb97cf 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/AssemblyInfo.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/AssemblyInfo.cs @@ -4,4 +4,4 @@ using System; using Xunit; -[assembly: SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.iOS | TestPlatforms.tvOS, "System.IO.FileSystem.Watcher is not supported on Browser/iOS/tvOS")] +[assembly: SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.Wasi | TestPlatforms.iOS | TestPlatforms.tvOS, "System.IO.FileSystem.Watcher is not supported on Browser/iOS/tvOS")] diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.Unix.cs index 9455ff312b307..8a6b74de5fd35 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.Unix.cs @@ -11,11 +11,15 @@ public partial struct Guid public static unsafe Guid NewGuid() { Guid g; - +#if !TARGET_WASI // Guid.NewGuid is often used as a cheap source of random data that are sometimes used for security purposes. // Windows implementation uses secure RNG to implement it. We use secure RNG for Unix too to avoid subtle security // vulnerabilities in applications that depend on it. See https://github.com/dotnet/runtime/issues/42752 for details. Interop.GetCryptographicallySecureRandomBytes((byte*)&g, sizeof(Guid)); +#else + // TODOWASI: crypto secure random bytes + Interop.GetRandomBytes((byte*)&g, sizeof(Guid)); +#endif const ushort VersionMask = 0xF000; const ushort RandomGuidVersion = 0x4000; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index cdc2c7f54ffc8..bbe1ad497a9de 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -334,8 +334,8 @@ - - + + diff --git a/src/libraries/pretest.proj b/src/libraries/pretest.proj index 27c862055389a..6118eb1120e6a 100644 --- a/src/libraries/pretest.proj +++ b/src/libraries/pretest.proj @@ -22,16 +22,19 @@ - + - + + + + + true + wasmtime + true + + + PrepareHelixCorrelationPayload_Wasi; + _AddWorkItemsForLibraryTests + + + $(BuildHelixWorkItemsDependsOn);PrepareForBuildHelixWorkItems_Wasi + false + false + false + + + true + false + true + + + + + + + + + + + + + + + + + + + + $(IsRunningLibraryTests) + + + true + + + + + + true + + true + false + + + + + true + false + + + + + + + + + + + + + + + + + + + <_WasiWorkItem Include="$(TestArchiveRoot)browseronly/**/*.zip" Condition="'$(Scenario)' == 'WasmTestOnBrowser'" /> + + %(Identity) + $(HelixCommand) + $(_workItemTimeout) + + + + + + <_WasiSampleZipFile Condition="'$(Scenario)' == 'normal' or '$(Scenario)' == ''" Include="$(TestArchiveRoot)runonly/**/*.Console.V8.*.Sample.zip" /> + + + %(Identity) + $(HelixCommand) + $(_workItemTimeout) + + + <_DefaultWorkItems Remove="@(_DefaultWorkItems)" /> + <_DefaultWorkItems Include="$(WorkItemArchiveWildCard)" Exclude="$(HelixCorrelationPayload)" /> + + + %(Identity) + $(HelixCommand) + $(_workItemTimeout) + + + + diff --git a/src/libraries/sendtohelix.proj b/src/libraries/sendtohelix.proj index 71619fc40466a..a5d1ae7f45b6a 100644 --- a/src/libraries/sendtohelix.proj +++ b/src/libraries/sendtohelix.proj @@ -152,7 +152,7 @@ - + diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj index bfb6447e647b6..b2e072c486c84 100644 --- a/src/libraries/sendtohelixhelp.proj +++ b/src/libraries/sendtohelixhelp.proj @@ -17,8 +17,9 @@ true + - + - false + false @@ -93,12 +94,14 @@ + true + SetStressModes_$(Scenario).cmd - SetStressModes_$(Scenario).sh + SetStressModes_$(Scenario).sh ErrorAndContinue $(ArtifactsBinDir)\*.Tests\**\coverage.opencover.xml @@ -404,6 +405,11 @@ + + + + + @@ -538,6 +544,16 @@ + + + + + + + + + + @@ -563,7 +579,8 @@ + Condition="'$(TestAssemblies)' == 'true'" + BuildInParallel="$(BuildTestInParallel)" /> true - + @@ -661,6 +678,12 @@ BuildInParallel="false" /> + + + + - $([MSBuild]::NormalizeDirectory('$(ArtifactsObjDir)', 'wasi-sdk')) - $(ProvisionWasiSdkDir.Replace('\', '/')) - true - true + <_ProvisionWasiSdkDir>$([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory), 'wasi', 'wasi-sdk')) + true + $(_ProvisionWasiSdkDir) + $([MSBuild]::EnsureTrailingSlash('$(WASI_SDK_PATH)').Replace('\', '/')) diff --git a/src/mono/mono.proj b/src/mono/mono.proj index 23fabbcc0e921..d6a4a8fbdee56 100644 --- a/src/mono/mono.proj +++ b/src/mono/mono.proj @@ -45,7 +45,7 @@ $([MSBuild]::NormalizeDirectory('$(MonoObjDir)', 'cross')) $([MSBuild]::NormalizePath('$(MonoObjCrossDir)', 'config.h')) true - true + true $(Compiler) clang <_CompilerTargetArch Condition="'$(RealTargetArchitecture)' == ''">$(Platform) @@ -178,16 +178,15 @@ - - @@ -201,7 +200,7 @@ %(_ActualVersionLines.Identity) %(_ExpectedVersionLines.Identity) - @@ -437,6 +436,7 @@ <_MonoCFLAGS Condition="'$(MonoWasmThreads)' == 'true'" Include="$(EscapedQuoteW)-I$([MSBuild]::NormalizePath('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'wasi-wasm-threads', 'native', 'include').Replace('\','/'))$(EscapedQuoteW)"/> <_MonoCFLAGS Condition="'$(MonoWasmThreads)' != 'true'" Include="$(EscapedQuoteW)-I$([MSBuild]::NormalizePath('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'wasi-wasm', 'native', 'include').Replace('\','/'))$(EscapedQuoteW)"/> + <_MonoCCOption>CC="$(XcodeDir)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" @@ -591,18 +591,19 @@ <_MonoCMakeConfigureCommand>cmake @(_MonoCMakeArgs, ' ') $(MonoCMakeExtraArgs) "$(MonoProjectRoot.TrimEnd('\/'))" <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' != 'true' and '$(TargetsWasi)' != 'true' and '$(_MonoSkipInitCompiler)' != 'true' and '$(HostOS)' != 'windows'">sh -c 'build_arch="$(_CompilerTargetArch)" compiler="$(MonoCCompiler)" . "$(RepositoryEngineeringCommonDir)native/init-compiler.sh" && @(_MonoBuildEnv, ' ') $(_MonoCMakeConfigureCommand)' <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' != 'true' and '$(TargetsWasi)' != 'true' and '$(_MonoSkipInitCompiler)' == 'true' and '$(HostOS)' != 'windows'">$(_MonoCCOption) $(_MonoCXXOption) @(_MonoBuildEnv, ' ') $(_MonoCMakeConfigureCommand) + <_MonoCMakeConfigureCommand Condition="'$(TargetsWasi)' == 'true'">$(_MonoCMakeConfigureCommand) -DWASI_SDK_PREFIX=$(WASI_SDK_PATH) -DCMAKE_SYSROOT=$(WASI_SDK_PATH)share/wasi-sysroot -DCMAKE_TOOLCHAIN_FILE=$(WASI_SDK_PATH)share/cmake/wasi-sdk.cmake -DCMAKE_CXX_FLAGS="--sysroot=$(WASI_SDK_PATH)share/wasi-sysroot" + <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' != 'true' and '$(TargetsWasi)' != 'true' and '$(HostOS)' == 'windows'">call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" $(_CompilerTargetArch) && cd /D "$(MonoObjDir)" && @(_MonoBuildEnv, ' ') $(_MonoCMakeConfigureCommand) <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' == 'true' and '$(HostOS)' != 'windows'">bash -c 'source $(EMSDK_PATH)/emsdk_env.sh 2>&1 && emcmake $(_MonoCMakeConfigureCommand)' <_MonoCMakeConfigureCommand Condition="'$(TargetsBrowser)' == 'true' and '$(HostOS)' == 'windows'">call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && call "$([MSBuild]::NormalizePath('$(EMSDK_PATH)', 'emsdk_env.bat'))" && emcmake $(_MonoCMakeConfigureCommand) - - <_MonoCMakeConfigureCommand Condition="'$(TargetsWasi)' == 'true'">$(_MonoCMakeConfigureCommand) -DWASI_SDK_PREFIX=$(WASI_SDK_PATH) -DCMAKE_SYSROOT=$(WASI_SDK_PATH)share/wasi-sysroot -DCMAKE_TOOLCHAIN_FILE=$(WASI_SDK_PATH)share/cmake/wasi-sdk.cmake -DCMAKE_CXX_FLAGS="--sysroot=$(WASI_SDK_PATH)share/wasi-sysroot" + <_MonoCMakeConfigureCommand Condition="'$(TargetsWasi)' == 'true' and '$(HostOS)' == 'windows'">call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && cmake $(_MonoCMakeConfigureCommand) <_MonoCMakeBuildCommand>cmake --build . --target install --config $(Configuration) <_MonoCMakeBuildCommand Condition="'$(MonoVerboseBuild)' == 'true'">$(_MonoCMakeBuildCommand) --verbose <_MonoCMakeBuildCommand Condition="'$(_MonoUseNinja)' != 'true'">$(_MonoCMakeBuildCommand) --parallel $([System.Environment]::ProcessorCount) <_MonoCMakeBuildCommand Condition="'$(TargetsBrowser)' != 'true' and '$(TargetsWasi)' != 'true' and '$(HostOS)' != 'windows'">@(_MonoBuildEnv, ' ') $(_MonoCMakeBuildCommand) <_MonoCMakeBuildCommand Condition="'$(TargetsBrowser)' != 'true' and '$(TargetsWasi)' != 'true' and '$(HostOS)' == 'windows'">call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" $(_CompilerTargetArch) && cd /D "$(MonoObjDir)" && @(_MonoBuildEnv, ' ') $(_MonoCMakeBuildCommand) - <_MonoCMakeBuildCommand Condition="'$(TargetsBrowser)' == 'true' and '$(HostOS)' == 'windows'">call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && $(_MonoCMakeBuildCommand) + <_MonoCMakeBuildCommand Condition="('$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true') and '$(HostOS)' == 'windows'">call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && $(_MonoCMakeBuildCommand) @@ -806,6 +807,7 @@ + @@ -1066,5 +1068,4 @@ - diff --git a/src/mono/mono/metadata/sgen-mono.c b/src/mono/mono/metadata/sgen-mono.c index 446ddb2c2e65e..ea70608fbf3f1 100644 --- a/src/mono/mono/metadata/sgen-mono.c +++ b/src/mono/mono/metadata/sgen-mono.c @@ -234,7 +234,7 @@ sgen_has_critical_method (void) gboolean mono_gc_is_critical_method (MonoMethod *method) { -#ifdef HOST_WASM +#if defined(HOST_WASM) || defined(HOST_WASI) //methods can't be critical under wasm due to the single thread'ness of it return FALSE; #else @@ -2281,7 +2281,7 @@ pin_handle_stack_interior_ptrs (void **ptr_slot, void *user_data) sgen_conservatively_pin_objects_from (ptr_slot, ptr_slot+1, ud->start_nursery, ud->end_nursery, PIN_TYPE_STACK); } -#ifdef HOST_WASM +#if defined(HOST_WASM) || defined(HOST_WASI) extern gboolean mono_wasm_enable_gc; #endif @@ -2293,7 +2293,7 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p { scan_area_arg_start = start_nursery; scan_area_arg_end = end_nursery; -#ifdef HOST_WASM +#if defined(HOST_WASM) || defined(HOST_WASI) //Under WASM we don't scan thread stacks and we can't trust the values we find there either. if (!mono_wasm_enable_gc) return; diff --git a/src/mono/mono/sgen/sgen-gc.c b/src/mono/mono/sgen/sgen-gc.c index fcb1acce709cd..0bdc7fd852a96 100644 --- a/src/mono/mono/sgen/sgen-gc.c +++ b/src/mono/mono/sgen/sgen-gc.c @@ -2737,7 +2737,9 @@ gc_pump_callback (void) sgen_perform_collection_inner (gc_request.requested_size, gc_request.generation_to_collect, gc_request.reason, TRUE, TRUE); gc_request.generation_to_collect = 0; } +#endif +#if defined(HOST_BROWSER) || defined(HOST_WASI) extern gboolean mono_wasm_enable_gc; #endif diff --git a/src/mono/mono/utils/CMakeLists.txt b/src/mono/mono/utils/CMakeLists.txt index 15c1630dec160..69c5a3ccd8ccf 100644 --- a/src/mono/mono/utils/CMakeLists.txt +++ b/src/mono/mono/utils/CMakeLists.txt @@ -222,6 +222,8 @@ elseif(TARGET_S390X) set(utils_arch_sources "${utils_arch_sources};mono-hwcap-s390x.c") elseif(TARGET_WASM) set(utils_arch_sources "${utils_arch_sources};mono-hwcap-wasm.c;mono-mmap-wasm.c") +elseif(TARGET_WASI) +set(utils_arch_sources "${utils_arch_sources};mono-hwcap-wasm.c") elseif(TARGET_POWERPC OR TARGET_POWERPC64) set(utils_arch_sources "${utils_arch_sources};mono-hwcap-ppc.c") else() diff --git a/src/mono/mono/utils/mono-threads-wasi.S b/src/mono/mono/utils/mono-threads-wasi.S index f2669f19d5e9b..6de6429445618 100644 --- a/src/mono/mono/utils/mono-threads-wasi.S +++ b/src/mono/mono/utils/mono-threads-wasi.S @@ -1,16 +1,15 @@ -.globl get_wasm_stack_high -.globl get_wasm_stack_low +// TODO after https://github.com/llvm/llvm-project/commit/1532be98f99384990544bd5289ba339bca61e15b +// use __stack_low && __stack_high -get_wasm_stack_high: - .functype get_wasm_stack_high () -> (i32) +.globl get_wasm_data_end +.globl get_wasm_heap_base + +get_wasm_data_end: + .functype get_wasm_data_end () -> (i32) global.get __data_end@GOT - // TODO after https://github.com/llvm/llvm-project/commit/1532be98f99384990544bd5289ba339bca61e15b - // global.get __stack_high@GOT end_function -get_wasm_stack_low: - .functype get_wasm_stack_low () -> (i32) +get_wasm_heap_base: + .functype get_wasm_heap_base () -> (i32) global.get __heap_base@GOT - // TODO after https://github.com/llvm/llvm-project/commit/1532be98f99384990544bd5289ba339bca61e15b - // global.get __stack_low@GOT end_function diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index e2031660baf4d..74cc69825f29f 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -43,24 +43,36 @@ wasm_get_stack_size (void) #else /* WASI */ +// TODO after https://github.com/llvm/llvm-project/commit/1532be98f99384990544bd5289ba339bca61e15b +// use __stack_low && __stack_high // see mono-threads-wasi.S -uintptr_t get_wasm_stack_high(void); -uintptr_t get_wasm_stack_low(void); +uintptr_t get_wasm_heap_base(void); +uintptr_t get_wasm_data_end(void); static int -wasm_get_stack_base (void) +wasm_get_stack_size (void) { - return get_wasm_stack_high(); - // this will need change for multithreading as the stack will allocated be per thread at different addresses + /* + * | -- increasing address ---> | + * | data (data_end)| stack |(heap_base) heap | + */ + size_t heap_base = get_wasm_heap_base(); + size_t data_end = get_wasm_data_end(); + size_t max_stack_size = heap_base - data_end; + + g_assert (data_end > 0); + g_assert (heap_base > data_end); + + // this is the max available stack size size, + // return a 16-byte aligned smaller size + return max_stack_size & ~0xF; } static int -wasm_get_stack_size (void) +wasm_get_stack_base (void) { - // keep in sync with src\mono\wasi\wasi.proj stack-size - return 8388608; - // TODO after https://github.com/llvm/llvm-project/commit/1532be98f99384990544bd5289ba339bca61e15b - // return (guint8*)get_wasm_stack_high () - (guint8*)get_wasm_stack_low (); + return get_wasm_data_end(); + // this will need further change for multithreading as the stack will allocated be per thread at different addresses } #endif diff --git a/src/mono/sample/wasi/Directory.Build.targets b/src/mono/sample/wasi/Directory.Build.targets index 353d73bf2e7cd..ffea4d25b08d8 100644 --- a/src/mono/sample/wasi/Directory.Build.targets +++ b/src/mono/sample/wasi/Directory.Build.targets @@ -3,72 +3,19 @@ - true + true + <_SampleProject Condition="'$(_SampleProject)' == ''">$(MSBuildProjectFile) + <_SampleAssembly Condition="'$(_SampleAssembly)' == ''">$(TargetFileName) + <_ExeExt Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">.exe - - - - <_ScriptExt Condition="'$(OS)' == 'Windows_NT'">.cmd - <_ScriptExt Condition="'$(OS)' != 'Windows_NT'">.sh - <_Dotnet>$(RepoRoot)dotnet$(_ScriptExt) - <_AOTFlag Condition="'$(RunAOTCompilation)' != ''">/p:RunAOTCompilation=$(RunAOTCompilation) - <_SampleProject Condition="'$(_SampleProject)' == ''">$(MSBuildProjectFile) - <_WasmMainJSFileName>$([System.IO.Path]::GetFileName('$(WasmMainJSPath)')) - - - - - - - - - - - - - - - - - - %(_VersionLines.Identity) - https://github.com/bytecodealliance/wasmtime/releases/download/v$(WasmtimeVersion)/wasmtime-v$(WasmtimeVersion)-x86_64-linux.tar.xz - https://github.com/bytecodealliance/wasmtime/releases/download/v$(WasmtimeVersion)/wasmtime-v$(WasmtimeVersion)-x86_64-macos.tar.xz - https://github.com/bytecodealliance/wasmtime/releases/download/v$(WasmtimeVersion)/wasmtime-v$(WasmtimeVersion)-x86_64-windows.zip - - - - - - - - - - - - - - - + + + + diff --git a/src/mono/sample/wasi/console/Makefile b/src/mono/sample/wasi/console/Makefile new file mode 100644 index 0000000000000..eae68ce1d83d3 --- /dev/null +++ b/src/mono/sample/wasi/console/Makefile @@ -0,0 +1,15 @@ +TOP=../../../../.. + +include ../wasi.mk + +ifneq ($(AOT),) +override MSBUILD_ARGS+=/p:RunAOTCompilation=true +endif + +ifneq ($(V),) +DOTNET_MONO_LOG_LEVEL=--setenv=MONO_LOG_LEVEL=debug +endif + +PROJECT_NAME=Wasi.Console.Sample.csproj + +run: run-console diff --git a/src/mono/sample/wasi/console/Program.cs b/src/mono/sample/wasi/console/Program.cs index c65033a418ebb..f46e3ce16b21d 100644 --- a/src/mono/sample/wasi/console/Program.cs +++ b/src/mono/sample/wasi/console/Program.cs @@ -6,10 +6,12 @@ public class Test { - public static int Main() + public static int Main(string[] args) { Console.WriteLine(""); - Console.WriteLine("Hello World!"); + Console.WriteLine($"Hello World! Got {args.Length} args"); + foreach (string arg in args) + Console.WriteLine ($"arg: {arg}"); Console.WriteLine(""); return 0; } diff --git a/src/mono/sample/wasi/console/Wasi.Console.Sample.csproj b/src/mono/sample/wasi/console/Wasi.Console.Sample.csproj index 6811cc3a1aeef..042811854a925 100644 --- a/src/mono/sample/wasi/console/Wasi.Console.Sample.csproj +++ b/src/mono/sample/wasi/console/Wasi.Console.Sample.csproj @@ -1,7 +1,6 @@ $(NetCoreAppCurrent) - false diff --git a/src/mono/sample/wasi/wasi.mk b/src/mono/sample/wasi/wasi.mk new file mode 100644 index 0000000000000..a9a937612f8e3 --- /dev/null +++ b/src/mono/sample/wasi/wasi.mk @@ -0,0 +1,26 @@ +DOTNET=$(TOP)/dotnet.sh + +ifeq ($(V),) +DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary -bl +else +DOTNET_Q_ARGS=--nologo -bl +endif + +CONFIG?=Release + +WASM_DEFAULT_BUILD_ARGS?=/p:TargetArchitecture=wasm /p:TargetOS=wasi /p:Configuration=$(CONFIG) +WASMTIME_PROV_PATH=$(realpath $(TOP)/artifacts/obj/wasmtime/wasmtime) + +all: publish + +build: + $(DOTNET) build $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) $(MSBUILD_ARGS) $(PROJECT_NAME) + +publish: + $(DOTNET) publish $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) -p:WasmBuildOnlyAfterPublish=true $(MSBUILD_ARGS) $(PROJECT_NAME) + +clean: + rm -rf bin $(TOP)/artifacts/obj/mono/$(PROJECT_NAME:%.csproj=%) + +run-console: + cd bin/wasi-wasm/AppBundle && $(WASMTIME_PROV_PATH) run $(PROJECT_NAME:.csproj=.wasm) $(ARGS) diff --git a/src/mono/wasi/.gitignore b/src/mono/wasi/.gitignore new file mode 100644 index 0000000000000..c2313f66ee085 --- /dev/null +++ b/src/mono/wasi/.gitignore @@ -0,0 +1,2 @@ +wasi-sdk/** +wasmtime/** diff --git a/src/mono/wasi/Makefile b/src/mono/wasi/Makefile new file mode 100644 index 0000000000000..d505e8ace0f15 --- /dev/null +++ b/src/mono/wasi/Makefile @@ -0,0 +1,107 @@ +TOP=$(realpath $(CURDIR)/../../..) +-include Make.config + +escape_quote = $(subst ",\",$(1)) + +ifneq ($(V),) +MSBUILD_ARGS+=/p:MonoVerboseBuild=true +MSBUILD_ARGS+=-bl +endif + +DOTNET=$(TOP)/dotnet.sh + +CONFIG?=Release +BINDIR?=$(TOP)/artifacts/bin +OBJDIR?=$(TOP)/artifacts/obj +_MSBUILD_WASM_BUILD_ARGS=/p:TargetOS=wasi /p:TargetArchitecture=wasm /p:Configuration=$(CONFIG) +HELIX_TARGET_QUEUE?=Ubuntu.1804.Amd64.Open + +all: build-all + +MONO_OBJ_DIR=$(OBJDIR)/mono/wasi.wasm.$(CONFIG) +BUILDS_OBJ_DIR=$(MONO_OBJ_DIR)/wasm + +# +# Helper targets +# + +.PHONY: runtime +.PHONY: build + +build: + $(TOP)/build.sh mono+libs.pretest -os wasi -c $(CONFIG) --binaryLog /p:ContinueOnError=false /p:StopOnFirstFailure=true $(MSBUILD_ARGS) + +build-all: + $(TOP)/build.sh mono+libs+packs -os wasi -c $(CONFIG) --binaryLog /p:ContinueOnError=false /p:StopOnFirstFailure=true $(MSBUILD_ARGS) + +runtime: + $(TOP)/build.sh mono.runtime+mono.wasiruntime+libs.native+libs.pretest -os wasi -c $(CONFIG) /p:ContinueOnError=false /p:StopOnFirstFailure=true $(MSBUILD_ARGS) + +# Rebuild only the mono runtime+cross compiler, don't build dotnet.wasm +mono-runtime: + $(TOP)/build.sh mono.runtime+libs.native+libs.pretest -os wasi -c $(CONFIG) /p:ContinueOnError=false /p:StopOnFirstFailure=true $(MSBUILD_ARGS) + +corlib: + $(TOP)/build.sh mono.corelib+mono.wasiruntime+libs.pretest -os wasi -c $(CONFIG) /p:ContinueOnError=false /p:StopOnFirstFailure=true $(MSBUILD_ARGS) + +test-runner: + $(DOTNET) build $(TOP)/src/libraries/Common/tests/WasmTestRunner /p:Configuration=$(CONFIG) $(MSBUILD_ARGS) + +app-builder: + $(DOTNET) build $(TOP)/src/tasks/WasmAppBuilder + +build-tasks: + $(DOTNET) build $(TOP)/src/tasks/WasmBuildTasks $(MSBUILD_ARGS) + +clean: + $(RM) -rf $(BUILDS_OBJ_DIR) + +run-tests-%: + $(DOTNET) build $(TOP)/src/libraries/$*/tests/ /t:Test $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) + +run-build-tests: + $(DOTNET) build $(TOP)/src/mono/wasi//Wasi.Build.Tests/ /t:Test /p:Configuration=$(CONFIG) $(MSBUILD_ARGS) + +build-debugger-tests-helix: + $(DOTNET) build -restore -bl:$(TOP)/artifacts/log/$(CONFIG)/Wasm.Debugger.Tests.binlog \ + /p:ContinuousIntegrationBuild=true /p:ArchiveTests=true \ + $(TOP)/src/mono/wasm/debugger/Wasm.Debugger.Tests/Wasm.Debugger.Tests.csproj \ + $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) + +submit-debugger-tests-helix: build-debugger-tests-helix + BUILD_REASON=wasm-test SYSTEM_TEAMPROJECT=public BUILD_REPOSITORY_NAME=dotnet/runtime BUILD_SOURCEBRANCH=main \ + $(TOP)/eng/common/msbuild.sh --ci -restore $(TOP)/src/libraries/sendtohelix.proj \ + /p:TestRunNamePrefixSuffix=WasmDebugger /p:HelixBuild=`date "+%Y%m%d.%H%M"` /p:Creator=`whoami` \ + /bl:$(TOP)/artifacts/log/$(CONFIG)/SendToHelix.binlog -p:HelixTargetQueue=$(HELIX_TARGET_QUEUE) \ + /p:RuntimeFlavor=mono /p:TargetRuntimeIdentifier= /p:MonoForceInterpreter= /p:TestScope=innerloop \ + /p:_Scenarios=wasmdebuggertests \ + $(_MSBUILD_WASM_BUILD_ARGS) \ + $(MSBUILD_ARGS) + +submit-tests-helix: + echo "\n** This will submit all the available test zip files to helix **\n" + BUILD_REASON=wasm-test SYSTEM_TEAMPROJECT=public BUILD_REPOSITORY_NAME=dotnet/runtime BUILD_SOURCEBRANCH=main \ + $(TOP)/eng/common/msbuild.sh --ci -restore $(TOP)/src/libraries/sendtohelix.proj \ + /p:TestRunNamePrefixSuffix=WasiTests /p:HelixBuild=`date "+%Y%m%d.%H%M"` /p:Creator=`whoami` \ + /bl:$(TOP)/artifacts/log/$(CONFIG)/SendToHelix.binlog -v:n -p:HelixTargetQueue=$(HELIX_TARGET_QUEUE) \ + /p:RuntimeFlavor=mono /p:TargetRuntimeIdentifier= /p:MonoForceInterpreter= /p:TestScope=innerloop \ + $(_MSBUILD_WASM_BUILD_ARGS) \ + $(MSBUILD_ARGS) + +run-debugger-tests: + rm -f $(TOP)/artifacts/bin/DebuggerTestSuite/x64/Debug/*log; \ + if [ ! -z "$(TEST_FILTER)" ]; then \ + $(DOTNET) test $(TOP)/src/mono/wasm/debugger/DebuggerTestSuite $(MSBUILD_ARGS) "-l:trx;LogFileName=DebuggerTestsResults.xml" --results-directory $(TOP)/artifacts/log/$(CONFIG) --filter "Category!=failing&FullyQualifiedName$(TEST_FILTER)" $(TEST_ARGS); \ + else \ + $(DOTNET) test $(TOP)/src/mono/wasm/debugger/DebuggerTestSuite $(MSBUILD_ARGS) "-l:trx;LogFileName=DebuggerTestsResults.xml" --results-directory $(TOP)/artifacts/log/$(CONFIG) --filter "Category!=failing" $(TEST_ARGS); \ + fi + +build-dbg-proxy: + $(DOTNET) build $(TOP)/src/mono/wasm/debugger/wasiDebugHost $(MSBUILD_ARGS) +build-dbg-testsuite: + $(DOTNET) build $(TOP)/src/mono/wasm/debugger/DebuggerTestSuite $(MSBUILD_ARGS) +build-app-host: + $(DOTNET) build $(TOP)/src/mono/wasm/host $(_MSBUILD_WASM_BUILD_ARGS) $(MSBUILD_ARGS) + +patch-deterministic: + cd emsdk/upstream/emscripten/ && patch -p1 < ../../../runtime/deterministic.diff diff --git a/src/mono/wasi/build/WasiApp.InTree.props b/src/mono/wasi/build/WasiApp.InTree.props index 8c119d5413b58..585dddf9454be 100644 --- a/src/mono/wasi/build/WasiApp.InTree.props +++ b/src/mono/wasi/build/WasiApp.InTree.props @@ -1,2 +1,23 @@ + + + + + AnyCPU + false + $(NetCoreAppCurrent) + $([MSBuild]::NormalizeDirectory($(MonoProjectRoot), 'wasm', 'wasi-sdk')) + false + true + partial + false + + + + <_MonoRuntimeComponentDontLink Include="libmono-component-debugger-stub-static.a" /> + + <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a" Condition="'$(FeatureWasmPerfTracing)' != 'true' and $(FeatureWasmThreads) != 'true'"/> + <_MonoRuntimeComponentDontLink Include="libmono-component-hot_reload-stub-static.a" /> + <_MonoRuntimeComponentDontLink Include="libmono-component-marshal-ilgen-stub-static.a" /> + diff --git a/src/mono/wasi/build/WasiApp.InTree.targets b/src/mono/wasi/build/WasiApp.InTree.targets index 8c119d5413b58..ece3fb2d1a3b5 100644 --- a/src/mono/wasi/build/WasiApp.InTree.targets +++ b/src/mono/wasi/build/WasiApp.InTree.targets @@ -1,2 +1,74 @@ + + + + + + + + + + + + <_WasmMainJSFileName>$([System.IO.Path]::GetFileName('$(WasmMainJSPath)')) + + + + + + + + + + + + + + + + + + + + <_LocalMicrosoftNetCoreAppRuntimePackDir>$([MSBuild]::NormalizeDirectory($(ArtifactsBinDir), 'microsoft.netcore.app.runtime.wasi-wasm', $(Configuration))) + + + + + + + + + + + + + + + + + + + + $(MSBuildProjectName) + + + $(TargetOS).AnyCPU.$(Configuration) + $([MSBuild]::NormalizeDirectory($(ArtifactsDir), 'helix')) + $([MSBuild]::NormalizeDirectory($(HelixArchiveRoot), 'runonly')) + $([MSBuild]::NormalizeDirectory($(HelixArchiveRunOnlyRoot), $(OSPlatformConfig), $(WasmHelixTestAppRelativeDir))) + $(OutputPath)$(AssemblyName).zip + + + + diff --git a/src/mono/wasi/build/WasiApp.Native.targets b/src/mono/wasi/build/WasiApp.Native.targets new file mode 100644 index 0000000000000..439b6360393f8 --- /dev/null +++ b/src/mono/wasi/build/WasiApp.Native.targets @@ -0,0 +1,592 @@ + + + + + + + + + <_WasiBuildNativeCoreDependsOn> + _SetupWasiSdk; + _SetWasmBuildNativeDefaults; + _PrepareForWasiBuildNative; + _GetNativeFilesForLinking; + _GenerateManagedToNative; + _GenerateAssemblyObjectFiles; + _WasiLink; + + + + + + + + + <_ExeExt Condition="$([MSBuild]::IsOSPlatform('WINDOWS'))">.exe + + + + + + + + + <_MonoComponent Include="hot_reload;debugger" /> + + + <_MonoComponent Include="marshal-ilgen" /> + + + + + + + <_ToolchainMissingPaths Condition="'$(_ToolchainMissingPaths)' == '' and ('$(WasiClang)' == '' or !Exists('$(WasiClang)'))">%24(WasiClang)=$(WasiClang) + + + + <_ToolchainMissingErrorMessage Condition="'$(WASI_SDK_PATH)' == '' and '$(WasiSdkRoot)' == ''">Could not find wasi-sdk. Either set %24(WASI_SDK_PATH), or use workloads to get the sdk. + <_IsToolchainMissing Condition="'$(_ToolchainMissingErrorMessage)' != ''">true + + + + + + + + + + + + + true + + true + + true + + false + + + + + + true + true + true + + + false + + + true + + false + + + + + + + false + + + + + + + + + + <_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm')) + <_WasiClangDefaultFlagsRsp>$([MSBuild]::NormalizePath($(_WasmRuntimePackSrcDir), 'emcc-default.rsp')) + <_WasiClangDefaultLinkFlagsRsp>$([MSBuild]::NormalizePath($(_WasmRuntimePackSrcDir), 'emcc-link.rsp')) + false + true + true + $(WasmBuildNative) + + <_WasmICallTablePath>$(_WasmIntermediateOutputPath)icall-table.h + + <_WasmPInvokeTablePath>$(_WasmIntermediateOutputPath)pinvoke-table.h + <_WasmInterpToNativeTablePath>$(_WasmIntermediateOutputPath)wasm_m2n_invoke.g.h + <_WasmPInvokeHPath>$(_WasmRuntimePackIncludeDir)wasm\pinvoke.h + <_DriverGenCPath>$(_WasmIntermediateOutputPath)driver-gen.c + false + $(DisableParallelAot) + + <_DriverGenCNeeded Condition="'$(_DriverGenCNeeded)' == '' and '$(_WasmShouldAOT)' == 'true'">true + + <_WasmDevel Condition="'$(_WasmDevel)' == '' and '$(WasmBuildNative)' == 'true' and '$(Configuration)' == 'Debug'">true + + + + + + + + + + + + + + + + + + + 52428800 + + + + <_WasmLinkDependencies Remove="@(_WasmLinkDependencies)" /> + + + + + + + + + + + <_WasmCommonIncludePaths Include="$(_WasmIntermediateOutputPath.TrimEnd('\/'))" /> + <_WasmCommonIncludePaths Include="$(_WasmRuntimePackIncludeDir)mono-2.0" /> + <_WasmCommonIncludePaths Include="$(_WasmRuntimePackIncludeDir)wasm" /> + + + + + <_WasmCommonIncludePathsFixedUp Include="$([System.String]::new(%(_WasmCommonIncludePaths.Identity)).Replace('\', '/'))" /> + <_WasmCommonIncludePaths Remove="@(_WasmCommonIncludePaths)" /> + <_WasmCommonIncludePaths Include="@(_WasmCommonIncludePathsFixedUp)" /> + + + + <_WasmCommonCFlags Include="-DGEN_PINVOKE=1" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_WasmPInvokeModules Include="%(_WasmNativeFileForLinking.FileName)" Condition="'%(_WasmNativeFileForLinking.ScanForPInvokes)' != 'false'" /> + + <_WasmPInvokeModules Include="libSystem.Native" /> + + + + + + + <_HasMscorlib Condition="'%(_WasmAssembliesInternal.FileName)%(_WasmAssembliesInternal.Extension)' == 'mscorlib.dll'">true + <_MscorlibPath Condition="'$(_HasMscorlib)' != 'true'">$(MicrosoftNetCoreAppRuntimePackRidDir)lib\net8.0\mscorlib.dll + + + + + + + + + + + + + + + + + + + + + + + + <_WasmEHLib Condition="'$(WasmEnableExceptionHandling)' == 'true'">libmono-wasm-eh-wasm.a + + + <_WasmEHLibToExclude Condition="'$(WasmEnableExceptionHandling)' != 'true'">libmono-wasm-eh-wasm.a + + + + + + + <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a" /> + + <_WasmNativeFileForLinking + Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)*.a" + Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)%(Identity)')" /> + <_WasmNativeFileForLinking Condition="'$(_WasmEHLib)' != ''" Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)$(_WasmEHLib)" /> + <_WasmNativeFileForLinking Remove="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)$(_WasmEHLibToExclude)" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_GetBundledFileSourcePath>$(_WasmIntermediateOutputPath)dotnet_wasi_getbundledfile.c + + + + + + + + + $(_WasmIntermediateOutputPath)%(WasmBundleFilesWithHashes.Filename)%(WasmBundleFilesWithHashes.Extension).$([System.String]::Copy(%(WasmBundleFilesWithHashes.FileHash)).Substring(0, 8)).o + + + + + + + + + + <_WasiObjectFilesForBundle Include="$(_GetBundledFileSourcePath)" /> + <_WasiObjectFilesForBundle Include="%(WasmBundleFilesWithHashes.ObjectFile)" /> + + + + + + + + + + + + + + + + + <_WasiGetEntrypointCFile>$(_WasmIntermediateOutputPath)entrypoint_$(WasmMainAssemblyFileName).c + + + + + + + + + + <_WasmRuntimePackSrcFile Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)src/*.c" /> + + + + + <_WasiSdkLinkerFlags Include="--initial-memory=$(WasmInitialHeapSize)" /> + + <_WasiFilePathForFixup Include="$(_WasiGetEntrypointCFile)" /> + <_WasiFilePathForFixup Include="@(_WasiObjectFilesForBundle)" /> + <_WasiFilePathForFixup Include="@(_WasmNativeFileForLinking)" /> + <_WasiFilePathForFixup Include="@(_WasmRuntimePackSrcFile)" /> + + <_WasiSdkClangArgs Condition="'$(OS)' == 'Windows_NT'" Include="$([System.String]::new(%(_WasiFilePathForFixup.Identity)).Replace('\', '/'))" /> + <_WasiSdkClangArgs Condition="'$(OS)' != 'Windows_NT'" Include="@(_WasiFilePathForFixup)" /> + + <_WasiSdkClangArgs Include="@(_WasmCommonCFlags)" /> + + <_WasiSdkClangArgs Include=""-I%(_WasmCommonIncludePaths.Identity)"" /> + <_WasiSdkClangArgs Include="--sysroot="$(WasiSdkRoot.Replace('\', '/'))/share/wasi-sysroot"" /> + <_WasiSdkClangArgs Include="-I"$(MicrosoftNetCoreAppRuntimePackRidNativeDir.Replace('\', '/'))include"" /> + <_WasiSdkClangArgs Include="-Wl,--export=malloc,--export=free,--export=__heap_base,--export=__data_end" /> + + <_WasiSdkClangArgs Include="-Wl,-z,stack-size=8388608,-lwasi-emulated-process-clocks,-lwasi-emulated-signal,-lwasi-emulated-mman"/> + <_WasiSdkClangArgs Include="-Wl,-s" /> + + <_WasiSdkClangArgs Include="@(_WasiSdkLinkerFlags -> '-Xlinker %(Identity)', ' ')" /> + + <_WasiSdkClangArgs Condition="'@(WasiAfterRuntimeLoadedDeclarations)' != ''" + Include="-D WASI_AFTER_RUNTIME_LOADED_DECLARATIONS="@(WasiAfterRuntimeLoadedDeclarations, ' ')"" /> + <_WasiSdkClangArgs Condition="'@(WasiAfterRuntimeLoadedCalls)' != ''" + Include="-D WASI_AFTER_RUNTIME_LOADED_CALLS="@(WasiAfterRuntimeLoadedCalls, ' ')"" /> + <_WasiSdkClangArgs Include="-o "$(_WasmOutputFileName.Replace('\', '/'))"" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm')) + + + + + + + + + + + + + + + + + + <_AotInputAssemblies Include="@(_WasmAssembliesInternal)"> + @(MonoAOTCompilerDefaultAotArguments, ';') + @(MonoAOTCompilerDefaultProcessArguments, ';') + + + <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> + + <_WasmAOTSearchPaths Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)" /> + <_WasmAOTSearchPaths Include="$(MicrosoftNetCoreAppRuntimePackRidDir)\lib\net8.0" /> + + + + + <_AotInputAssemblies Condition="'%(Filename)' != '' and '@(_AotInputAssemblies->Metadata(`Filename`))' != '' and '@(_AOT_InternalForceInterpretAssemblies->Metadata(`Filename`))' != ''"> + true + + + + + + LLVMOnlyInterp + <_AOTCompilerCacheFile>$(_WasmIntermediateOutputPath)aot_compiler_cache.json + + + + + + + <_WasmDedupAssembly>$(_WasmIntermediateOutputPath)\aot-instances.dll + + + + + <_AotInputAssemblies Include="$(_WasmDedupAssembly)"> + @(MonoAOTCompilerDefaultAotArguments, ';') + @(MonoAOTCompilerDefaultProcessArguments, ';') + + + + + + + + + + + <_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" /> + <_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> + + + <_WasmAssembliesInternal Include="@(_AotInputAssemblies->WithMetadataValue('AOT_InternalForceToInterpret', 'true'))" /> + + + + + + + <_WasmStrippedAssembliesPath>$([MSBuild]::NormalizeDirectory($(_WasmIntermediateOutputPath), 'stripped-assemblies')) + + + + <_AOTedAssemblies Include="@(_WasmAssembliesInternal)" /> + <_WasmStrippedAssemblies + Include="@(_AOTedAssemblies)" + OriginalPath="%(_WasmAssembliesInternal.Identity)" /> + + + + + + + + + <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> + <_WasmAssembliesInternal Include="@(_WasmStrippedAssemblies)" /> + + + diff --git a/src/mono/wasi/build/WasiApp.props b/src/mono/wasi/build/WasiApp.props new file mode 100644 index 0000000000000..9a063a4d5e92f --- /dev/null +++ b/src/mono/wasi/build/WasiApp.props @@ -0,0 +1,28 @@ + + + + <_RuntimeIdentifierUsesAppHost>false + wasm + Browser + wasi-wasm + true + partial + _WasiBuildNativeCore + + <_WasmBuildCoreDependsOn> + _InitializeCommonProperties; + _PrepareForAfterBuild; + _BeforeWasmBuildApp; + $(WasmBuildNativeTarget); + _WasiGenerateAppBundle; + + + $(_WasmBuildCoreDependsOn) + + + + _PrepareForNestedPublish; + $(_WasmBuildCoreDependsOn) + + + diff --git a/src/mono/wasi/build/WasiApp.targets b/src/mono/wasi/build/WasiApp.targets new file mode 100644 index 0000000000000..b6f5d563120cf --- /dev/null +++ b/src/mono/wasi/build/WasiApp.targets @@ -0,0 +1,440 @@ + + + + + + + + false + false + false + + + + false + + <_BeforeWasmBuildAppDependsOn /> + + true + true + true + + Build + + Publish + <_WasmNestedPublishAppPreTarget Condition="'$(DisableAutoWasmPublishApp)' != 'true'">Publish + + true + true + true + false + + + false + true + partial + + + false + + + -1 + + true + true + + true + wasmtime + + + + + + + + + + + + + + <_AppBundleDirForRunCommand Condition="'$(WasmAppDir)' != ''">$(WasmAppDir) + + + <_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' == ''">$([System.IO.Path]::Combine($(OutputPath), $(RuntimeIdentifier), 'AppBundle')) + + + <_AppBundleDirForRunCommand Condition="'$(_AppBundleDirForRunCommand)' != '' and !$([System.IO.Path]::IsPathRooted($(_AppBundleDirForRunCommand)))">$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(_AppBundleDirForRunCommand))) + + + + $(DOTNET_HOST_PATH) + dotnet + + <_RuntimeConfigJsonPath>$([MSBuild]::NormalizePath($(_AppBundleDirForRunCommand), '$(AssemblyName).runtimeconfig.json')) + exec "$([MSBuild]::NormalizePath($(WasmAppHostDir), 'WasmAppHost.dll'))" --runtime-config "$(_RuntimeConfigJsonPath)" $(WasmHostArguments) + $(_AppBundleDirForRunCommand) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_WasmRuntimeConfigFilePath Condition="$([System.String]::new(%(PublishItemsOutputGroupOutputs.Identity)).EndsWith('$(AssemblyName).runtimeconfig.json'))">@(PublishItemsOutputGroupOutputs) + + + + + + + + + <_WasmRuntimeConfigFilePath Condition="$([System.String]::new(%(PublishItemsOutputGroupOutputs.Identity)).EndsWith('$(AssemblyName).runtimeconfig.json'))">@(PublishItemsOutputGroupOutputs) + + + + + + + + + + + + + + + %(ResolvedRuntimePack.PackageDirectory) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackDir), 'runtimes', $(RuntimeIdentifier))) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir))) + $([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidDir), 'native')) + + <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include')) + <_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src')) + + <_WasmIntermediateOutputPath Condition="'$(WasmBuildingForNestedPublish)' == ''">$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm', 'for-build')) + <_WasmIntermediateOutputPath Condition="'$(WasmBuildingForNestedPublish)' != ''">$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm', 'for-publish')) + + <_DriverGenCPath>$(_WasmIntermediateOutputPath)driver-gen.c + <_WasmShouldAOT Condition="'$(WasmBuildingForNestedPublish)' == 'true' and '$(RunAOTCompilation)' == 'true'">true + <_WasmShouldAOT Condition="'$(RunAOTCompilationAfterBuild)' == 'true' and '$(RunAOTCompilation)' == 'true'">true + <_WasmShouldAOT Condition="'$(_WasmShouldAOT)' == ''">false + + + + + + + + + + + + + + + + + + + $([MSBuild]::NormalizeDirectory($(OutputPath), 'AppBundle')) + $(TargetFileName) + <_WasmOutputFileName Condition="'$(WasmSingleFileBundle)' == 'true'">$([System.IO.Path]::GetFileNameWithoutExtension('$(WasmMainAssemblyFileName)')).wasm + <_WasmOutputFileName Condition="'$(WasmSingleFileBundle)' != 'true'">dotnet.wasm + <_WasmOutputFileName>$([System.IO.Path]::Combine($(_WasmIntermediateOutputPath), $(_WasmOutputFileName))) + + $([MSBuild]::NormalizeDirectory($(WasmAppDir))) + + <_MainAssemblyPath Condition="'%(WasmAssembliesToBundle.FileName)' == $(AssemblyName) and '%(WasmAssembliesToBundle.Extension)' == '.dll' and $(WasmGenerateAppBundle) == 'true'">%(WasmAssembliesToBundle.Identity) + <_WasmRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' == '' and $(_MainAssemblyPath) != ''">$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json')) + <_ParsedRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' != ''">$([System.IO.Path]::GetDirectoryName($(_WasmRuntimeConfigFilePath)))\runtimeconfig.bin + + + + + + + + <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> + <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" WasmRole="assembly" /> + + <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" /> + <_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" /> + <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" /> + + <_WasmSatelliteAssemblies CultureName="$([System.IO.Directory]::GetParent('%(Identity)').Name)" WasmRole="SatelliteAssembly" /> + + <_WasmAssembliesInternal Remove="@(_WasmSatelliteAssemblies)" /> + + + + + + <_RuntimeConfigReservedProperties Include="RUNTIME_IDENTIFIER"/> + <_RuntimeConfigReservedProperties Include="APP_CONTEXT_BASE_DIRECTORY"/> + + + + + + + + + + + + + + + icudt.dat + + <_HasDotnetWasm Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.wasm'">true + <_HasDotnetJsWorker Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.worker.js'">true + <_HasDotnetJsSymbols Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js.symbols'">true + <_HasDotnetJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">true + + + + + + + + + + + + + + + + + + + + + + + <_WasmAppIncludeThreadsWorker Condition="'$(WasmEnableThreads)' == 'true' or '$(WasmEnablePerfTracing)' == 'true'">true + + <_WasmPThreadPoolSize Condition="'$(_WasmPThreadPoolSize)' == '' and ('$(WasmEnableThreads)' == 'true' or '$(WasmEnablePerfTracing)' == 'true')">-1 + + + + + + + + + + + + + + + + + + $(WasmAppDir)run-v8.sh + <_WasmMainJSFileName>$([System.IO.Path]::GetFileName('$(WasmMainJSPath)')) + + + + + + + + + + + + + + + + + + + + <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> + <_WasmAssembliesInternal Include="@(_TmpWasmAssemblies)" /> + + + + + + + + + diff --git a/src/mono/wasi/build/WasiSdk.Defaults.props b/src/mono/wasi/build/WasiSdk.Defaults.props new file mode 100644 index 0000000000000..51ce6b50a2eff --- /dev/null +++ b/src/mono/wasi/build/WasiSdk.Defaults.props @@ -0,0 +1,7 @@ + + + $(WASI_SDK_PATH) + $(WasiSdkRoot)\bin\clang + $(WasiClang).exe + + diff --git a/src/mono/wasi/mono-include/driver.h b/src/mono/wasi/mono-include/driver.h index 664680e137c23..5e1f66825f588 100644 --- a/src/mono/wasi/mono-include/driver.h +++ b/src/mono/wasi/mono-include/driver.h @@ -8,7 +8,7 @@ void mono_wasm_load_runtime (const char *unused, int debug_level); int mono_wasm_add_assembly (const char *name, const unsigned char *data, unsigned int size); MonoAssembly* mono_wasm_assembly_load(const char *name); -MonoMethod* mono_wasm_assembly_get_entry_point (MonoAssembly *assembly); +MonoMethod* mono_wasi_assembly_get_entry_point (MonoAssembly *assembly); MonoClass* mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name); MonoMethod* mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments); MonoObject* mono_wasm_invoke_method (MonoMethod *method, MonoObject *this_arg, void *params[], MonoObject **out_exc); diff --git a/src/mono/wasi/provision.ps1 b/src/mono/wasi/provision.ps1 index 3d3602f0a901c..9f8d049fdc72d 100644 --- a/src/mono/wasi/provision.ps1 +++ b/src/mono/wasi/provision.ps1 @@ -11,6 +11,6 @@ param( New-Item -Path $WasiSdkPath -ItemType "directory" Invoke-WebRequest -Uri $WasiSdkUrl -OutFile ./wasi-sdk-$WasiSdkVersion.0-mingw.tar.gz -tar --strip-components=1 -xvzf ./wasi-sdk-$WasiSdkVersion.0-mingw.tar.gz -C $WasiSdkPath +tar --strip-components=1 -xzf ./wasi-sdk-$WasiSdkVersion.0-mingw.tar.gz -C $WasiSdkPath Copy-Item $WasiLocalPath/wasi-sdk-version.txt $WasiSdkPath/wasi-sdk-version.txt Remove-Item ./wasi-sdk-$WasiSdkVersion.0-mingw.tar.gz -fo diff --git a/src/mono/wasi/runtime/driver.c b/src/mono/wasi/runtime/driver.c index f460a450c0bd3..2971e0499476a 100644 --- a/src/mono/wasi/runtime/driver.c +++ b/src/mono/wasi/runtime/driver.c @@ -35,7 +35,8 @@ #ifdef GEN_PINVOKE #include "wasm_m2n_invoke.g.h" #endif -#include "../../wasm/runtime/gc-common.h" +#include "gc-common.h" +#include "driver.h" #if !defined(ENABLE_AOT) || defined(EE_MODE_LLVMONLY_INTERP) @@ -63,6 +64,9 @@ int32_t monoeg_g_hasenv(const char *variable); void mono_free (void*); int32_t mini_parse_debug_option (const char *option); char *mono_method_get_full_name (MonoMethod *method); +extern const char* dotnet_wasi_getbundledfile(const char* name, int* out_length); +extern void dotnet_wasi_registerbundledassemblies(); +extern const char* dotnet_wasi_getentrypointassemblyname(); int mono_wasm_enable_gc = 1; @@ -81,7 +85,10 @@ static MonoDomain *root_domain; static void wasi_trace_logger (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { - printf("[wasi_trace_logger] %s\n", message); + if (strcmp(log_level, "error") == 0) + fprintf(stderr, "[MONO] %s: %s\n", log_level, message); + else + printf("[MONO] %s: %s\n", log_level, message); if (fatal) { // make it trap so we could see the stack trace // (*(int*)(void*)-1)++; @@ -107,6 +114,7 @@ static int assembly_count; int mono_wasm_add_assembly (const char *name, const unsigned char *data, unsigned int size) { + /*printf("wasi: mono_wasm_add_assembly: %s size: %u\n", name, size);*/ int len = strlen (name); if (!strcasecmp (".pdb", &name [len - 4])) { char *new_name = strdup (name); @@ -542,7 +550,7 @@ mono_wasm_invoke_method_ref (MonoMethod *method, MonoObject **this_arg_in, void } MonoMethod* -mono_wasm_assembly_get_entry_point (MonoAssembly *assembly) +mono_wasi_assembly_get_entry_point (MonoAssembly *assembly) { MonoImage *image; MonoMethod *method; @@ -556,43 +564,6 @@ mono_wasm_assembly_get_entry_point (MonoAssembly *assembly) mono_domain_ensure_entry_assembly (root_domain, assembly); method = mono_get_method (image, entry, NULL); - /* - * If the entry point looks like a compiler generated wrapper around - * an async method in the form "" then try to look up the async methods - * "$" and "Name" it could be wrapping. We do this because the generated - * sync wrapper will call task.GetAwaiter().GetResult() when we actually want - * to yield to the host runtime. - */ - if (mono_method_get_flags (method, NULL) & 0x0800 /* METHOD_ATTRIBUTE_SPECIAL_NAME */) { - const char *name = mono_method_get_name (method); - int name_length = strlen (name); - - if ((*name != '<') || (name [name_length - 1] != '>')) - goto end; - - MonoClass *klass = mono_method_get_class (method); - assert(klass); - char *async_name = malloc (name_length + 2); - snprintf (async_name, name_length + 2, "%s$", name); - - // look for "$" - MonoMethodSignature *sig = mono_method_get_signature (method, image, mono_method_get_token (method)); - MonoMethod *async_method = mono_class_get_method_from_name (klass, async_name, mono_signature_get_param_count (sig)); - if (async_method != NULL) { - free (async_name); - method = async_method; - goto end; - } - - // look for "Name" by trimming the first and last character of "" - async_name [name_length - 1] = '\0'; - async_method = mono_class_get_method_from_name (klass, async_name + 1, mono_signature_get_param_count (sig)); - - free (async_name); - if (async_method != NULL) - method = async_method; - } - end: MONO_EXIT_GC_UNSAFE; return method; @@ -698,26 +669,43 @@ MonoMethod* lookup_dotnet_method(const char* assembly_name, const char* namespac return method; } -int main() { +MonoArray* +mono_wasm_string_array_new (int size) +{ + return mono_array_new (root_domain, mono_get_string_class (), size); +} + +#ifdef _WASI_DEFAULT_MAIN +int main(int argc, char * argv[]) { + printf("TODOWASI: default main for non-relinked, non-aot apps, TODO\n"); + if (argc < 2) { + printf("Error: First argument must be the name of the main assembly\n"); + return 1; + } // Assume the runtime pack has been copied into the output directory as 'runtime' // Otherwise we have to mount an unrelated part of the filesystem within the WASM environment - mono_set_assemblies_path(".:./runtime/native:./runtime/lib/net8.0"); + // AJ: not needed right now as we are bundling all the assemblies in the .wasm + mono_set_assemblies_path(".:./runtime/native:./runtime/lib/net7.0"); mono_wasm_load_runtime("", 0); - MonoAssembly* assembly = mono_wasm_assembly_load ("Wasi.Console.Sample"); - MonoMethod* entry_method = mono_wasm_assembly_get_entry_point (assembly); + MonoAssembly* assembly = mono_wasm_assembly_load (argv[1]); + if (!assembly) { + printf("wasi: mono_wasm_assembly_load returned NULL!\n"); + return 1; + } + MonoMethod* entry_method = mono_wasi_assembly_get_entry_point (assembly); + if (!entry_method) { + fprintf(stderr, "Could not find entrypoint in the assembly.\n"); + exit(1); + } + MonoObject* out_exc; MonoObject* out_res; - mono_wasm_invoke_method_ref (entry_method, NULL, NULL, &out_exc, &out_res); - if (out_exc) - { + int ret = mono_runtime_run_main(entry_method, argc, argv, &out_exc); + if (out_exc) { mono_print_unhandled_exception(out_exc); exit(1); } - if(out_res) - { - int r= mono_unbox_int (out_res); - return r; - } - return 0; -} \ No newline at end of file + return ret; +} +#endif diff --git a/src/mono/wasi/runtime/main.c b/src/mono/wasi/runtime/main.c new file mode 100644 index 0000000000000..2cf4c86186728 --- /dev/null +++ b/src/mono/wasi/runtime/main.c @@ -0,0 +1,48 @@ +#include +#include +#include + +// This symbol's implementation is generated during the build +const char* dotnet_wasi_getentrypointassemblyname(); + +// These are generated by EmitWasmBundleObjectFile +const char* dotnet_wasi_getbundledfile(const char* name, int* out_length); +void dotnet_wasi_registerbundledassemblies(); + +#ifdef WASI_AFTER_RUNTIME_LOADED_DECLARATIONS +// This is supplied from the MSBuild itemgroup @(WasiAfterRuntimeLoaded) +WASI_AFTER_RUNTIME_LOADED_DECLARATIONS +#endif + +int main(int argc, char * argv[]) { + // generated during the build + dotnet_wasi_registerbundledassemblies(); + +#ifdef WASI_AFTER_RUNTIME_LOADED_CALLS + // This is supplied from the MSBuild itemgroup @(WasiAfterRuntimeLoaded) + WASI_AFTER_RUNTIME_LOADED_CALLS +#endif + // Assume the runtime pack has been copied into the output directory as 'runtime' + // Otherwise we have to mount an unrelated part of the filesystem within the WASM environment + // AJ: not needed right now as we are bundling all the assemblies in the .wasm + /*mono_set_assemblies_path(".:./runtime/native:./runtime/lib/net7.0");*/ + mono_wasm_load_runtime("", 0); + + const char* assembly_name = dotnet_wasi_getentrypointassemblyname(); + MonoAssembly* assembly = mono_assembly_open(assembly_name, NULL); + MonoMethod* entry_method = mono_wasi_assembly_get_entry_point (assembly); + if (!entry_method) { + fprintf(stderr, "Could not find entrypoint in assembly %s\n", assembly_name); + exit(1); + } + + MonoObject* out_exc; + MonoObject* out_res; + int ret = mono_runtime_run_main(entry_method, argc, argv, &out_exc); + if (out_exc) + { + mono_print_unhandled_exception(out_exc); + exit(1); + } + return ret; +} diff --git a/src/mono/wasi/wasi.proj b/src/mono/wasi/wasi.proj index 92257ee0187e5..a45ee39654e0f 100644 --- a/src/mono/wasi/wasi.proj +++ b/src/mono/wasi/wasi.proj @@ -6,8 +6,9 @@ - $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'wasi-wasm', 'native', 'lib')) - $([MSBuild]::NormalizeDirectory('$(PkgMicrosoft_NETCore_Runtime_ICU_Transport)', 'runtimes', 'wasi-wasm-threads', 'native', 'lib')) + + + false false $(ArtifactsObjDir)wasm @@ -101,8 +102,10 @@ <_WasiCompileFlags Include="-D_WASI_EMULATED_PROCESS_CLOCKS"/> <_WasiCompileFlags Include="-D_WASI_EMULATED_SIGNAL"/> <_WasiCompileFlags Include="-D_WASI_EMULATED_MMAN"/> + <_WasiCompileFlags Include="-D_WASI_DEFAULT_MAIN"/> + - <_WasiLinkFlags Include="-Wl,-z,stack-size=8388608,--initial-memory=52428800,--max-memory=671088640,-lwasi-emulated-process-clocks,-lwasi-emulated-signal,-lwasi-emulated-mman"/> + <_WasiLinkFlags Include="-Wl,-z,stack-size=8388608,--initial-memory=52428800,-lwasi-emulated-process-clocks,-lwasi-emulated-signal,-lwasi-emulated-mman"/> @@ -146,10 +149,10 @@ DependsOnTargets="GenerateWasiPropsAndRspFiles;GenerateManagedToNative;BundleTimeZones"> - - + + + + $(ArtifactsObjDir)wasm/pinvoke-table.h @@ -187,9 +190,21 @@ -v cmake --build . --config $(Configuration) $(CmakeOptions) - + call "$(RepositoryEngineeringDir)native\init-vs-env.cmd" && $(CMakeBuildRuntimeCmd) + + <_FilesToCopy Include="$(MSBuildThisFileDirectory)runtime/driver.c" DestinationFolder="$(NativeBinDir)src" /> + <_FilesToCopy Include="$(MSBuildThisFileDirectory)runtime/main.c" DestinationFolder="$(NativeBinDir)src" /> + <_FilesToCopy Include="$(MSBuildThisFileDirectory)runtime/pinvoke.c" DestinationFolder="$(NativeBinDir)src" /> + <_FilesToCopy Include="$(MSBuildThisFileDirectory)runtime/stubs.c" DestinationFolder="$(NativeBinDir)src" /> + <_FilesToCopy Include="$(MSBuildThisFileDirectory)runtime/synthetic-pthread.c" DestinationFolder="$(NativeBinDir)src" /> + + <_FilesToCopy Include="$(MonoProjectRoot)wasi\mono-include\driver.h" DestinationFolder="$(NativeBinDir)include\wasm" /> + <_FilesToCopy Include="$(MonoProjectRoot)wasi\mono-include\pinvoke.h" DestinationFolder="$(NativeBinDir)include\wasm" /> + <_FilesToCopy Include="$(MonoProjectRoot)wasm\runtime\gc-common.h" DestinationFolder="$(NativeBinDir)include\wasm" /> + + @@ -197,19 +212,10 @@ - - - + > _filesToBundleByObjectFileName = default!; + + [Required] + public ITaskItem[] FilesToBundle { get; set; } = default!; + + [Required] + public string ClangExecutable { get; set; } = default!; + + [Output] + public string? BundleApiSourceCode { get; set; } + + private static byte[] InitLookupTable() + { + // Every 6 bytes in this array represents the output for a different input byte value. + // For example, the input byte 0x1a (26 decimal) corresponds to bytes 156-161 (26*6=156), + // whose values will be ['0', 'x', '1', 'a', ',', ' '], which is the UTF-8 representation + // for "0x1a, ". This is just a faster alternative to calling .ToString("x2") on every + // byte of the input file and then pushing that string through UTF8Encoding. + var lookup = new byte[256 * 6]; + for (int i = 0; i < 256; i++) + { + string byteAsHex = i.ToString("x2"); + char highOrderChar = BitConverter.IsLittleEndian ? byteAsHex[0] : byteAsHex[1]; + char lowOrderChar = BitConverter.IsLittleEndian ? byteAsHex[1] : byteAsHex[0]; + lookup[i * 6 + 0] = (byte)'0'; + lookup[i * 6 + 1] = (byte)'x'; + lookup[i * 6 + 2] = (byte)highOrderChar; + lookup[i * 6 + 3] = (byte)lowOrderChar; + lookup[i * 6 + 4] = (byte)','; + lookup[i * 6 + 5] = (byte)' '; + } + + return lookup; + } + + public override bool Execute() + { + if (!File.Exists(ClangExecutable)) + { + Log.LogError($"Cannot find {nameof(ClangExecutable)}={ClangExecutable}"); + return false; + } + + // The ObjectFile (output filename) already includes a content hash. Grouping by this filename therefore + // produces one group per file-content. We only want to emit one copy of each file-content, and one symbol for it. + _filesToBundleByObjectFileName = FilesToBundle.GroupBy(f => f.GetMetadata("ObjectFile")).ToList(); + + // We're handling the incrementalism within this task, because it needs to be based on file content hashes + // and not on timetamps. The output filenames contain a content hash, so if any such file already exists on + // disk with that name, we know it must be up-to-date. + var remainingObjectFilesToBundle = _filesToBundleByObjectFileName.Where(g => !File.Exists(g.Key)).ToArray(); + + // If you're only touching the leaf project, we don't really need to tell you that. + // But if there's more work to do it's valuable to show progress. + var verbose = remainingObjectFilesToBundle.Length > 1; + var verboseCount = 0; + + if (remainingObjectFilesToBundle.Length > 0) + { + int allowedParallelism = Math.Max(Math.Min(remainingObjectFilesToBundle.Length, Environment.ProcessorCount), 1); + if (BuildEngine is IBuildEngine9 be9) + allowedParallelism = be9.RequestCores(allowedParallelism); + + Parallel.For(0, remainingObjectFilesToBundle.Length, new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism, CancellationToken = BuildTaskCancelled.Token }, i => + { + var objectFile = remainingObjectFilesToBundle[i]; + + // Since the object filenames include a content hash, we can pick an arbitrary ITaskItem from each group, + // since we know each group's ITaskItems all contain the same binary data + var contentSourceFile = objectFile.First(); + + var outputFile = objectFile.Key; + if (verbose) + { + var count = Interlocked.Increment(ref verboseCount); + Log.LogMessage(MessageImportance.High, "{0}/{1} Bundling {2}...", count, remainingObjectFilesToBundle.Length, Path.GetFileName(contentSourceFile.ItemSpec)); + } + + EmitObjectFile(contentSourceFile, outputFile); + }); + } + + BundleApiSourceCode = GetBundleFileApiSource(_filesToBundleByObjectFileName); + + return !Log.HasLoggedErrors; + } + + private void EmitObjectFile(ITaskItem fileToBundle, string destinationObjectFile) + { + Log.LogMessage(MessageImportance.Low, "Bundling {0} as {1}", fileToBundle.ItemSpec, destinationObjectFile); + + if (Path.GetDirectoryName(destinationObjectFile) is string destDir && !string.IsNullOrEmpty(destDir)) + Directory.CreateDirectory(destDir); + + var clangProcess = Process.Start(new ProcessStartInfo + { + FileName = ClangExecutable, + Arguments = $"-xc -o \"{destinationObjectFile}\" -c -", + RedirectStandardInput = true, + UseShellExecute = false, + })!; + + BundleFileToCSource(destinationObjectFile, fileToBundle, clangProcess.StandardInput.BaseStream); + clangProcess.WaitForExit(); + } + + private static string GetBundleFileApiSource(ICollection> bundledFilesByObjectFileName) + { + // Emit an object file that uses all the bundle file symbols and supplies an API + // for getting the bundled file data at runtime + var result = new StringBuilder(); + + result.AppendLine("#include "); + result.AppendLine(); + result.AppendLine("int mono_wasm_add_assembly(const char* name, const unsigned char* data, unsigned int size);"); + result.AppendLine(); + + foreach (var objectFileGroup in bundledFilesByObjectFileName) + { + var symbol = ToSafeSymbolName(objectFileGroup.Key); + result.AppendLine($"extern const unsigned char {symbol}[];"); + result.AppendLine($"extern const int {symbol}_len;"); + } + + result.AppendLine(); + result.AppendLine("const unsigned char* dotnet_wasi_getbundledfile(const char* name, int* out_length) {"); + + // TODO: Instead of a naive O(N) search through all bundled files, consider putting them in a + // hashtable or at least generating a series of comparisons equivalent to a binary search + + foreach (var objectFileGroup in bundledFilesByObjectFileName) + { + foreach (var file in objectFileGroup.Where(f => !string.Equals(f.GetMetadata("WasmRole"), "assembly", StringComparison.OrdinalIgnoreCase))) + { + var symbol = ToSafeSymbolName(objectFileGroup.Key); + result.AppendLine($" if (!strcmp (name, \"{file.ItemSpec.Replace("\\", "/")}\")) {{"); + result.AppendLine($" *out_length = {symbol}_len;"); + result.AppendLine($" return {symbol};"); + result.AppendLine(" }"); + result.AppendLine(); + } + } + + result.AppendLine(" return NULL;"); + result.AppendLine("}"); + + result.AppendLine(); + result.AppendLine("void dotnet_wasi_registerbundledassemblies() {"); + + foreach (var objectFileGroup in bundledFilesByObjectFileName) + { + foreach (var file in objectFileGroup.Where(f => string.Equals(f.GetMetadata("WasmRole"), "assembly", StringComparison.OrdinalIgnoreCase))) + { + var symbol = ToSafeSymbolName(objectFileGroup.Key); + result.AppendLine($" mono_wasm_add_assembly (\"{Path.GetFileName(file.ItemSpec)}\", {symbol}, {symbol}_len);"); + } + } + + result.AppendLine("}"); + + return result.ToString(); + } + + private static void BundleFileToCSource(string objectFileName, ITaskItem fileToBundle, Stream outputStream) + { + // Emits a C source file in the same format as "xxd --include". Example: + // + // unsigned char Some_File_dll[] = { + // 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x0a + // }; + // unsigned int Some_File_dll_len = 6; + + using var inputStream = File.OpenRead(fileToBundle.ItemSpec); + using var outputUtf8Writer = new StreamWriter(outputStream, Utf8NoBom); + + var symbolName = ToSafeSymbolName(objectFileName); + outputUtf8Writer.Write($"unsigned char {symbolName}[] = {{"); + outputUtf8Writer.Flush(); + + var buf = new byte[4096]; + var bytesRead = 0; + var generatedArrayLength = 0; + var bytesEmitted = 0; + while ((bytesRead = inputStream.Read(buf, 0, buf.Length)) > 0) + { + for (var i = 0; i < bytesRead; i++) + { + if (bytesEmitted++ % 12 == 0) + { + outputStream.Write(NewLineAndIndentation, 0, NewLineAndIndentation.Length); + } + + var byteValue = buf[i]; + outputStream.Write(HexToUtf8Lookup, byteValue * 6, 6); + } + + generatedArrayLength += bytesRead; + } + + outputStream.Flush(); + outputUtf8Writer.WriteLine("0\n};"); + outputUtf8Writer.WriteLine($"unsigned int {symbolName}_len = {generatedArrayLength};"); + } + + private static string ToSafeSymbolName(string objectFileName) + { + // Since objectFileName includes a content hash, we can safely strip off the directory name + // as the filename is always unique enough. This avoid disclosing information about the build + // file structure in the resulting symbols. + var filename = Path.GetFileName(objectFileName); + + // Equivalent to the logic from "xxd --include" + var sb = new StringBuilder(); + foreach (var c in filename) + { + sb.Append(IsAlphanumeric(c) ? c : '_'); + } + + return sb.ToString(); + } + + // Equivalent to "isalnum" + private static bool IsAlphanumeric(char c) => c + is (>= 'a' and <= 'z') + or (>= 'A' and <= 'Z') + or (>= '0' and <= '9'); + + public void Cancel() + { + BuildTaskCancelled.Cancel(); + } +} diff --git a/src/tasks/WasmAppBuilder/wasi/WasmResolveAssemblyDependencies.cs b/src/tasks/WasmAppBuilder/wasi/WasmResolveAssemblyDependencies.cs new file mode 100644 index 0000000000000..f0571c8eb3abf --- /dev/null +++ b/src/tasks/WasmAppBuilder/wasi/WasmResolveAssemblyDependencies.cs @@ -0,0 +1,199 @@ +// 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; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using Microsoft.Build.Framework; +using TaskItem = Microsoft.Build.Utilities.TaskItem; + +namespace Microsoft.WebAssembly.Build.Tasks; + +/// +/// Starting from the entrypoint assembly, walks the graph of referenced assemblies using candidates from the +/// runtime pack (first priority) or application assembly list (second priority). This is a way of reducing the +/// number of bundled assemblies to the minimal set, instead of including every possible assembly from the runtime +/// pack and all framework references. +/// +public class WasmResolveAssemblyDependencies : Microsoft.Build.Utilities.Task +{ + [Required] + public string EntryPoint { get; set; } = default!; + + [Required] + public ITaskItem[] ApplicationAssemblies { get; set; } = default!; + + [Required] + public ITaskItem[] WasiRuntimePackAssemblies { get; set; } = default!; + + [Output] + public ITaskItem[]? Dependencies { get; set; } + + public override bool Execute() + { + var paths = ResolveRuntimeDependenciesCore(EntryPoint, ApplicationAssemblies, WasiRuntimePackAssemblies); + Dependencies = paths.Select(p => new TaskItem(p.Path)).ToArray(); + + return true; + } + + private static List ResolveRuntimeDependenciesCore( + string entryPointPath, + IEnumerable applicationAssemblies, + IEnumerable runtimePackAssemblies) + { + var entryAssembly = new AssemblyEntry(entryPointPath, GetAssemblyName(entryPointPath), originalTaskItem: null); + var applicationAssemblyEntries = CreateAssemblyLookup(applicationAssemblies); + var runtimePackAssemblyEntries = CreateAssemblyLookup(runtimePackAssemblies); + + var assemblyResolutionContext = new AssemblyResolutionContext( + entryAssembly, + applicationAssemblyEntries, + runtimePackAssemblyEntries); + assemblyResolutionContext.ResolveAssemblies(); + + return assemblyResolutionContext.Results; + } + + private static Dictionary CreateAssemblyLookup(IEnumerable assemblies) + { + var dictionary = new Dictionary(StringComparer.Ordinal); + foreach (var assembly in assemblies) + { + var assemblyName = GetAssemblyName(assembly.ItemSpec); + if (dictionary.TryGetValue(assemblyName, out var previous)) + { + throw new InvalidOperationException($"Multiple assemblies found with the same assembly name '{assemblyName}':" + + Environment.NewLine + string.Join(Environment.NewLine, previous, assembly.ItemSpec)); + } + dictionary[assemblyName] = new AssemblyEntry(assembly.ItemSpec, assemblyName, assembly); + } + + return dictionary; + } + + private static string GetAssemblyName(string assemblyPath) + { + // It would be more correct to return AssemblyName.GetAssemblyName(assemblyPath).Name, but that involves + // actually loading the assembly file and maybe hitting a BadImageFormatException if it's not actually + // something that can be loaded by the active .NET version (e.g., .NET Framework if this task is running + // inside VS). + // Instead we'll rely on the filename matching the assembly name. + return Path.GetFileNameWithoutExtension(assemblyPath); + } + + private sealed class AssemblyResolutionContext + { + public AssemblyResolutionContext( + AssemblyEntry entryAssembly, + Dictionary applicationAssemblies, + Dictionary runtimePackAssemblies) + { + EntryAssembly = entryAssembly; + ApplicationAssemblies = applicationAssemblies; + RuntimePackAssemblies = runtimePackAssemblies; + } + + public AssemblyEntry EntryAssembly { get; } + public Dictionary ApplicationAssemblies { get; } + public Dictionary RuntimePackAssemblies { get; } + + public List Results { get; } = new(); + + public void ResolveAssemblies() + { + var visitedAssemblies = new HashSet(); + var pendingAssemblies = new Stack(); + pendingAssemblies.Push(EntryAssembly.Name); + ResolveAssembliesCore(); + + void ResolveAssembliesCore() + { + while (pendingAssemblies.Count > 0) + { + var current = pendingAssemblies.Pop(); + if (visitedAssemblies.Add(current)) + { + // Not all references will be resolvable within the runtime pack. + // Skipping unresolved assemblies here is equivalent to passing "--skip-unresolved true" to the .NET linker. + if (Resolve(current) is AssemblyEntry resolved) + { + Results.Add(resolved); + var references = GetAssemblyReferences(resolved.Path); + foreach (var reference in references) + { + pendingAssemblies.Push(reference); + } + } + } + } + } + + AssemblyEntry? Resolve(string assemblyName) + { + if (string.Equals(assemblyName, EntryAssembly.Name, StringComparison.Ordinal)) + { + return EntryAssembly; + } + + // Resolution logic. For right now, we will prefer the runtime pack version of a given + // assembly if there is a candidate assembly and an equivalent runtime pack assembly. + if (RuntimePackAssemblies.TryGetValue(assemblyName, out var assembly) + || ApplicationAssemblies.TryGetValue(assemblyName, out assembly)) + { + return assembly; + } + + return null; + } + + static IReadOnlyList GetAssemblyReferences(string assemblyPath) + { + try + { + using var peReader = new PEReader(File.OpenRead(assemblyPath)); + if (!peReader.HasMetadata) + { + return Array.Empty(); // not a managed assembly + } + + var metadataReader = peReader.GetMetadataReader(); + + var references = new List(); + foreach (var handle in metadataReader.AssemblyReferences) + { + var reference = metadataReader.GetAssemblyReference(handle); + var referenceName = metadataReader.GetString(reference.Name); + + references.Add(referenceName); + } + + return references; + } + catch (BadImageFormatException) + { + // not a PE file, or invalid metadata + } + + return Array.Empty(); // not a managed assembly + } + } + } + + internal readonly struct AssemblyEntry + { + public AssemblyEntry(string path, string name, ITaskItem? originalTaskItem) + { + Path = path; + Name = name; + _originalTaskItem = originalTaskItem; + } + + private readonly ITaskItem? _originalTaskItem; + public string Path { get; } + public string Name { get; } + } +} diff --git a/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs b/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs index ad68f35537b92..d805f4c21a1cf 100644 --- a/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs +++ b/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs @@ -46,7 +46,8 @@ static OutOfProcessTest() !OperatingSystem.IsIOS() && !OperatingSystem.IsTvOS() && !OperatingSystem.IsAndroid() - && !OperatingSystem.IsBrowser(); + && !OperatingSystem.IsBrowser() + && !OperatingSystem.IsOSPlatform("Wasi"); public static void RunOutOfProcessTest(string basePath, string assemblyPath) {