<SQLitePCLRawbundle_greenVersion>2.0.4</SQLitePCLRawbundle_greenVersion>
<MoqVersion>4.12.0</MoqVersion>
<FsCheckVersion>2.14.3</FsCheckVersion>
- <SdkVersionForWorkloadTesting>6.0.100-rc.2.21425.12</SdkVersionForWorkloadTesting>
+ <SdkVersionForWorkloadTesting>6.0.100-rc.2.21463.12</SdkVersionForWorkloadTesting>
<!-- Docs -->
<MicrosoftPrivateIntellisenseVersion>5.0.0-preview-20201009.2</MicrosoftPrivateIntellisenseVersion>
<!-- ILLink -->
-BlazorWasmTests
-FlagsChangeRebuildTest
-InvariantGlobalizationTests
-LocalEMSDKTests
-MainWithArgsTests
-NativeBuildTests
-NativeLibraryTests
-NoopNativeRebuildTest
-RebuildTests
-ReferenceNewAssemblyRebuildTest
-SatelliteAssembliesTests
-SimpleSourceChangeRebuildTest
-WasmBuildAppTest
-WorkloadTests
+Wasm.Build.NativeRebuild.Tests.FlagsChangeRebuildTest
+Wasm.Build.NativeRebuild.Tests.NoopNativeRebuildTest
+Wasm.Build.NativeRebuild.Tests.ReferenceNewAssemblyRebuildTest
+Wasm.Build.NativeRebuild.Tests.SimpleSourceChangeRebuildTest
+Wasm.Build.Tests.BlazorWasmBuildPublishTests
+Wasm.Build.Tests.BlazorWasmTests
+Wasm.Build.Tests.BuildPublishTests
+Wasm.Build.Tests.CleanTests
+Wasm.Build.Tests.InvariantGlobalizationTests
+Wasm.Build.Tests.LocalEMSDKTests
+Wasm.Build.Tests.MainWithArgsTests
+Wasm.Build.Tests.NativeBuildTests
+Wasm.Build.Tests.NativeLibraryTests
+Wasm.Build.Tests.RebuildTests
+Wasm.Build.Tests.SatelliteAssembliesTests
+Wasm.Build.Tests.WasmBuildAppTest
+Wasm.Build.Tests.WasmNativeDefaultsTests
+Wasm.Build.Tests.WorkloadTests
<PublishingTestsRun>true</PublishingTestsRun>
<BundleTestAppTargets>BundleTestAppleApp;BundleTestAndroidApp</BundleTestAppTargets>
+ <PublishTestAsSelfContainedDependsOn>Publish</PublishTestAsSelfContainedDependsOn>
</PropertyGroup>
<PropertyGroup Condition="'$(EnableAggressiveTrimming)' == 'true'">
OutputType="AsmOnly"
Assemblies="@(AotInputAssemblies)"
AotModulesTablePath="$(BundleDir)\modules.c"
+ IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(MonoEnableLLVM)"
LLVMPath="$(MonoAotCrossDir)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
Assemblies="@(AotInputAssemblies)"
AotModulesTablePath="$(BundleDir)\modules.m"
AotModulesTableLanguage="ObjC"
+ IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(MonoEnableLLVM)"
LLVMPath="$(MonoAotCrossDir)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
<Target Name="PublishTestAsSelfContained"
Condition="'$(IsCrossTargetingBuild)' != 'true'"
AfterTargets="Build"
- DependsOnTargets="Publish;$(BundleTestAppTargets);ArchiveTests" />
+ DependsOnTargets="$(PublishTestAsSelfContainedDependsOn);$(BundleTestAppTargets);ArchiveTests" />
<Target Name="PrepareForTestUsingWorkloads"
BeforeTargets="Test"
<Project>
<!-- We need to set this in order to get extensibility on xunit category traits and other arguments we pass down to xunit via MSBuild properties -->
<PropertyGroup>
+ <IsWasmProject Condition="'$(IsWasmProject)' == ''">true</IsWasmProject>
+ <WasmGenerateAppBundle Condition="'$(WasmGenerateAppBundle)' == ''">true</WasmGenerateAppBundle>
<BundleTestAppTargets>$(BundleTestAppTargets);BundleTestWasmApp</BundleTestAppTargets>
<DebuggerSupport Condition="'$(DebuggerSupport)' == '' and '$(Configuration)' == 'Debug'">true</DebuggerSupport>
<!-- Some tests expect to load satellite assemblies by path, eg. System.Runtime.Loader.Tests,
Condition="'$(BuildAOTTestsOn)' == 'local'" />
<PropertyGroup>
- <WasmBuildAppDependsOn>PrepareForWasmBuildApp;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
+ <BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOn)' == 'local'">WasmTriggerPublishApp</BundleTestWasmAppDependsOn>
+ <BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOnHelix)' == 'true'">$(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix</BundleTestWasmAppDependsOn>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(BuildAOTTestsOnHelix)' == 'true'">
+ <!-- wasm targets are not imported at all, in this case, because we run the wasm build on helix -->
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(BuildAOTTestsOnHelix)' != 'true'">
+ <WasmBuildOnlyAfterPublish>true</WasmBuildOnlyAfterPublish>
- <BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOn)' == 'local'">WasmBuildApp</BundleTestWasmAppDependsOn>
- <BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOnHelix)' == 'true'">$(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix</BundleTestWasmAppDependsOn>
+ <!-- wasm's publish targets will trigger publish, so we shouldn't do that -->
+ <PublishTestAsSelfContainedDependsOn />
+ <WasmNestedPublishAppDependsOn>PrepareForWasmBuildApp;$(WasmNestedPublishAppDependsOn)</WasmNestedPublishAppDependsOn>
</PropertyGroup>
<ItemGroup>
<RuntimeConfigFilePath>$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))</RuntimeConfigFilePath>
</PropertyGroup>
+ <Error Text="Item WasmAssembliesToBundle is empty. This is likely an authoring error." Condition="@(WasmAssembliesToBundle->Count()) == 0" />
+
<ItemGroup>
<BundleFiles Include="$(WasmMainJSPath)" TargetDir="publish" />
<BundleFiles Include="@(WasmAssembliesToBundle)" TargetDir="publish\%(WasmAssembliesToBundle.RecursiveDir)" />
</ItemGroup>
<ItemGroup>
- <WasmAssembliesToBundle Include="$(PublishDir)\**\*.dll"/>
+ <WasmAssembliesToBundle Include="$(PublishDir)\**\*.dll" Condition="'$(BuildAOTTestsOnHelix)' == 'true'" />
<WasmFilesToIncludeInFileSystem Include="@(ContentWithTargetPath)" />
<_CopyLocalPaths
</PropertyGroup>
<ItemGroup Condition="'$(Scenario)' == 'BuildWasmApps'">
- <HelixWorkItem Include="@(BuildWasmApps_PerJobList->'$(WorkItemPrefix)%(FileName)')">
+ <HelixWorkItem Include="@(BuildWasmApps_PerJobList->'$(WorkItemPrefix)%(Extension)')">
<PayloadArchive>$(_BuildWasmAppsPayloadArchive)</PayloadArchive>
- <PreCommands Condition="'$(OS)' == 'Windows_NT'">set "HELIX_XUNIT_ARGS=-class Wasm.Build.Tests.%(Identity)"</PreCommands>
- <PreCommands Condition="'$(OS)' != 'Windows_NT'">export "HELIX_XUNIT_ARGS=-class Wasm.Build.Tests.%(Identity)"</PreCommands>
+ <PreCommands Condition="'$(OS)' == 'Windows_NT'">set "HELIX_XUNIT_ARGS=-class %(Identity)"</PreCommands>
+ <PreCommands Condition="'$(OS)' != 'Windows_NT'">export "HELIX_XUNIT_ARGS=-class %(Identity)"</PreCommands>
<Command>$(HelixCommand)</Command>
<Timeout>$(_workItemTimeout)</Timeout>
</HelixWorkItem>
<Error Text="%24(SdkWithWorkloadForTestingPath) is not set" Condition="'$(SdkWithWorkloadForTestingPath)' == ''" />
<Error Text="%24(SdkVersionForWorkloadTesting) is not set" Condition="'$(SdkVersionForWorkloadTesting)' == ''" />
- <Message Text="** Installing sdk $(SdkWithWorkloadForTestingPath) for workload based tests into $(SdkWithWorkloadForTestingPath)" Importance="High" />
+ <Message Text="** Installing sdk $(SdkVersionForWorkloadTesting) for workload based tests into $(SdkWithWorkloadForTestingPath)" Importance="High" />
<RemoveDir Directories="$(SdkWithWorkloadForTestingPath)" />
<MakeDir Directories="$(SdkWithWorkloadForTestingPath)" />
</ItemGroup>
<Copy SourceFiles="@(_SourceFiles)" DestinationFolder="$(SdkWithWorkloadForTestingPath)\%(_SourceFiles.RecursiveDir)" />
+ <Copy SourceFiles="$(MonoProjectRoot)\wasm\BlazorOverwrite.targets" DestinationFiles="$(SdkWithWorkloadForTestingPath)\sdk\$(SdkVersionForWorkloadTesting)\Sdks\Microsoft.NET.Sdk.BlazorWebAssembly\targets\Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets" />
+
<WriteLinesToFile File="$(SdkWithWorkloadStampPath)" Lines="" Overwrite="true" />
</Target>
<Error Text="%24(SdkWithNoWorkloadForTestingPath) is not set" Condition="'$(SdkWithNoWorkloadForTestingPath)' == ''" />
<Error Text="%24(SdkVersionForWorkloadTesting) is not set" Condition="'$(SdkVersionForWorkloadTesting)' == ''" />
- <Message Text="** Installing sdk $(SdkWithNoWorkloadForTestingPath) for workload based tests into $(SdkWithNoWorkloadForTestingPath)" Importance="High" />
+ <Message Text="** Installing sdk $(SdkVersionForWorkloadTesting) for workload based tests into $(SdkWithNoWorkloadForTestingPath)" Importance="High" />
<RemoveDir Directories="$(SdkWithNoWorkloadForTestingPath)" />
<MakeDir Directories="$(SdkWithNoWorkloadForTestingPath)" />
<Exec Condition="$([MSBuild]::IsOSPlatform('windows'))"
Command='powershell -ExecutionPolicy ByPass -NoProfile -command "& $(_DotNetInstallScriptPath) -InstallDir $(SdkWithNoWorkloadForTestingPath) -Version $(SdkVersionForWorkloadTesting)"' />
+ <Copy SourceFiles="$(MonoProjectRoot)\wasm\BlazorOverwrite.targets" DestinationFiles="$(SdkWithNoWorkloadForTestingPath)\sdk\$(SdkVersionForWorkloadTesting)\Sdks\Microsoft.NET.Sdk.BlazorWebAssembly\targets\Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets" />
<WriteLinesToFile File="$(SdkWithNoWorkloadStampPath)" Lines="" Overwrite="true" />
</Target>
AotModulesTablePath="$(_AotModulesTablePath)"
ToolPrefix="$(_AotToolPrefix)"
LibraryFormat="$(_AotLibraryFormat)"
+ IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(UseLLVM)"
LLVMPath="$(MonoAotCrossDir)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
AotModulesTablePath="$(AppDir)\modules.m"
AotModulesTableLanguage="ObjC"
OutputDir="$(PublishDir)"
+ IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(UseLLVM)"
LLVMPath="$(MonoAotCrossDir)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
<Target Name="PrepareDeltasForWasmApp" DependsOnTargets="Build;CompileDiff;ComputeDeltaFileOutputNames">
<ItemGroup>
- <WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
<WasmFilesToIncludeInFileSystem Include="@(_DeltaFileForPublish)">
<TargetPath>\%(_DeltaFileForPublish.Filename)%(_DeltaFileForPublish.Extension)</TargetPath>
</WasmFilesToIncludeInFileSystem>
<Project>
<Import Project="../Directory.Build.targets" />
- <Target Name="PrepareForWasmBuild" BeforeTargets="WasmBuildApp">
- <ItemGroup>
- <WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
- </ItemGroup>
- </Target>
<Import Project="$(MonoProjectRoot)\wasm\build\WasmApp.InTree.targets" />
<Target Name="BuildSampleInTree"
}
return args.Length;
}
-}
\ No newline at end of file
+}
WASM_DEFAULT_BUILD_ARGS?=/p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG)
-all: build
+all: publish
build:
+ EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) build $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) $(MSBUILD_ARGS) $(PROJECT_NAME)
+
+publish:
EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) publish $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) $(MSBUILD_ARGS) $(PROJECT_NAME)
clean:
--- /dev/null
+<!--
+***********************************************************************************************
+Microsoft.NET.Sdk.BlazorWebAssembly.targets
+
+WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
+ created a backup copy. Incorrect changes to this file will make it
+ impossible to load or build your projects from the command-line or the IDE.
+
+Copyright (c) .NET Foundation. All rights reserved.
+***********************************************************************************************
+-->
+<Project ToolsVersion="14.0">
+
+ <PropertyGroup>
+ <EnableDefaultContentItems Condition=" '$(EnableDefaultContentItems)' == '' ">true</EnableDefaultContentItems>
+
+ <!-- Trimmer defaults that depend on user-definable settings.
+ This must be configured before it's initialized in the .NET SDK targets (which are imported by the Razor SDK). -->
+ <SuppressTrimAnalysisWarnings Condition="'$(SuppressTrimAnalysisWarnings)' == '' And '$(TrimmerDefaultAction)' != 'link'">true</SuppressTrimAnalysisWarnings>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!-- Paths to tools, tasks, and extensions are calculated relative to the BlazorWebAssemblySdkDirectoryRoot. This can be modified to test a local build. -->
+ <BlazorWebAssemblySdkDirectoryRoot Condition="'$(BlazorWebAssemblySdkDirectoryRoot)'==''">$(MSBuildThisFileDirectory)..\</BlazorWebAssemblySdkDirectoryRoot>
+ <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net6.0</_BlazorWebAssemblySdkTasksTFM>
+ <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net472</_BlazorWebAssemblySdkTasksTFM>
+ <_BlazorWebAssemblySdkTasksAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\$(_BlazorWebAssemblySdkTasksTFM)\Microsoft.NET.Sdk.BlazorWebAssembly.Tasks.dll</_BlazorWebAssemblySdkTasksAssembly>
+ <_BlazorWebAssemblySdkToolAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\net6.0\Microsoft.NET.Sdk.BlazorWebAssembly.Tool.dll</_BlazorWebAssemblySdkToolAssembly>
+ </PropertyGroup>
+
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.GenerateBlazorWebAssemblyBootJson" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.BlazorWriteSatelliteAssemblyFile" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.BlazorReadSatelliteAssemblyFile" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.BrotliCompress" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.GzipCompress" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.CreateBlazorTrimmerRootDescriptorFile" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.ComputeBlazorFilesToCompress" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.ComputeBlazorBuildAssets" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+ <UsingTask TaskName="Microsoft.NET.Sdk.BlazorWebAssembly.ComputeBlazorPublishAssets" AssemblyFile="$(_BlazorWebAssemblySdkTasksAssembly)" />
+
+ <PropertyGroup>
+ <SelfContained>true</SelfContained>
+ <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
+
+ <!-- Runtime feature defaults to trim unnecessary code -->
+ <InvariantGlobalization Condition="'$(InvariantGlobalization)' == ''">false</InvariantGlobalization>
+ <EventSourceSupport Condition="'$(EventSourceSupport)' == ''">false</EventSourceSupport>
+ <UseSystemResourceKeys Condition="'$(UseSystemResourceKeys)' == ''">true</UseSystemResourceKeys>
+ <EnableUnsafeUTF7Encoding Condition="'$(EnableUnsafeUTF7Encoding)' == ''">false</EnableUnsafeUTF7Encoding>
+ <HttpActivityPropagationSupport Condition="'$(HttpActivityPropagationSupport)' == ''">false</HttpActivityPropagationSupport>
+ <NullabilityInfoContextSupport Condition="'$(NullabilityInfoContextSupport)' == ''">false</NullabilityInfoContextSupport>
+ <_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true</_AggressiveAttributeTrimming>
+ <DebuggerSupport Condition="'$(DebuggerSupport)' == '' and '$(Configuration)' != 'Debug'">false</DebuggerSupport>
+ <BlazorCacheBootResources Condition="'$(BlazorCacheBootResources)' == ''">true</BlazorCacheBootResources>
+
+ <!-- Turn off parts of the build that do not apply to WASM projects -->
+ <GenerateDependencyFile>false</GenerateDependencyFile>
+ <GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
+ <PreserveCompilationContext>false</PreserveCompilationContext>
+ <PreserveCompilationReferences>false</PreserveCompilationReferences>
+ <IsWebConfigTransformDisabled>true</IsWebConfigTransformDisabled>
+
+ <!-- Don't generate a NETSDK1151 error if a non self-contained Exe references a Blazor Exe -->
+ <ShouldBeValidatedAsExecutableReference>false</ShouldBeValidatedAsExecutableReference>
+
+ <_TargetingNET60OrLater Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '6.0'))">true</_TargetingNET60OrLater>
+
+ <!-- JS Modules -->
+ <!-- We disable the manifest generation because we are going to inline the modules in the blazor.boot.json manifest -->
+ <GenerateJSModuleManifest>false</GenerateJSModuleManifest>
+
+ <DisableAutoWasmBuildApp>true</DisableAutoWasmBuildApp>
+ <DisableAutoWasmPublishApp>true</DisableAutoWasmPublishApp>
+ <EnableDefaultWasmAssembliesToBundle>false</EnableDefaultWasmAssembliesToBundle>
+ <WasmNestedPublishAppDependsOn>ComputeFilesToPublish;_GatherWasmFilesToPublish;$(WasmNestedPublishAppDependsOn)</WasmNestedPublishAppDependsOn>
+ <_ScrambleDotnetJsFileNameAfterThisTarget Condition="'$(UsingBrowserRuntimeWorkload)' != 'true'">ResolveRuntimePackAssets</_ScrambleDotnetJsFileNameAfterThisTarget>
+ <_ScrambleDotnetJsFileNameAfterThisTarget Condition="'$(UsingBrowserRuntimeWorkload)' == 'true'">WasmBuildApp</_ScrambleDotnetJsFileNameAfterThisTarget>
+
+ <!-- Workaround https://github.com/dotnet/sdk/issues/20923 where we end up with two publish dirs - 'Publish', and 'publish' -->
+ <PublishDirName Condition="'$(PublishDirName)' == 'publish'">Publish</PublishDirName>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <!-- Configuration for the platform compatibility analyzer. See https://github.com/dotnet/designs/blob/main/accepted/2020/platform-exclusion/platform-exclusion.md#build-configuration-for-platforms -->
+ <SupportedPlatform Remove="@(SupportedPlatform)" />
+ <SupportedPlatform Include="browser" />
+ </ItemGroup>
+
+ <!-- Wire-up static web assets -->
+ <PropertyGroup>
+ <ResolveStaticWebAssetsInputsDependsOn>
+ $(ResolveStaticWebAssetsInputsDependsOn);
+ _AddBlazorWasmStaticWebAssets;
+ </ResolveStaticWebAssetsInputsDependsOn>
+
+ <StaticWebAssetsPrepareForRunDependsOn>
+ _GenerateBuildBlazorBootJson;
+ $(StaticWebAssetsPrepareForRunDependsOn)
+ </StaticWebAssetsPrepareForRunDependsOn>
+
+ <ResolvePublishStaticWebAssetsDependsOn>
+ $(ResolvePublishStaticWebAssetsDependsOn);
+ ProcessPublishFilesForBlazor;
+ ComputeBlazorExtensions;
+ _AddPublishBlazorBootJsonToStaticWebAssets;
+ </ResolvePublishStaticWebAssetsDependsOn>
+
+ <GenerateStaticWebAssetsPublishManifestDependsOn>
+ $(GenerateStaticWebAssetsPublishManifestDependsOn);
+ GeneratePublishBlazorBootJson;
+ </GenerateStaticWebAssetsPublishManifestDependsOn>
+
+ </PropertyGroup>
+
+ <Import Project="Microsoft.NET.Sdk.BlazorWebAssembly.ServiceWorkerAssetsManifest.targets" Condition="'$(ServiceWorkerAssetsManifest)' != ''" />
+
+ <Target Name="_ScrambleDotnetJsFileNameForPublish">
+ <!--
+ We want the dotnet.js file output to have a version to better work with caching. We'll append the runtime version to the file name as soon as file has been discovered.
+ -->
+ <PropertyGroup>
+ <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)</_DotNetJsVersion>
+ <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)</_DotNetJsVersion>
+ <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js</_BlazorDotnetJsFileName>
+ </PropertyGroup>
+
+ <ItemGroup Condition="@(WasmNativeAsset->Count()) != 0">
+ <_DotNetJsItem Remove="@(_DotNetJsItem)" />
+ <_DotNetJsItem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">
+ <RelativePath>_framework/$(_BlazorDotnetJsFileName)</RelativePath>
+ <AssetType>native</AssetType>
+ <CopyLocal>true</CopyLocal>
+ </_DotNetJsItem>
+ </ItemGroup>
+
+ <ItemGroup>
+ <_DotnetJsStaticWebAssetCandidate Remove="@(_DotnetJsStaticWebAssetCandidate)" />
+ <_DotnetJsCopyCandidates Remove="@(_DotnetJsCopyCandidates)" />
+ </ItemGroup>
+
+ <DefineStaticWebAssets Condition="'@(_DotNetJsItem->Count())' != '0'"
+ CandidateAssets="@(_DotNetJsItem)"
+ SourceId="$(PackageId)"
+ SourceType="Computed"
+ AssetKind="Build"
+ AssetRole="Primary"
+ AssetTraitName="BlazorWebAssemblyResource"
+ AssetTraitValue="native"
+ CopyToOutputDirectory="PreserveNewest"
+ CopyToPublishDirectory="Never"
+ ContentRoot="$(IntermediateOutputPath)\blazor"
+ BasePath="$(StaticWebAssetBasePath)"
+ >
+ <Output TaskParameter="Assets" ItemName="_DotnetJsStaticWebAssetCandidate" />
+ <Output TaskParameter="CopyCandidates" ItemName="_DotnetJsCopyCandidates" />
+ </DefineStaticWebAssets>
+
+ <Copy
+ SourceFiles="@(_DotnetJsCopyCandidates)"
+ DestinationFiles="@(_DotnetJsCopyCandidates->'%(TargetPath)')"
+ SkipUnchangedFiles="true"
+ OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" />
+
+ <ItemGroup Condition="'@(_DotNetJsItem->Count())' != '0'">
+ <ReferenceCopyLocalPaths Remove="@(_DotNetJsItem)" />
+ <_DotnetJsStaticWebAsset Include="@(_DotnetJsStaticWebAssetCandidate->'%(ContentRoot)_framework\dotnet.js')" />
+ <_BlazorStaticWebAsset Include="@(_DotnetJsStaticWebAsset)" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="_ScrambleDotnetJsFileName" AfterTargets="$(_ScrambleDotnetJsFileNameAfterThisTarget)">
+ <!--
+ We want the dotnet.js file output to have a version to better work with caching. We'll append the runtime version to the file name as soon as file has been discovered.
+ -->
+ <PropertyGroup>
+ <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)</_DotNetJsVersion>
+ <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)</_DotNetJsVersion>
+ <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js</_BlazorDotnetJsFileName>
+ </PropertyGroup>
+
+ <ItemGroup Condition="@(WasmNativeAsset->Count()) == 0">
+ <_DotNetJsItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.DestinationSubPath)' == 'dotnet.js' AND '%(ReferenceCopyLocalPaths.AssetType)' == 'native'">
+ <RelativePath>_framework/$(_BlazorDotnetJsFileName)</RelativePath>
+ </_DotNetJsItem>
+ </ItemGroup>
+
+ <ItemGroup Condition="@(WasmNativeAsset->Count()) != 0">
+ <_DotNetJsItem Remove="@(_DotNetJsItem)" />
+ <_DotNetJsItem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">
+ <RelativePath>_framework/$(_BlazorDotnetJsFileName)</RelativePath>
+ <AssetType>native</AssetType>
+ <CopyLocal>true</CopyLocal>
+ </_DotNetJsItem>
+ </ItemGroup>
+
+ <DefineStaticWebAssets Condition="'@(_DotNetJsItem->Count())' != '0'"
+ CandidateAssets="@(_DotNetJsItem)"
+ SourceId="$(PackageId)"
+ SourceType="Computed"
+ AssetKind="Build"
+ AssetRole="Primary"
+ AssetTraitName="BlazorWebAssemblyResource"
+ AssetTraitValue="native"
+ CopyToOutputDirectory="PreserveNewest"
+ CopyToPublishDirectory="Never"
+ ContentRoot="$(IntermediateOutputPath)\blazor"
+ BasePath="$(StaticWebAssetBasePath)"
+ >
+ <Output TaskParameter="Assets" ItemName="_DotnetJsStaticWebAssetCandidate" />
+ <Output TaskParameter="CopyCandidates" ItemName="_DotnetJsCopyCandidates" />
+ </DefineStaticWebAssets>
+
+ <Copy
+ SourceFiles="@(_DotnetJsCopyCandidates)"
+ DestinationFiles="@(_DotnetJsCopyCandidates->'%(TargetPath)')"
+ SkipUnchangedFiles="true"
+ OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" />
+
+ <ItemGroup Condition="'@(_DotNetJsItem->Count())' != '0'">
+ <ReferenceCopyLocalPaths Remove="@(_DotNetJsItem)" />
+ <_DotnetJsStaticWebAsset Include="@(_DotnetJsStaticWebAssetCandidate->'%(ContentRoot)_framework\dotnet.js')" />
+ <_BlazorStaticWebAsset Include="@(_DotnetJsStaticWebAsset)" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="_BlazorWasmNativeForBuild" DependsOnTargets="_GatherWasmFilesToBuild;WasmBuildApp" Condition="'$(UsingBrowserRuntimeWorkload)' == 'true'" />
+
+ <Target Name="_GatherWasmFilesToBuild">
+ <ItemGroup>
+ <WasmAssembliesToBundle Remove="@(WasmAssembliesToBundle)" />
+ <WasmAssembliesToBundle Include="@(ReferenceCopyLocalPaths);@(MainAssembly)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'" />
+ <WasmAssembliesToBundle Condition="'%(WasmAssembliesToBundle.FileName)' == 'Microsoft.JSInterop.WebAssembly'" AOT_InternalForceToInterpret="true" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="_ResolveBlazorWasmConfiguration">
+ <PropertyGroup>
+ <_BlazorEnableTimeZoneSupport>$(BlazorEnableTimeZoneSupport)</_BlazorEnableTimeZoneSupport>
+ <_BlazorEnableTimeZoneSupport Condition="'$(_BlazorEnableTimeZoneSupport)' == ''">true</_BlazorEnableTimeZoneSupport>
+ <_BlazorInvariantGlobalization>$(InvariantGlobalization)</_BlazorInvariantGlobalization>
+ <_BlazorInvariantGlobalization Condition="'$(_BlazorInvariantGlobalization)' == ''">true</_BlazorInvariantGlobalization>
+ <_BlazorCopyOutputSymbolsToOutputDirectory>$(CopyOutputSymbolsToOutputDirectory)</_BlazorCopyOutputSymbolsToOutputDirectory>
+ <_BlazorCopyOutputSymbolsToOutputDirectory Condition="'$(_BlazorCopyOutputSymbolsToOutputDirectory)'==''">true</_BlazorCopyOutputSymbolsToOutputDirectory>
+ <_BlazorWebAssemblyLoadAllGlobalizationData>$(BlazorWebAssemblyLoadAllGlobalizationData)</_BlazorWebAssemblyLoadAllGlobalizationData>
+ <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(_BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false</_BlazorWebAssemblyLoadAllGlobalizationData>
+
+ <!-- Workaround for https://github.com/dotnet/sdk/issues/12114-->
+ <PublishDir Condition="'$(AppendRuntimeIdentifierToOutputPath)' != 'true' AND '$(PublishDir)' == '$(OutputPath)$(RuntimeIdentifier)\$(PublishDirName)\'">$(OutputPath)$(PublishDirName)\</PublishDir>
+ </PropertyGroup>
+ </Target>
+
+ <Target Name="_ResolveBlazorWasmOutputs" DependsOnTargets="ResolveReferences;PrepareResourceNames;ComputeIntermediateSatelliteAssemblies;_ResolveBlazorWasmConfiguration;_BlazorWasmNativeForBuild" BeforeTargets="_RazorPrepareForRun">
+ <ItemGroup>
+ <_BlazorJSFile Include="$(BlazorWebAssemblyJSPath)" />
+ <_BlazorJSFile Include="$(BlazorWebAssemblyJSMapPath)" Condition="Exists('$(BlazorWebAssemblyJSMapPath)')" />
+ <_BlazorJsFile>
+ <RelativePath>_framework/%(Filename)%(Extension)</RelativePath>
+ </_BlazorJsFile>
+
+ <!-- A missing blazor.webassembly.js is our packaging error. Produce an error so it's discovered early. -->
+ <Error
+ Text="Unable to find BlazorWebAssembly JS files. This usually indicates a packaging error."
+ Code="RAZORSDK1007"
+ Condition="'@(_BlazorJSFile->Count())' == '0'" />
+
+ <_BlazorConfigFileCandidates Include="@(StaticWebAsset)" Condition="'%(SourceType)' == 'Discovered'" />
+
+ <ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)"
+ Condition="@(WasmNativeAsset->Count()) > 0 and '%(FileName)' == 'dotnet' and ('%(Extension)' == '.wasm' or '%(Extension)' == '.js')" />
+ </ItemGroup>
+
+ <ComputeBlazorBuildAssets
+ Candidates="@(ReferenceCopyLocalPaths);@(WasmNativeAsset)"
+ ProjectAssembly="@(IntermediateAssembly)"
+ ProjectDebugSymbols="@(_DebugSymbolsIntermediatePath)"
+ SatelliteAssemblies="@(ReferenceSatellitePaths)"
+ ProjectSatelliteAssemblies="@(IntermediateSatelliteAssembliesWithTargetPath)"
+ TimeZoneSupport="$(_BlazorEnableTimeZoneSupport)"
+ InvariantGlobalization="$(_BlazorInvariantGlobalization)"
+ CopySymbols="$(_BlazorCopyOutputSymbolsToOutputDirectory)"
+ OutputPath="$(OutputPath)"
+ >
+ <Output TaskParameter="AssetCandidates" ItemName="_BuildAssetsCandidates" />
+ <Output TaskParameter="FilesToRemove" ItemName="_BlazorBuildFilesToRemove" />
+ </ComputeBlazorBuildAssets>
+
+ <DefineStaticWebAssets
+ CandidateAssets="@(_BuildAssetsCandidates)"
+ SourceId="$(PackageId)"
+ SourceType="Computed"
+ AssetKind="Build"
+ AssetRole="Primary"
+ CopyToOutputDirectory="PreserveNewest"
+ CopyToPublishDirectory="Never"
+ ContentRoot="$(OutputPath)wwwroot"
+ BasePath="$(StaticWebAssetBasePath)"
+ >
+ <Output TaskParameter="Assets" ItemName="_BlazorStaticWebAsset" />
+ </DefineStaticWebAssets>
+
+ <DefineStaticWebAssets
+ CandidateAssets="@(_BlazorJSFile)"
+ SourceId="$(PackageId)"
+ SourceType="Computed"
+ AssetKind="All"
+ AssetRole="Primary"
+ AssetTraitName="BlazorWebAssemblyResource"
+ AssetTraitValue="boot"
+ CopyToOutputDirectory="PreserveNewest"
+ CopyToPublishDirectory="PreserveNewest"
+ ContentRoot="$(OutputPath)wwwroot"
+ BasePath="$(StaticWebAssetBasePath)"
+ >
+ <Output TaskParameter="Assets" ItemName="_BlazorStaticWebAsset" />
+ </DefineStaticWebAssets>
+
+ <DefineStaticWebAssets
+ CandidateAssets="@(_BlazorConfigFileCandidates)"
+ AssetTraitName="BlazorWebAssemblyResource"
+ AssetTraitValue="settings"
+ RelativePathFilter="appsettings*.json"
+ >
+ <Output TaskParameter="Assets" ItemName="_BlazorJsConfigStaticWebAsset" />
+ </DefineStaticWebAssets>
+
+ <ItemGroup>
+ <!-- Update the config blazor static web asset since we've given it a trait -->
+ <StaticWebAsset Remove="@(_BlazorJsConfigStaticWebAsset)" />
+ <StaticWebAsset Include="@(_BlazorJsConfigStaticWebAsset)" />
+
+ <ReferenceCopyLocalPaths Remove="@(_BlazorBuildFilesToRemove)" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <_BlazorBuildGZipCompressDirectory>$(IntermediateOutputPath)build-gz\</_BlazorBuildGZipCompressDirectory>
+ </PropertyGroup>
+
+ <!--
+ Compress referenced binaries using GZip during build. This skips files such as the project's assemblies
+ that change from build to build. Runtime assets contribute to the bulk of the download size. Compressing it
+ has the most benefit while avoiding any ongoing costs to the dev inner loop.
+ -->
+
+ <ComputeBlazorFilesToCompress Assets="@(_BlazorStaticWebAsset)">
+ <Output TaskParameter="AssetsToCompress" ItemName="_GzipFileToCompressForBuild" />
+ </ComputeBlazorFilesToCompress>
+
+ <GZipCompress
+ FilesToCompress="@(_GzipFileToCompressForBuild)"
+ OutputDirectory="$(_BlazorBuildGZipCompressDirectory)">
+
+ <Output TaskParameter="CompressedFiles" ItemName="_BlazorBuildGZipCompressedFile" />
+ <Output TaskParameter="CompressedFiles" ItemName="FileWrites" />
+ </GZipCompress>
+
+ <ItemGroup>
+ <_BlazorBuildGZipCompressedFile>
+ <OriginalItemSpec>%(RelatedAsset)</OriginalItemSpec>
+ </_BlazorBuildGZipCompressedFile>
+
+ <_BlazorGzipStaticWebAsset Include="@(_BlazorBuildGZipCompressedFile->'%(FullPath)')" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json</_BlazorBuildBootJsonPath>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <_BuildBlazorBootJson
+ Include="$(_BlazorBuildBootJsonPath)"
+ RelativePath="_framework/blazor.boot.json" />
+ </ItemGroup>
+
+ <DefineStaticWebAssets
+ CandidateAssets="@(_BuildBlazorBootJson)"
+ SourceId="$(PackageId)"
+ SourceType="Computed"
+ AssetKind="Build"
+ AssetRole="Primary"
+ AssetTraitName="BlazorWebAssemblyResource"
+ AssetTraitValue="manifest"
+ CopyToOutputDirectory="PreserveNewest"
+ CopyToPublishDirectory="Never"
+ ContentRoot="$(OutDir)wwwroot"
+ BasePath="$(StaticWebAssetBasePath)"
+ >
+ <Output TaskParameter="Assets" ItemName="_BuildBlazorBootJsonStaticWebAsset" />
+ </DefineStaticWebAssets>
+
+ </Target>
+
+ <Target Name="_AddBlazorWasmStaticWebAssets" DependsOnTargets="_ResolveBlazorWasmOutputs">
+ <ItemGroup>
+ <StaticWebAsset Include="@(_BlazorStaticWebAsset)" />
+ <StaticWebAsset Include="@(_BlazorGzipStaticWebAsset)" />
+ <StaticWebAsset Include="@(_BuildBlazorBootJsonStaticWebAsset)" />
+ </ItemGroup>
+
+ </Target>
+
+ <Target Name="_GenerateBuildBlazorBootJson" DependsOnTargets="ResolveStaticWebAssetsInputs">
+ <PropertyGroup>
+ <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json</_BlazorBuildBootJsonPath>
+ <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false</_BlazorWebAssemblyLoadAllGlobalizationData>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <_BlazorJsModuleCandidatesForBuild
+ Include="@(StaticWebAsset)"
+ Condition="'%(StaticWebAsset.AssetTraitName)' == 'JSModule' and '%(StaticWebAsset.AssetTraitValue)' == 'JSLibraryModule' and '%(AssetKind)' != 'Publish'" />
+ </ItemGroup>
+
+ <GetFileHash Files="@(_BlazorStaticWebAsset->'%(OriginalItemSpec)')" Algorithm="SHA256" HashEncoding="base64">
+ <Output TaskParameter="Items" ItemName="_BlazorOutputWithHash" />
+ </GetFileHash>
+
+ <ComputeStaticWebAssetsTargetPaths
+ Assets="@(_BlazorJsModuleCandidatesForBuild)"
+ PathPrefix=""
+ UseAlternatePathDirectorySeparator="true"
+ >
+ <Output TaskParameter="AssetsWithTargetPath" ItemName="_BlazorJsModuleCandidatesForBuildWithTargetPath" />
+ </ComputeStaticWebAssetsTargetPaths>
+
+ <GetFileHash Files="@(_BlazorJsModuleCandidatesForBuildWithTargetPath)" Algorithm="SHA256" HashEncoding="base64">
+ <Output TaskParameter="Items" ItemName="_BlazorOutputWithHash" />
+ </GetFileHash>
+
+
+ <GenerateBlazorWebAssemblyBootJson
+ AssemblyPath="@(IntermediateAssembly)"
+ Resources="@(_BlazorOutputWithHash)"
+ DebugBuild="true"
+ LinkerEnabled="false"
+ CacheBootResources="$(BlazorCacheBootResources)"
+ OutputPath="$(_BlazorBuildBootJsonPath)"
+ ConfigurationFiles="@(_BlazorJsConfigStaticWebAsset)"
+ LazyLoadedAssemblies="@(BlazorWebAssemblyLazyLoad)"
+ InvariantGlobalization="$(InvariantGlobalization)"
+ LoadAllICUData="$(_BlazorWebAssemblyLoadAllGlobalizationData)" />
+
+ <ItemGroup>
+ <FileWrites Include="$(_BlazorBuildBootJsonPath)" />
+ </ItemGroup>
+
+ </Target>
+
+ <!-- Just print a message here, static web assets takes care of all the copying -->
+ <Target Name="_BlazorCopyFilesToOutputDirectory" AfterTargets="CopyFilesToOutputDirectory">
+ <Message Importance="High" Text="$(MSBuildProjectName) (Blazor output) -> $(TargetDir)wwwroot" Condition="'$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)'!='true'" />
+ </Target>
+
+ <!-- Publish starts here -->
+
+ <!-- Make sure that ResolveAssemblyReferences runs early enough to ensure satellite assemblies are populated in the ResolvedFilesToPublish -->
+ <Target Name="_BlazorPrepareForPublish"
+ DependsOnTargets="PrepareResourceNames;ComputeIntermediateSatelliteAssemblies;ResolveAssemblyReferences"
+ BeforeTargets="PrepareForPublish" />
+
+ <!--
+ This target configures special trimming for Microsoft.Extensions.* and Microsoft.AspNetCore.* assemblies.
+ We only need this for net5.0 projects since trimmablity is declared using assembly attributes in net6.0 and later.
+ -->
+ <Target Name="_BlazorWasmPrepareForLink" BeforeTargets="PrepareForILLink" Condition="'$(_TargetingNET60OrLater)' != 'true'">
+ <PropertyGroup>
+ <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml</_BlazorTypeGranularTrimmerDescriptorFile>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <_BlazorTypeGranularAssembly
+ Include="@(ManagedAssemblyToLink)"
+ Condition="'%(Extension)' == '.dll' AND $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))">
+ <Required>false</Required>
+ <Preserve>all</Preserve>
+ </_BlazorTypeGranularAssembly>
+
+ <ManagedAssemblyToLink
+ IsTrimmable="true"
+ Condition="'%(Extension)' == '.dll' AND ($([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.')) or $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.Extensions.')))" />
+ </ItemGroup>
+
+ <CreateBlazorTrimmerRootDescriptorFile
+ Assemblies="@(_BlazorTypeGranularAssembly)"
+ TrimmerFile="$(_BlazorTypeGranularTrimmerDescriptorFile)" />
+
+ <ItemGroup>
+ <TrimmerRootDescriptor Include="$(_BlazorTypeGranularTrimmerDescriptorFile)" />
+
+ <FileWrites Include="$(_BlazorTypeGranularTrimmerDescriptorFile)" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="ProcessPublishFilesForBlazor" DependsOnTargets="_ResolveBlazorWasmConfiguration;LoadStaticWebAssetsBuildManifest" AfterTargets="ILLink" Condition="'$(WasmBuildingForNestedPublish)' != 'true'">
+ <PropertyGroup>
+ <_BlazorAotEnabled>$(UsingBrowserRuntimeWorkload)</_BlazorAotEnabled>
+ <_BlazorAotEnabled Condition="'$(_BlazorAotEnabled)' == ''">false</_BlazorAotEnabled>
+ <_BlazorLinkerEnabled>$(PublishTrimmed)</_BlazorLinkerEnabled>
+ <_BlazorLinkerEnabled Condition="'$(_BlazorLinkerEnabled)' == ''">true</_BlazorLinkerEnabled>
+ </PropertyGroup>
+
+ <!-- The list of static web assets already contains all the assets from the build. We want to correct certain assets that might
+ have changed as part of the publish process. We are going to do so as follows:
+ * We will update Blazor runtime asset dlls if we are running PublishTrimmed
+ * We will update Blazor native runtime resources if we are using Aot
+ Other than that, we'll filter the unwanted assets from the list of resolved files to publish in the same way we did during the build.
+ -->
+
+ <ItemGroup>
+ <_BlazorPublishPrefilteredAssets
+ Include="@(StaticWebAsset)"
+ Condition="'%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture' or '%(AssetRole)' == 'Alternative'" />
+ </ItemGroup>
+
+ <ComputeBlazorPublishAssets
+ ResolvedFilesToPublish="@(ResolvedFileToPublish)"
+ TimeZoneSupport="$(_BlazorEnableTimeZoneSupport)"
+ PublishPath="$(PublishDir)"
+ WasmAotAssets="@(WasmNativeAsset)"
+ InvariantGlobalization="$(_BlazorInvariantGlobalization)"
+ CopySymbols="$(CopyOutputSymbolsToPublishDirectory)"
+ ExistingAssets="@(_BlazorPublishPrefilteredAssets)"
+ >
+ <Output TaskParameter="NewCandidates" ItemName="_NewBlazorPublishStaticWebAssets" />
+ <Output TaskParameter="FilesToRemove" ItemName="_PublishResolvedFilesToRemove" />
+ </ComputeBlazorPublishAssets>
+
+ <ItemGroup>
+ <ResolvedFileToPublish Remove="@(_PublishResolvedFilesToRemove)" />
+ <StaticWebAsset Include="@(_NewBlazorPublishStaticWebAssets)" />
+ <PublishBlazorBootStaticWebAsset
+ Include="@(StaticWebAsset)"
+ Condition="'%(AssetKind)' != 'Build' and
+ (('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' and '%(StaticWebAsset.AssetTraitValue)' != 'manifest' and '%(StaticWebAsset.AssetTraitValue)' != 'boot') or
+ '%(StaticWebAsset.AssetTraitName)' == 'Culture')" />
+ </ItemGroup>
+ </Target>
+
+ <Target
+ Name="ComputeBlazorExtensions"
+ AfterTargets="ProcessPublishFilesForBlazor"
+ DependsOnTargets="$(ComputeBlazorExtensionsDependsOn)" >
+ <ItemGroup>
+ <_BlazorExtensionsCandidate Include="@(BlazorPublishExtension->'%(FullPath)')">
+ <SourceId>$(PackageId)</SourceId>
+ <SourceType>Computed</SourceType>
+ <ContentRoot>$(PublishDir)wwwroot</ContentRoot>
+ <BasePath>$(StaticWebAssetBasePath)</BasePath>
+ <RelativePath>%(BlazorPublishExtension.RelativePath)</RelativePath>
+ <AssetKind>Publish</AssetKind>
+ <AssetMode>All</AssetMode>
+ <AssetRole>Primary</AssetRole>
+ <AssetTraitName>BlazorWebAssemblyResource</AssetTraitName>
+ <AssetTraitValue>extension:%(BlazorPublishExtension.ExtensionName)</AssetTraitValue>
+ <CopyToOutputDirectory>Never</CopyToOutputDirectory>
+ <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+ <OriginalItemSpec>%(BlazorPublishExtension.Identity)</OriginalItemSpec>
+ </_BlazorExtensionsCandidate>
+ </ItemGroup>
+
+ <DefineStaticWebAssets CandidateAssets="@(_BlazorExtensionsCandidate)">
+ <Output TaskParameter="Assets" ItemName="StaticWebAsset" />
+ <Output TaskParameter="Assets" ItemName="_BlazorExtensionsCandidatesForPublish" />
+ </DefineStaticWebAssets>
+
+ </Target>
+
+ <Target Name="_AddBlazorWebConfigFile" AfterTargets="ILLink">
+ <ItemGroup Condition="'@(ResolvedFileToPublish->AnyHaveMetadataValue('RelativePath', 'web.config'))' != 'true'">
+ <ResolvedFileToPublish
+ Include="$(MSBuildThisFileDirectory)BlazorWasm.web.config"
+ ExcludeFromSingleFile="true"
+ CopyToPublishDirectory="PreserveNewest"
+ RelativePath="web.config" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="_AddPublishBlazorBootJsonToStaticWebAssets">
+ <ItemGroup>
+ <_PublishBlazorBootJson
+ Include="$(IntermediateOutputPath)blazor.publish.boot.json"
+ RelativePath="_framework/blazor.boot.json" />
+ </ItemGroup>
+
+ <DefineStaticWebAssets
+ CandidateAssets="@(_PublishBlazorBootJson)"
+ SourceId="$(PackageId)"
+ SourceType="Computed"
+ AssetKind="Publish"
+ AssetRole="Primary"
+ AssetTraitName="BlazorWebAssemblyResource"
+ AssetTraitValue="manifest"
+ CopyToOutputDirectory="Never"
+ CopyToPublishDirectory="PreserveNewest"
+ ContentRoot="$(PublishDir)wwwroot"
+ BasePath="$(StaticWebAssetBasePath)"
+ >
+ <Output TaskParameter="Assets" ItemName="StaticWebAsset" />
+ </DefineStaticWebAssets>
+ </Target>
+
+ <Target Name="GeneratePublishBlazorBootJson">
+
+ <ItemGroup>
+ <_BlazorPublishAsset
+ Include="@(StaticWebAsset)"
+ Condition="'%(AssetKind)' != 'Build' and '%(StaticWebAsset.AssetTraitValue)' != 'manifest' and ('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture') and '%(StaticWebAsset.AssetTraitValue)' != 'boot'" />
+
+ <_BlazorPublishConfigFile
+ Include="@(StaticWebAsset)"
+ Condition="'%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' and '%(StaticWebAsset.AssetTraitValue)' == 'settings'"/>
+
+ <_BlazorJsModuleCandidatesForPublish
+ Include="@(StaticWebAsset)"
+ Condition="'%(StaticWebAsset.AssetTraitName)' == 'JSModule' and '%(StaticWebAsset.AssetTraitValue)' == 'JSLibraryModule' and '%(AssetKind)' != 'Build'" />
+
+ <!-- We remove the extensions since they are added to the list of static web assets but we need to compute the target path for them -->
+ <_BlazorPublishAsset Remove="@(_BlazorExtensionsCandidatesForPublish)" />
+
+ </ItemGroup>
+
+ <ComputeStaticWebAssetsTargetPaths
+ Assets="@(_BlazorJsModuleCandidatesForPublish);@(_BlazorExtensionsCandidatesForPublish)"
+ PathPrefix=""
+ UseAlternatePathDirectorySeparator="true"
+ >
+ <Output TaskParameter="AssetsWithTargetPath" ItemName="_BlazorCandidatesForPublishWithTargetPath" />
+ </ComputeStaticWebAssetsTargetPaths>
+
+ <GetFileHash Files="@(_BlazorPublishAsset);@(_BlazorCandidatesForPublishWithTargetPath)" Algorithm="SHA256" HashEncoding="base64">
+ <Output TaskParameter="Items" ItemName="_BlazorPublishBootResourceWithHash" />
+ </GetFileHash>
+
+ <GenerateBlazorWebAssemblyBootJson
+ AssemblyPath="@(IntermediateAssembly)"
+ Resources="@(_BlazorPublishBootResourceWithHash)"
+ DebugBuild="false"
+ LinkerEnabled="$(PublishTrimmed)"
+ CacheBootResources="$(BlazorCacheBootResources)"
+ OutputPath="$(IntermediateOutputPath)blazor.publish.boot.json"
+ ConfigurationFiles="@(_BlazorPublishConfigFile)"
+ LazyLoadedAssemblies="@(BlazorWebAssemblyLazyLoad)"
+ InvariantGlobalization="$(InvariantGlobalization)"
+ LoadAllICUData="$(_BlazorWebAssemblyLoadAllGlobalizationData)" />
+
+ <ItemGroup>
+ <FileWrites Include="$(IntermediateOutputPath)blazor.publish.boot.json" />
+ </ItemGroup>
+
+ </Target>
+
+ <Target Name="_BlazorWasmNative"
+ DependsOnTargets="_EnsureWasmRuntimeWorkload;WasmTriggerPublishApp;_ScrambleDotnetJsFileNameForPublish"
+ BeforeTargets="ProcessPublishFilesForBlazor"
+ Condition="'$(UsingBrowserRuntimeWorkload)' == 'true'" />
+
+ <Target Name="_EnsureWasmRuntimeWorkload" Condition="'$(UsingBlazorAOTWorkloadManifest)' != 'true'">
+ <Error
+ Text="Publishing with AOT enabled requires the .NET WebAssembly AOT workload to be installed. To learn more, visit https://aka.ms/AAb4uzl."
+ Code="BLAZORSDK1002" />
+ </Target>
+
+ <Target Name="_GatherWasmFilesToPublish">
+ <ItemGroup>
+ <WasmAssembliesToBundle Remove="@(WasmAssembliesToBundle)" />
+ <WasmAssembliesToBundle Include="%(ResolvedFileToPublish.FullPath)" Exclude="@(_Exclude)" Condition="%(Extension) == '.dll'" />
+ <WasmAssembliesToBundle Condition="'%(WasmAssembliesToBundle.FileName)' == 'Microsoft.JSInterop.WebAssembly'" AOT_InternalForceToInterpret="true" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="_BlazorCompressPublishFiles" AfterTargets="GeneratePublishBlazorBootJson" Condition="'$(BlazorEnableCompression)' != 'false'">
+ <PropertyGroup>
+ <_CompressedFileOutputPath>$(IntermediateOutputPath)compress\</_CompressedFileOutputPath>
+ <_BlazorWebAssemblyBrotliIncremental>true</_BlazorWebAssemblyBrotliIncremental>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(DOTNET_HOST_PATH)' == ''">
+ <_DotNetHostDirectory>$(NetCoreRoot)</_DotNetHostDirectory>
+ <_DotNetHostFileName>dotnet</_DotNetHostFileName>
+ <_DotNetHostFileName Condition="'$(OS)' == 'Windows_NT'">dotnet.exe</_DotNetHostFileName>
+ </PropertyGroup>
+
+ <Message Text="Compressing Blazor WebAssembly publish artifacts. This may take a while..." Importance="High" />
+
+ <MakeDir Directories="$(_CompressedFileOutputPath)" Condition="!Exists('$(_CompressedFileOutputPath)')" />
+
+ <ItemGroup>
+ <_GzipFileToCompressForPublish Include="@(StaticWebAsset)"
+ Condition="'%(AssetKind)' != 'Build' and ('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture')" >
+ <RelatedAsset>%(Identity)</RelatedAsset>
+ <AssetRole>Alternative</AssetRole>
+ <AssetTraitName>Content-Encoding</AssetTraitName>
+ <AssetTraitValue>gzip</AssetTraitValue>
+ </_GzipFileToCompressForPublish>
+
+ <_BrotliFileToCompressForPublish Include="@(_GzipFileToCompressForPublish)" Condition="'%(AssetKind)' != 'Build'">
+ <AssetTraitValue>br</AssetTraitValue>
+ </_BrotliFileToCompressForPublish>
+
+ <!-- We compressed a bunch of assets that were not modified since the build process. We can reuse those and avoid the extra compression we just
+ need to check that they are still relevant (we have updated existing assets to account for linking) -->
+ <_AlreadyGzipCompressedAssets
+ Include="@(StaticWebAsset)"
+ Condition="'%(AssetKind)' != 'Build' and ('%(StaticWebAsset.AssetTraitName)' == 'Content-Encoding' and '%(StaticWebAsset.AssetTraitValue)' == 'gzip')" />
+ <_GzipFileToCompressForPublish Remove="@(_AlreadyGzipCompressedAssets->'%(RelatedAsset)')" />
+ </ItemGroup>
+
+ <GZipCompress
+ FilesToCompress="@(_GzipFileToCompressForPublish)"
+ OutputDirectory="$(_CompressedFileOutputPath)">
+
+ <Output TaskParameter="CompressedFiles" ItemName="_BlazorPublishGZipCompressedFile" />
+ <Output TaskParameter="CompressedFiles" ItemName="FileWrites" />
+ </GZipCompress>
+
+ <BrotliCompress
+ OutputDirectory="$(_CompressedFileOutputPath)"
+ FilesToCompress="@(_BrotliFileToCompressForPublish)"
+ CompressionLevel="$(_BlazorBrotliCompressionLevel)"
+ SkipIfOutputIsNewer="$(_BlazorWebAssemblyBrotliIncremental)"
+ ToolAssembly="$(_BlazorWebAssemblySdkToolAssembly)"
+ ToolExe="$(_DotNetHostFileName)"
+ ToolPath="$(_DotNetHostDirectory)">
+
+ <Output TaskParameter="CompressedFiles" ItemName="_BlazorPublishBrotliCompressedFile" />
+ <Output TaskParameter="CompressedFiles" ItemName="FileWrites" />
+ </BrotliCompress>
+
+ <ItemGroup>
+ <_BlazorPublishGZipCompressedFile>
+ <OriginalItemSpec>%(RelatedAsset)</OriginalItemSpec>
+ </_BlazorPublishGZipCompressedFile>
+ <_BlazorPublishBrotliCompressedFile>
+ <OriginalItemSpec>%(RelatedAsset)</OriginalItemSpec>
+ </_BlazorPublishBrotliCompressedFile>
+
+ <StaticWebAsset Include="@(_BlazorPublishGZipCompressedFile->'%(FullPath)')" />
+ <StaticWebAsset Include="@(_BlazorPublishBrotliCompressedFile->'%(FullPath)')" />
+ </ItemGroup>
+ </Target>
+
+</Project>
+# Wasm app build
+
+This usually consists of taking the built assemblies, and related files, and generating an app bundle.
+
+Wasm app build can run in two scenarios:
+
+1. After build, eg. when running the app in VS, or `dotnet build foo.csproj`
+2. For Publish, eg, when publishing the app to a folder, or Azure
+
+A dotnet wasm app has some native wasm files (`dotnet.wasm`, and `dotnet.js`). How these files are obtained, or generated:
+
+1. Build
+ a. with no native libraries referenced (AOT setting is ignored here)
+ - files from the runtime pack are used as-is
+ b. with native libraries referenced
+ - dotnet.wasm is relinked with the native libraries
+2. Publish
+ - dotnet.wasm is relinked with the native libraries, and updated pinvoke/icalls from the trimmed assemblies
+ - if `RunAOTCompilation=true`, then the relinking includes AOT'ed assemblies
+
+## `Build`
+
+Implementation:
+
+- Target `WasmBuildApp`
+- runs after `Build` by default
+ - which can be disabled by `$(DisableAutoWasmBuildApp)`
+ - or the run-after target can be set via `$(WasmBuildAppAfterThisTarget)`
+
+- To run a custom target
+ - *before* any of the wasm build targets, use `$(WasmBuildAppDependsOn)`, and prepend your target name to that
+ - *after* any of the wasm build targets, use `AfterTargets="WasmBuildApp"` on that target
+- Avoid depending on this target, because it is available only when the workload is installed. Use `$(WasmNativeWorkload)` to check if it is installed.
+
+## `Publish`
+
+Implementation:
+
+- This part runs as a nested build using a `MSBuild` task, which means that the project gets reevaluated. So, if there were any changes made to items/properties in targets before this, then they won't be visible in the nested build.
+- By default `WasmTriggerPublishApp` runs after the `Publish` target, and that triggers the nested build
+ - The nested build runs `WasmNestedPublishApp`, which causes `Build`, and `Publish` targets to be run
+ - Because this causes `Build` to be run again, if you have any targets that get triggered by that, then they will be running twice.
+ - But the original *build* run, and this *publish* run can be differentiated using `$(WasmBuildingForNestedPublish)`
+
+- `WasmTriggerPublishApp` essentially just invokes the nested publish
+ - This runs after `Publish`
+ - which can be disabled by `$(DisableAutoWasmPublishApp)`
+ - or the run-after target can be set via `$(WasmTriggerPublishAppAfterThisTarget)`
+
+ - To influence the wasm build for publish, use `WasmNestedPublishApp`
+ - To run a custom target before it, use `$(WasmNestedPublishAppDependsOn)`
+ - to run a custom target *after* it, use `AfterTargets="WasmNestedPublishApp"`
+
+ - If you want to *dependsOn* on this, then use `DependsOnTargets="WasmTriggerPublishApp"`
+
# `WasmApp.{props,targets}`, and `WasmApp.InTree.{props,targets}`
- Any project that wants to use this, can import the props+targets, and set up the
3. Make changes similar to the one for existing dependent tasks in
- `eng/testing/linker/trimmingTests.targets`,
- `src/tests/Common/wasm-test-runner/WasmTestRunner.proj`
- - `src/tests/Directory.Build.targets`
\ No newline at end of file
+ - `src/tests/Directory.Build.targets`
Condition="'$(_LocalMicrosoftNetCoreAppRuntimePackDir)' != '' and
'%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App'" />
</ItemGroup>
- <Message Text="Used runtime pack: %(ResolvedRuntimePack.PackageDirectory)" Importance="high" />
+ <Message Text="Used runtime pack: %(ResolvedRuntimePack.PackageDirectory) for $(MSBuildProjectName)" Importance="normal" />
</Target>
<Target Name="RebuildWasmAppBuilder">
</Target>
<Target Name="CopyAppZipToHelixTestDir"
- Condition="'$(WasmCopyAppZipToHelixTestDir)' == 'true'"
+ Condition="'$(WasmCopyAppZipToHelixTestDir)' == 'true' and '$(WasmBuildingForNestedPublish)' != 'true'"
AfterTargets="Build"
- DependsOnTargets="Publish">
+ DependsOnTargets="WasmTriggerPublishApp">
<PropertyGroup>
<WasmHelixTestAppRelativeDir Condition="'$(WasmHelixTestAppRelativeDir)' == ''">$(MSBuildProjectName)</WasmHelixTestAppRelativeDir>
<!-- Helix properties -->
<UsingTask TaskName="RuntimeConfigParserTask" AssemblyFile="$(RuntimeConfigParserTasksAssemblyPath)" />
<PropertyGroup>
- <PublishTrimmed>true</PublishTrimmed>
+ <PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
Condition="'$(MicrosoftNetCoreAppRuntimePackLocationToUse)' != '' and
'%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App'" />
</ItemGroup>
- <Message Text="Used runtime pack: %(ResolvedRuntimePack.PackageDirectory)" Importance="high" />
+ <Message Text="Used runtime pack: %(ResolvedRuntimePack.PackageDirectory) for $(MSBuildProjectName)" Importance="normal" />
</Target>
<!-- the actual properties need to get set in the props, so because UsingTasks depend on those. -->
_CompleteWasmBuildNative
</_WasmBuildNativeCoreDependsOn>
- <WasmBuildNativeOnlyDependsOn>
- _InitializeCommonProperties;
- _PrepareForWasmBuildNativeOnly;
- _WasmBuildNativeCore;
- </WasmBuildNativeOnlyDependsOn>
-
<_BeforeWasmBuildAppDependsOn>
$(_BeforeWasmBuildAppDependsOn);
_SetupEmscripten;
<Import Project="$(MSBuildThisFileDirectory)EmSdkRepo.Defaults.props" Condition="'$(WasmUseEMSDK_PATH)' == 'true'" />
- <!-- "public" target meant for use outside the regular wasm app generation process FIXME: rename please! -->
- <Target Name="WasmBuildNativeOnly" DependsOnTargets="$(WasmBuildNativeOnlyDependsOn)" Condition="'$(WasmBuildNative)' == 'true'" />
-
- <Target Name="_PrepareForWasmBuildNativeOnly">
- <ItemGroup>
- <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
- <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" />
- </ItemGroup>
- </Target>
-
<Target Name="_SetupEmscripten">
<PropertyGroup>
<_EMSDKMissingPaths Condition="'$(_EMSDKMissingPaths)' == '' and ('$(EmscriptenSdkToolsPath)' == '' or !Exists('$(EmscriptenSdkToolsPath)'))">%24(EmscriptenSdkToolsPath)=$(EmscriptenSdkToolsPath) </_EMSDKMissingPaths>
<Error Condition="'$(RunAOTCompilation)' == 'true' and '$(_IsEMSDKMissing)' == 'true'"
Text="$(_EMSDKMissingErrorMessage) Emscripten SDK is required for AOT'ing assemblies." />
- <PropertyGroup>
+ <!-- When Building -->
+ <PropertyGroup Condition="'$(WasmBuildingForNestedPublish)' != 'true'">
+ <!-- build AOT, only if explicitly requested -->
+ <WasmBuildNative Condition="'$(RunAOTCompilation)' == 'true' and '$(RunAOTCompilationAfterBuild)' == 'true'">true</WasmBuildNative>
+
+ <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >true</WasmBuildNative>
+ <WasmBuildNative Condition="'$(WasmBuildNative)' == ''">false</WasmBuildNative>
+ </PropertyGroup>
+
+ <!-- When Publishing -->
+ <PropertyGroup Condition="'$(WasmBuildingForNestedPublish)' == 'true'">
+ <!-- AOT==true overrides WasmBuildNative -->
<WasmBuildNative Condition="'$(RunAOTCompilation)' == 'true'">true</WasmBuildNative>
<WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >true</WasmBuildNative>
- <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(PublishTrimmed)' != 'true'" >false</WasmBuildNative>
- <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(Configuration)' == 'Release'" >true</WasmBuildNative>
+
+ <!-- not aot, not trimmed app, no reason to relink -->
+ <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(PublishTrimmed)' != 'true'">false</WasmBuildNative>
+
+ <!-- default to relinking in Release config -->
+ <WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(Configuration)' == 'Release'">true</WasmBuildNative>
+
<WasmBuildNative Condition="'$(WasmBuildNative)' == ''">false</WasmBuildNative>
</PropertyGroup>
<_WasmPInvokeHPath>$(_WasmRuntimePackIncludeDir)wasm\pinvoke.h</_WasmPInvokeHPath>
<_DriverGenCPath>$(_WasmIntermediateOutputPath)driver-gen.c</_DriverGenCPath>
- <_DriverGenCNeeded Condition="'$(_DriverGenCNeeded)' == '' and '$(RunAOTCompilation)' == 'true'">true</_DriverGenCNeeded>
+ <_DriverGenCNeeded Condition="'$(_DriverGenCNeeded)' == '' and '$(_WasmShouldAOT)' == 'true'">true</_DriverGenCNeeded>
<_EmccAssertionLevelDefault>0</_EmccAssertionLevelDefault>
<_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault)</_EmccOptimizationFlagDefault>
<_EmccCFlags Include="$(EmccCompileOptimizationFlag)" />
<_EmccCFlags Include="@(_EmccCommonFlags)" />
- <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" />
- <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" />
+ <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(_WasmShouldAOT)' == 'true'" />
+ <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(_WasmShouldAOT)' == 'true'" />
<_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
<_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCFlags Include="-DCORE_BINDINGS" />
<PInvokeTableGenerator
Modules="@(_WasmPInvokeModules)"
Assemblies="@(_WasmAssembliesInternal)"
- OutputPath="$(_WasmPInvokeTablePath)" />
+ OutputPath="$(_WasmPInvokeTablePath)">
+ <Output TaskParameter="FileWrites" ItemName="FileWrites" />
+ </PInvokeTableGenerator>
</Target>
<Target Name="_GenerateICallTable" Condition="'$(WasmLinkIcalls)' == 'true'">
Text="Could not find AOT cross compiler at %24(_MonoAotCrossCompilerPath)=$(_MonoAotCrossCompilerPath)" />
<Exec Command='"$(_MonoAotCrossCompilerPath)" --print-icall-table > "$(_WasmRuntimeICallTablePath)"' />
+ <ItemGroup>
+ <FileWrites Include="$(_WasmRuntimeICallTablePath)" />
+ </ItemGroup>
+
<IcallTableGenerator
RuntimeIcallTableFile="$(_WasmRuntimeICallTablePath)"
Assemblies="@(_WasmAssembliesInternal)"
- OutputPath="$(_WasmICallTablePath)" />
+ OutputPath="$(_WasmICallTablePath)">
+ <Output TaskParameter="FileWrites" ItemName="FileWrites" />
+ </IcallTableGenerator>
</Target>
<Target Name="_WasmSelectRuntimeComponentsForLinking" Condition="'$(WasmNativeWorkload)' == true" DependsOnTargets="_MonoSelectRuntimeComponents" />
</ItemGroup>
<WriteLinesToFile Lines="@(_EmccCFlags)" File="$(_EmccCompileRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />
+ <ItemGroup>
+ <FileWrites Include="$(_EmccCompileRsp)" />
+ </ItemGroup>
<!-- warm up the cache -->
<Exec Command="$(_EmBuilder) build MINIMAL" EnvironmentVariables="@(EmscriptenEnvVars)" StandardOutputImportance="Low" StandardErrorImportance="Low" />
SourceFiles="@(_WasmSourceFileToCompile)"
Arguments='"@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileRsp)"'
EnvironmentVariables="@(EmscriptenEnvVars)"
- OutputMessageImportance="$(_EmccCompileOutputMessageImportance)" />
+ OutputMessageImportance="$(_EmccCompileOutputMessageImportance)">
+ <Output TaskParameter="OutputFiles" ItemName="FileWrites" />
+ </EmccCompile>
</Target>
<Target Name="_WasmCompileAssemblyBitCodeFilesForAOT"
Inputs="@(_BitcodeFile);$(_EmccDefaultFlagsRsp);$(_EmccCompileBitcodeRsp)"
Outputs="@(_BitcodeFile->'%(ObjectFile)')"
- Condition="'$(RunAOTCompilation)' == 'true' and @(_BitcodeFile->Count()) > 0"
+ Condition="'$(_WasmShouldAOT)' == 'true' and @(_BitcodeFile->Count()) > 0"
DependsOnTargets="_WasmWriteRspForCompilingBitcode">
<ItemGroup>
SourceFiles="@(_BitCodeFile)"
Arguments=""@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileBitcodeRsp)""
EnvironmentVariables="@(EmscriptenEnvVars)"
- OutputMessageImportance="$(_EmccCompileOutputMessageImportance)" />
+ OutputMessageImportance="$(_EmccCompileOutputMessageImportance)">
+ <Output TaskParameter="OutputFiles" ItemName="FileWrites" />
+ </EmccCompile>
</Target>
<Target Name="_WasmWriteRspForCompilingBitcode">
<_BitcodeLDFlags Include="$(EmccExtraBitcodeLDFlags)" />
</ItemGroup>
<WriteLinesToFile Lines="@(_BitcodeLDFlags)" File="$(_EmccCompileBitcodeRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />
+ <ItemGroup>
+ <FileWrites Include="$(_EmccCompileBitcodeRsp)" />
+ </ItemGroup>
</Target>
<Target Name="_WasmWriteRspFilesForLinking">
<_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />
<_WasmNativeFileForLinking
- Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a"
- Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\%(Identity)')" />
+ Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)*.a"
+ Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)%(Identity)')" />
<_WasmExtraJSFile Include="@(Content)" Condition="'%(Content.Extension)' == '.js'" />
</ItemGroup>
<WriteLinesToFile Lines="@(_EmccLinkStepArgs)" File="$(_EmccLinkRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />
+ <ItemGroup>
+ <FileWrites Include="$(_EmccLinkRsp)" />
+ </ItemGroup>
</Target>
<Target Name="_WasmLinkDotNet"
<Message Text="Linking with emcc. This may take a while ..." Importance="High" />
<Message Text="Running emcc with @(_EmccLinkStepArgs->'%(Identity)', ' ')" Importance="Low" />
<Exec Command='emcc "@$(_EmccDefaultFlagsRsp)" "@$(_EmccLinkRsp)"' EnvironmentVariables="@(EmscriptenEnvVars)" />
+ <ItemGroup>
+ <FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.wasm" />
+ <FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.js" />
+ </ItemGroup>
<Message Text="Optimizing dotnet.wasm ..." Importance="High" />
<Exec Command='wasm-opt$(_ExeExt) --strip-dwarf "$(_WasmIntermediateOutputPath)dotnet.wasm" -o "$(_WasmIntermediateOutputPath)dotnet.wasm"' Condition="'$(WasmNativeStrip)' == 'true'" IgnoreStandardErrorWarningFormat="true" EnvironmentVariables="@(EmscriptenEnvVars)" />
</ItemGroup>
</Target>
- <Target Name="_GenerateDriverGenC" Condition="'$(RunAOTCompilation)' != 'true' and '$(WasmProfilers)' != ''">
+ <Target Name="_GenerateDriverGenC" Condition="'$(_WasmShouldAOT)' != 'true' and '$(WasmProfilers)' != ''">
<PropertyGroup>
<EmccExtraCFlags>$(EmccExtraCFlags) -DDRIVER_GEN=1</EmccExtraCFlags>
<_DriverGenCNeeded>true</_DriverGenCNeeded>
*******************************
-->
- <Target Name="_WasmAotCompileApp" Condition="'$(RunAOTCompilation)' == 'true'">
+ <Target Name="_WasmAotCompileApp" Condition="'$(_WasmShouldAOT)' == 'true'">
<PropertyGroup>
<!-- FIXME: do it once -->
<_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm'))</_MonoAotCrossCompilerPath>
<MonoAOTCompilerDefaultAotArguments Include="deterministic" />
</ItemGroup>
<ItemGroup>
- <_AotInputAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'">
+ <_AotInputAssemblies Include="@(_WasmAssembliesInternal)">
<AotArguments>@(MonoAOTCompilerDefaultAotArguments, ';')</AotArguments>
<ProcessArguments>@(MonoAOTCompilerDefaultProcessArguments, ';')</ProcessArguments>
</_AotInputAssemblies>
- <_AOT_InternalForceInterpretAssemblies Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
-
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
<_WasmAOTSearchPaths Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)" />
<_AOTCompilerCacheFile>$(_WasmIntermediateOutputPath)aot_compiler_cache.json</_AOTCompilerCacheFile>
</PropertyGroup>
- <Error Condition="'$(AOTMode)' == 'llvmonly' and @(_AOT_InternalForceInterpretAssemblies->Count()) > 0"
- Text="Builing in AOTMode=LLVMonly, but found some assemblies marked as _InternalForceInterpret: @(_AOT_InternalForceInterpretAssemblies)" />
-
<Message Text="AOT'ing @(_AotInputAssemblies->Count()) assemblies" Importance="High" />
<!-- Dedup -->
<PropertyGroup Condition="'$(WasmDedup)' == 'true'">
<_WasmDedupAssembly>$(_WasmIntermediateOutputPath)\aot-instances.dll</_WasmDedupAssembly>
</PropertyGroup>
- <WriteLinesToFile Condition="'$(WasmDedup)' == 'true'" File="$(_WasmIntermediateOutputPath)/aot-instances.cs" Overwrite="true" Lines="" />
+ <WriteLinesToFile Condition="'$(WasmDedup)' == 'true'" File="$(_WasmIntermediateOutputPath)/aot-instances.cs" Overwrite="true" Lines="" WriteOnlyWhenDifferent="true" />
<Csc
Condition="'$(WasmDedup)' == 'true'"
Sources="$(_WasmIntermediateOutputPath)\aot-instances.cs"
DedupAssembly="$(_WasmDedupAssembly)"
CacheFilePath="$(_AOTCompilerCacheFile)"
LLVMDebug="dwarfdebug"
- LLVMPath="$(EmscriptenUpstreamBinPath)" >
+ LLVMPath="$(EmscriptenUpstreamBinPath)"
+ IntermediateOutputPath="$(_WasmIntermediateOutputPath)">
<Output TaskParameter="CompiledAssemblies" ItemName="_WasmAssembliesInternal" />
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
</MonoAOTCompiler>
<ItemGroup>
- <!-- Add back the interpreter-only assemblies -->
- <_WasmAssembliesInternal Include="@(_AOT_InternalForceInterpretAssemblies)" />
-
- <_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" />
<_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" />
<_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" />
</ItemGroup>
</Target>
<!-- '$(ArchiveTests)' != 'true' is to skip on CI for now -->
- <Target Name="_WasmStripAOTAssemblies" Condition="'$(RunAOTCompilation)' == 'true' and '$(WasmStripAOTAssemblies)' == 'true' and '$(AOTMode)' != 'LLVMOnlyInterp' and '$(ArchiveTests)' != 'true'">
+ <Target Name="_WasmStripAOTAssemblies" Condition="'$(_WasmShouldAOT)' == 'true' and '$(WasmStripAOTAssemblies)' == 'true' and '$(AOTMode)' != 'LLVMOnlyInterp' and '$(ArchiveTests)' != 'true'">
<PropertyGroup>
<_WasmStrippedAssembliesPath>$([MSBuild]::NormalizeDirectory($(_WasmIntermediateOutputPath), 'stripped-assemblies'))</_WasmStrippedAssembliesPath>
</PropertyGroup>
<ItemGroup>
+ <_AOTedAssemblies Include="@(_WasmAssembliesInternal)" />
<_WasmStrippedAssemblies
- Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'"
- Include="@(_WasmAssembliesInternal->'$(_WasmStrippedAssembliesPath)%(FileName)%(Extension)')"
+ Include="@(_AOTedAssemblies)"
OriginalPath="%(_WasmAssembliesInternal.Identity)" />
- <_WasmInterpOnlyAssembly Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
</ItemGroup>
<!-- Run mono-cil-strip on the assemblies -->
<ItemGroup>
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
- <_WasmAssembliesInternal Include="@(_WasmStrippedAssemblies);@(_WasmInterpOnlyAssembly)" />
+ <_WasmAssembliesInternal Include="@(_WasmStrippedAssemblies)" />
</ItemGroup>
</Target>
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<UseMonoRuntime>true</UseMonoRuntime>
- <WasmBuildAppAfterThisTarget Condition="'$(WasmBuildAppAfterThisTarget)' == ''">Publish</WasmBuildAppAfterThisTarget>
- <WasmBuildAppDependsOn>
+ <_WasmBuildCoreDependsOn>
_InitializeCommonProperties;
_BeforeWasmBuildApp;
_WasmResolveReferences;
_WasmBuildNativeCore;
_WasmGenerateAppBundle;
_AfterWasmBuildApp
+ </_WasmBuildCoreDependsOn>
+
+ <WasmBuildAppDependsOn>
+ _PrepareForAfterBuild;
+ $(_WasmBuildCoreDependsOn)
</WasmBuildAppDependsOn>
+
+ <WasmNestedPublishAppDependsOn>
+ _PrepareForNestedPublish;
+ $(_WasmBuildCoreDependsOn)
+ </WasmNestedPublishAppDependsOn>
</PropertyGroup>
</Project>
<!--
Required public items/properties:
- $(WasmMainJSPath)
- - @(WasmAssembliesToBundle) - list of assemblies to package as the wasm app
- - %(_InternalForceInterpret) metadata - if true, then skips this assembly from the AOT step.
- Error for this to be set with AOTMode=LLVMOnly
- $(EMSDK_PATH) - points to the emscripten sdk location.
- $(WasmStripAOTAssemblies) - Whether to run `mono-cil-strip` on the assemblies.
Always set to false!
- - $(WasmBuildAppAfterThisTarget) - This target is used as `AfterTargets` for `WasmBuildApp. this
- is what triggers the wasm app building. Defaults to `Publish`.
-
- $(EmccVerbose) - Set to false to disable verbose emcc output.
- $(EmccLinkOptimizationFlag) - Optimization flag to use for the link step
- $(EmccExtraCFlags) - Extra emcc flags for compiling native files
- $(EmccTotalMemory) - Total memory specified with `emcc`. Default value: 536870912
+ - $(WasmBuildAppAfterThisTarget) - This target is used as `AfterTargets` for `WasmBuildApp. this
+ is what triggers the wasm app building. Defaults to `Build`.
+ - $(WasmTriggerPublishAppAfterThisTarget) - This target is used as `AfterTargets` for `WasmTriggerPublishApp.
+ Defaults to `Publish`.
+
+ - $(EnableDefaultWasmAssembliesToBundle) - Get list of assemblies to bundle automatically. Defaults to true.
+ - $(WasmBuildOnlyAfterPublish) - Causes relinking to be done only for Publish. Defaults to false.
+ - $(RunAOTCompilationAfterBuild) - Run AOT compilation even after Build. By default, it is run only for publish.
+ Defaults to false.
Public items:
- @(WasmExtraFilesToDeploy) - Files to copy to $(WasmAppDir).
<WasmStripAOTAssemblies>false</WasmStripAOTAssemblies>
<_BeforeWasmBuildAppDependsOn />
+
+ <IsWasmProject Condition="'$(IsWasmProject)' == '' and '$(OutputType)' != 'Library'">true</IsWasmProject>
+ <WasmBuildAppAfterThisTarget Condition="'$(WasmBuildAppAfterThisTarget)' == '' and '$(DisableAutoWasmBuildApp)' != 'true'">Build</WasmBuildAppAfterThisTarget>
+
+ <WasmTriggerPublishAppAfterThisTarget Condition="'$(DisableAutoWasmPublishApp)' != 'true' and '$(WasmBuildingForNestedPublish)' != 'true'">Publish</WasmTriggerPublishAppAfterThisTarget>
+ <_WasmNestedPublishAppPreTarget Condition="'$(DisableAutoWasmPublishApp)' != 'true'">Publish</_WasmNestedPublishAppPreTarget>
+
+ <EnableDefaultWasmAssembliesToBundle Condition="'$(EnableDefaultWasmAssembliesToBundle)' == ''">true</EnableDefaultWasmAssembliesToBundle>
+ <WasmBuildOnlyAfterPublish Condition="'$(WasmBuildOnlyAfterPublish)' == '' and '$(DeployOnBuild)' == 'true'">true</WasmBuildOnlyAfterPublish>
</PropertyGroup>
- <Import Project="$(MSBuildThisFileDirectory)WasmApp.Native.targets" />
+ <!-- PUBLISH -->
+
+ <Target Name="WasmTriggerPublishApp"
+ AfterTargets="$(WasmTriggerPublishAppAfterThisTarget)"
+ Condition="'$(IsWasmProject)' == 'true' and '$(WasmBuildingForNestedPublish)' != 'true' and '$(IsCrossTargetingBuild)' != 'true'">
+
+ <!-- Use a unique property, so the already run wasm targets can also run -->
+ <MSBuild Projects="$(MSBuildProjectFile)"
+ Targets="WasmNestedPublishApp"
+ Properties="_WasmInNestedPublish_UniqueProperty_XYZ=true;;WasmBuildingForNestedPublish=true;DeployOnBuild=">
+ <Output TaskParameter="TargetOutputs" ItemName="WasmNestedPublishAppResultItems" />
+ </MSBuild>
+
+ <ItemGroup>
+ <WasmAssembliesFinal Remove="@(WasmAssembliesFinal)" />
+ <WasmAssembliesFinal Include="@(WasmNestedPublishAppResultItems)" Condition="'%(WasmNestedPublishAppResultItems.OriginalItemName)' == 'WasmAssembliesFinal'" />
+
+ <WasmNativeAsset Remove="@(WasmNativeAsset)" />
+ <WasmNativeAsset Include="@(WasmNestedPublishAppResultItems)" Condition="'%(WasmNestedPublishAppResultItems.OriginalItemName)' == 'WasmNativeAsset'" />
+
+ <FileWrites Include="@(WasmNestedPublishAppResultItems)" Condition="'%(WasmNestedPublishAppResultItems.OriginalItemName)' == 'FileWrites'" />
+ </ItemGroup>
+ </Target>
+
+ <!-- Public target. Do not depend on this target, as it is meant to be run by a msbuild task -->
+ <Target Name="WasmNestedPublishApp"
+ DependsOnTargets="ResolveRuntimePackAssets;$(_WasmNestedPublishAppPreTarget);$(WasmNestedPublishAppDependsOn)"
+ Condition="'$(WasmBuildingForNestedPublish)' == 'true'"
+ Returns="@(WasmNativeAsset);@(WasmAssembliesFinal);@(FileWrites)">
+
+ <ItemGroup>
+ <WasmNativeAsset OriginalItemName="WasmNativeAsset" />
+ <WasmAssembliesFinal OriginalItemName="WasmAssembliesFinal" />
+ <FileWrites OriginalItemName="FileWrites" />
+ </ItemGroup>
+ </Target>
+
+ <Target Name="_PrepareForNestedPublish" Condition="'$(WasmBuildingForNestedPublish)' == 'true'">
+ <PropertyGroup>
+ <_WasmRuntimeConfigFilePath Condition="$([System.String]::new(%(PublishItemsOutputGroupOutputs.Identity)).EndsWith('$(AssemblyName).runtimeconfig.json'))">@(PublishItemsOutputGroupOutputs)</_WasmRuntimeConfigFilePath>
+ </PropertyGroup>
+
+ <ItemGroup Condition="'$(EnableDefaultWasmAssembliesToBundle)' == 'true' and '$(DisableAutoWasmPublishApp)' != 'true'">
+ <WasmAssembliesToBundle Remove="@(WasmAssembliesToBundle)" />
+ <WasmAssembliesToBundle Include="$(PublishDir)\**\*.dll" />
+ </ItemGroup>
- <!-- Having this separate target allows users to cleanly add After/BeforeTargets for this -->
- <Target Name="WasmBuildApp" AfterTargets="$(WasmBuildAppAfterThisTarget)" />
+ <PropertyGroup Condition="'$(_WasmRuntimeConfigFilePath)' == ''">
+ <_WasmRuntimeConfigFilePath Condition="$([System.String]::new(%(PublishItemsOutputGroupOutputs.Identity)).EndsWith('$(AssemblyName).runtimeconfig.json'))">@(PublishItemsOutputGroupOutputs)</_WasmRuntimeConfigFilePath>
+ </PropertyGroup>
+ </Target>
+
+ <Import Project="$(MSBuildThisFileDirectory)WasmApp.Native.targets" />
- <Target Name="_WasmCoreBuild" BeforeTargets="WasmBuildApp" DependsOnTargets="$(WasmBuildAppDependsOn)" />
+ <!-- public target for Build -->
+ <Target Name="WasmBuildApp"
+ AfterTargets="$(WasmBuildAppAfterThisTarget)"
+ DependsOnTargets="$(WasmBuildAppDependsOn)"
+ Condition="'$(IsWasmProject)' == 'true' and '$(WasmBuildingForNestedPublish)' == '' and '$(WasmBuildOnlyAfterPublish)' != 'true' and '$(IsCrossTargetingBuild)' != 'true'" />
<Target Name="_InitializeCommonProperties">
<Error Condition="'$(MicrosoftNetCoreAppRuntimePackDir)' == '' and ('%(ResolvedRuntimePack.PackageDirectory)' == '' or !Exists(%(ResolvedRuntimePack.PackageDirectory)))"
<_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include'))</_WasmRuntimePackIncludeDir>
<_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src'))</_WasmRuntimePackSrcDir>
- <_WasmIntermediateOutputPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm'))</_WasmIntermediateOutputPath>
+ <_WasmIntermediateOutputPath Condition="'$(WasmBuildingForNestedPublish)' == ''">$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm', 'for-build'))</_WasmIntermediateOutputPath>
+ <_WasmIntermediateOutputPath Condition="'$(WasmBuildingForNestedPublish)' != ''">$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'wasm', 'for-publish'))</_WasmIntermediateOutputPath>
<_DriverGenCPath>$(_WasmIntermediateOutputPath)driver-gen.c</_DriverGenCPath>
+ <_WasmShouldAOT Condition="'$(WasmBuildingForNestedPublish)' == 'true' and '$(RunAOTCompilation)' == 'true'">true</_WasmShouldAOT>
+ <_WasmShouldAOT Condition="'$(RunAOTCompilationAfterBuild)' == 'true' and '$(RunAOTCompilation)' == 'true'">true</_WasmShouldAOT>
+ <_WasmShouldAOT Condition="'$(_WasmShouldAOT)' == ''">false</_WasmShouldAOT>
</PropertyGroup>
<MakeDir Directories="$(_WasmIntermediateOutputPath)" />
</Target>
+ <Target Name="_PrepareForAfterBuild" Condition="'$(WasmBuildingForNestedPublish)' != 'true'">
+ <ItemGroup Condition="'$(EnableDefaultWasmAssembliesToBundle)' == 'true'">
+ <WasmAssembliesToBundle Include="@(ReferenceCopyLocalPaths);@(MainAssembly)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'" />
+ </ItemGroup>
+ </Target>
+
<Target Name="_BeforeWasmBuildApp" DependsOnTargets="$(_BeforeWasmBuildAppDependsOn)">
<Error Condition="!Exists('$(MicrosoftNetCoreAppRuntimePackRidDir)')" Text="MicrosoftNetCoreAppRuntimePackRidDir=$(MicrosoftNetCoreAppRuntimePackRidDir) doesn't exist" />
<Error Condition="@(WasmAssembliesToBundle->Count()) == 0" Text="WasmAssembliesToBundle item is empty. No assemblies to process" />
<PropertyGroup>
- <WasmGenerateAppBundle Condition="'$(WasmGenerateAppBundle)' == ''">true</WasmGenerateAppBundle>
+ <WasmGenerateAppBundle Condition="'$(WasmGenerateAppBundle)' == '' and '$(OutputType)' != 'Library'">true</WasmGenerateAppBundle>
+ <WasmGenerateAppBundle Condition="'$(WasmGenerateAppBundle)' == ''">false</WasmGenerateAppBundle>
<WasmAppDir Condition="'$(WasmAppDir)' == ''">$([MSBuild]::NormalizeDirectory($(OutputPath), 'AppBundle'))</WasmAppDir>
<WasmMainAssemblyFileName Condition="'$(WasmMainAssemblyFileName)' == ''">$(TargetFileName)</WasmMainAssemblyFileName>
<WasmAppDir>$([MSBuild]::NormalizeDirectory($(WasmAppDir)))</WasmAppDir>
<_MainAssemblyPath Condition="'%(WasmAssembliesToBundle.FileName)' == $(AssemblyName) and '%(WasmAssembliesToBundle.Extension)' == '.dll' and $(WasmGenerateAppBundle) == 'true'">%(WasmAssembliesToBundle.Identity)</_MainAssemblyPath>
- <_WasmRuntimeConfigFilePath Condition="$(_MainAssemblyPath) != ''">$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))</_WasmRuntimeConfigFilePath>
- <_ParsedRuntimeConfigFilePath Condition="'$(_MainAssemblyPath)' != ''">$([System.IO.Path]::GetDirectoryName($(_MainAssemblyPath)))\runtimeconfig.bin</_ParsedRuntimeConfigFilePath>
+ <_WasmRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' == '' and $(_MainAssemblyPath) != ''">$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))</_WasmRuntimeConfigFilePath>
+ <_ParsedRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' != ''">$([System.IO.Path]::GetDirectoryName($(_WasmRuntimeConfigFilePath)))\runtimeconfig.bin</_ParsedRuntimeConfigFilePath>
</PropertyGroup>
- <Warning Condition="'$(WasmGenerateAppBundle)' == 'true' and $(_MainAssemblyPath) == ''" Text="Could not find %24(AssemblyName)=$(AssemblyName) in the assemblies to be bundled.." />
+ <Warning Condition="'$(WasmGenerateAppBundle)' == 'true' and $(_MainAssemblyPath) == ''" Text="Could not find %24(AssemblyName)=$(AssemblyName).dll in the assemblies to be bundled." />
<Warning Condition="'$(WasmGenerateAppBundle)' == 'true' and $(_WasmRuntimeConfigFilePath) != '' and !Exists($(_WasmRuntimeConfigFilePath))"
Text="Could not find $(_WasmRuntimeConfigFilePath) for $(_MainAssemblyPath)." />
<ItemGroup>
+ <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
<_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" />
+ <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" />
<_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" />
<_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" />
<!-- FIXME: Only include the ones with valid culture name -->
</ItemGroup>
</Target>
- <Target Name="_WasmGenerateAppBundle" Condition="'$(WasmGenerateAppBundle)' == 'true'" DependsOnTargets="_WasmGenerateRuntimeConfig">
- <Error Condition="'$(WasmMainJSPath)' == ''" Text="%24(WasmMainJSPath) property needs to be set" />
-
+ <Target Name="_GetWasmGenerateAppBundleDependencies">
<PropertyGroup>
<WasmIcuDataFileName Condition="'$(InvariantGlobalization)' != 'true'">icudt.dat</WasmIcuDataFileName>
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)$(WasmIcuDataFileName)" Condition="'$(InvariantGlobalization)' != 'true'" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.timezones.blat" />
</ItemGroup>
+ </Target>
+ <Target Name="_WasmGenerateAppBundle"
+ Inputs="@(_WasmAssembliesInternal);$(WasmMainJSPath);$(WasmIcuDataFileName);@(WasmNativeAsset)"
+ Outputs="$(WasmAppDir)\.stamp"
+ Condition="'$(WasmGenerateAppBundle)' == 'true'"
+ DependsOnTargets="_WasmGenerateRuntimeConfig;_GetWasmGenerateAppBundleDependencies">
+ <Error Condition="'$(WasmMainJSPath)' == ''" Text="%24(WasmMainJSPath) property needs to be set" />
+
+ <RemoveDir Directories="$(WasmAppDir)" />
<WasmAppBuilder
AppDir="$(WasmAppDir)"
MainJS="$(WasmMainJSPath)"
</WasmAppBuilder>
<CallTarget Targets="_GenerateRunV8Script" Condition="'$(WasmGenerateRunV8Script)' == 'true'" />
+
+ <WriteLinesToFile File="$(WasmAppDir)\.stamp" Lines="" Overwrite="true" />
</Target>
<Target Name="_GenerateRunV8Script">
<TestRootDir Condition="'$(HELIX_WORKITEM_ROOT)' == ''">$(MSBuildThisFileDirectory)..\wasm_build\</TestRootDir>
<RunAOTCompilation Condition="'$(RunAOTCompilation)' == ''">true</RunAOTCompilation>
+ <RunAOTCompilationAfterBuild>true</RunAOTCompilationAfterBuild>
<OriginalPublishDir>$(TestRootDir)..\publish\</OriginalPublishDir>
<ExtraFilesPath>$(OriginalPublishDir)..\extraFiles\</ExtraFilesPath>
<IntermediateOutputPath>$(TestRootDir)\obj\</IntermediateOutputPath>
<PropertyGroup>
<TargetFramework>$(AspNetCoreAppCurrent)</TargetFramework>
<OutputType>Library</OutputType>
+ <IsWasmProject>true</IsWasmProject>
<Configuration>Debug</Configuration>
<RuntimeConfiguration Condition="'$(RuntimeConfiguration)'==''">Release</RuntimeConfiguration>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<RunAnalyzers>false</RunAnalyzers>
<WasmBuildAppDependsOn>PrepareForWasmBuildApp;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
+ <WasmGenerateAppBundle>true</WasmGenerateAppBundle>
</PropertyGroup>
<ItemGroup>
/// </summary>
public string? CacheFilePath { get; set; }
+ [Required]
+ public string IntermediateOutputPath { get; set; } = string.Empty;
+
[Output]
public string[]? FileWrites { get; private set; }
private List<string> _fileWrites = new();
+ private IList<ITaskItem>? _assembliesToCompile;
private ConcurrentDictionary<string, ITaskItem> compiledAssemblies = new();
private MonoAotMode parsedAotMode;
private int _numCompiled;
private int _totalNumAssemblies;
- public override bool Execute()
+ private bool ProcessAndValidateArguments()
{
if (!File.Exists(CompilerBinaryPath))
{
return false;
}
+ if (!Directory.Exists(IntermediateOutputPath))
+ Directory.CreateDirectory(IntermediateOutputPath);
+
if (AotProfilePath != null)
{
foreach (var path in AotProfilePath)
{
if (string.IsNullOrEmpty(LLVMPath))
// prevent using some random llc/opt from PATH (installed with clang)
- throw new ArgumentException($"'{nameof(LLVMPath)}' is required when '{nameof(UseLLVM)}' is true.", nameof(LLVMPath));
+ throw new LogAsErrorException($"'{nameof(LLVMPath)}' is required when '{nameof(UseLLVM)}' is true.");
if (!Directory.Exists(LLVMPath))
{
Log.LogWarning($"'{nameof(OutputType)}=Normal' is deprecated, use 'ObjectFile' instead.");
parsedOutputType = MonoAotOutputType.ObjectFile; break;
default:
- throw new ArgumentException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.ObjectFile)}', '{nameof(MonoAotOutputType.AsmOnly)}', '{nameof(MonoAotOutputType.Library)}'. Received: '{OutputType}'.", nameof(OutputType));
+ throw new LogAsErrorException($"'{nameof(OutputType)}' must be one of: '{nameof(MonoAotOutputType.ObjectFile)}', '{nameof(MonoAotOutputType.AsmOnly)}', '{nameof(MonoAotOutputType.Library)}'. Received: '{OutputType}'.");
}
switch (LibraryFormat)
case "So": parsedLibraryFormat = MonoAotLibraryFormat.So; break;
default:
if (parsedOutputType == MonoAotOutputType.Library)
- throw new ArgumentException($"'{nameof(LibraryFormat)}' must be one of: '{nameof(MonoAotLibraryFormat.Dll)}', '{nameof(MonoAotLibraryFormat.Dylib)}', '{nameof(MonoAotLibraryFormat.So)}'. Received: '{LibraryFormat}'.", nameof(LibraryFormat));
+ throw new LogAsErrorException($"'{nameof(LibraryFormat)}' must be one of: '{nameof(MonoAotLibraryFormat.Dll)}', '{nameof(MonoAotLibraryFormat.Dylib)}', '{nameof(MonoAotLibraryFormat.So)}'. Received: '{LibraryFormat}'.");
break;
}
if (parsedAotMode == MonoAotMode.LLVMOnly && !UseLLVM)
{
- throw new ArgumentException($"'{nameof(UseLLVM)}' must be true when '{nameof(Mode)}' is {nameof(MonoAotMode.LLVMOnly)}.", nameof(UseLLVM));
+ throw new LogAsErrorException($"'{nameof(UseLLVM)}' must be true when '{nameof(Mode)}' is {nameof(MonoAotMode.LLVMOnly)}.");
}
switch (AotModulesTableLanguage)
case "C": parsedAotModulesTableLanguage = MonoAotModulesTableLanguage.C; break;
case "ObjC": parsedAotModulesTableLanguage = MonoAotModulesTableLanguage.ObjC; break;
default:
- throw new ArgumentException($"'{nameof(AotModulesTableLanguage)}' must be one of: '{nameof(MonoAotModulesTableLanguage.C)}', '{nameof(MonoAotModulesTableLanguage.ObjC)}'. Received: '{AotModulesTableLanguage}'.", nameof(AotModulesTableLanguage));
+ throw new LogAsErrorException($"'{nameof(AotModulesTableLanguage)}' must be one of: '{nameof(MonoAotModulesTableLanguage.C)}', '{nameof(MonoAotModulesTableLanguage.ObjC)}'. Received: '{AotModulesTableLanguage}'.");
}
if (!string.IsNullOrEmpty(AotModulesTablePath))
{
// AOT modules for static linking, needs the aot modules table
UseStaticLinking = true;
-
- if (!GenerateAotModulesTable(Assemblies, Profilers, AotModulesTablePath))
- return false;
}
if (UseDirectIcalls && !UseStaticLinking)
{
- throw new ArgumentException($"'{nameof(UseDirectIcalls)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectIcalls));
+ throw new LogAsErrorException($"'{nameof(UseDirectIcalls)}' can only be used with '{nameof(UseStaticLinking)}=true'.");
}
if (UseDirectPInvoke && !UseStaticLinking)
{
- throw new ArgumentException($"'{nameof(UseDirectPInvoke)}' can only be used with '{nameof(UseStaticLinking)}=true'.", nameof(UseDirectPInvoke));
+ throw new LogAsErrorException($"'{nameof(UseDirectPInvoke)}' can only be used with '{nameof(UseStaticLinking)}=true'.");
}
if (UseStaticLinking && (parsedOutputType == MonoAotOutputType.Library))
{
- throw new ArgumentException($"'{nameof(OutputType)}=Library' can not be used with '{nameof(UseStaticLinking)}=true'.", nameof(OutputType));
+ throw new LogAsErrorException($"'{nameof(OutputType)}=Library' can not be used with '{nameof(UseStaticLinking)}=true'.");
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ public override bool Execute()
+ {
+ try
+ {
+ return ExecuteInternal();
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
}
+ }
+
+ private bool ExecuteInternal()
+ {
+ if (!ProcessAndValidateArguments())
+ return false;
+
+ _assembliesToCompile = EnsureAndGetAssembliesInTheSameDir(Assemblies);
+
+ if (!string.IsNullOrEmpty(AotModulesTablePath) && !GenerateAotModulesTable(_assembliesToCompile, Profilers, AotModulesTablePath))
+ return false;
string? monoPaths = null;
if (AdditionalAssemblySearchPaths != null)
//FIXME: check the nothing changed at all case
- _totalNumAssemblies = Assemblies.Length;
- int allowedParallelism = Math.Min(Assemblies.Length, Environment.ProcessorCount);
+ _totalNumAssemblies = _assembliesToCompile.Count;
+ int allowedParallelism = Math.Min(_assembliesToCompile.Count, Environment.ProcessorCount);
if (BuildEngine is IBuildEngine9 be9)
allowedParallelism = be9.RequestCores(allowedParallelism);
if (DisableParallelAot || allowedParallelism == 1)
{
- foreach (var assemblyItem in Assemblies)
+ foreach (var assemblyItem in _assembliesToCompile)
{
if (!PrecompileLibrarySerial(assemblyItem, monoPaths))
return !Log.HasLoggedErrors;
else
{
ParallelLoopResult result = Parallel.ForEach(
- Assemblies,
+ _assembliesToCompile,
new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
(assemblyItem, state) => PrecompileLibraryParallel(assemblyItem, monoPaths, state));
if (!result.IsCompleted)
{
- if (!Log.HasLoggedErrors)
- Log.LogError("Unknown failure occured while compiling");
-
return false;
}
}
if (_cache.Save(CacheFilePath!))
_fileWrites.Add(CacheFilePath!);
- CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, Assemblies).ToArray();
+ CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, _assembliesToCompile).ToArray();
FileWrites = _fileWrites.ToArray();
return !Log.HasLoggedErrors;
}
+ private IList<ITaskItem> EnsureAndGetAssembliesInTheSameDir(ITaskItem[] originalAssemblies)
+ {
+ List<ITaskItem> filteredAssemblies = new();
+ string firstAsmDir = Path.GetDirectoryName(originalAssemblies[0].GetMetadata("FullPath")) ?? string.Empty;
+ bool allInSameDir = true;
+
+ foreach (var origAsm in originalAssemblies)
+ {
+ if (allInSameDir && Path.GetDirectoryName(origAsm.GetMetadata("FullPath")) != firstAsmDir)
+ allInSameDir = false;
+
+ if (ShouldSkip(origAsm))
+ {
+ if (parsedAotMode == MonoAotMode.LLVMOnly)
+ throw new LogAsErrorException($"Building in AOTMode=LLVMonly is not compatible with excluding any assemblies for AOT. Excluded assembly: {origAsm.ItemSpec}");
+
+ Log.LogMessage(MessageImportance.Low, $"Skipping {origAsm.ItemSpec} because it has %(AOT_InternalForceToInterpret)=true");
+ continue;
+ }
+
+ filteredAssemblies.Add(origAsm);
+ }
+
+ if (allInSameDir)
+ return filteredAssemblies;
+
+ // Copy to aot-in
+
+ string aotInPath = Path.Combine(IntermediateOutputPath, "aot-in");
+ Directory.CreateDirectory(aotInPath);
+
+ List<ITaskItem> newAssemblies = new();
+ foreach (var origAsm in originalAssemblies)
+ {
+ string asmPath = origAsm.GetMetadata("FullPath");
+ string newPath = Path.Combine(aotInPath, Path.GetFileName(asmPath));
+
+ // FIXME: delete files not in originalAssemblies though
+ // FIXME: or .. just delete the whole dir?
+ if (Utils.CopyIfDifferent(asmPath, newPath, useHash: true))
+ Log.LogMessage(MessageImportance.Low, $"Copying {asmPath} to {newPath}");
+
+ if (!ShouldSkip(origAsm))
+ {
+ ITaskItem newAsm = new TaskItem(newPath);
+ origAsm.CopyMetadataTo(newAsm);
+ newAssemblies.Add(newAsm);
+ }
+ }
+
+ return newAssemblies;
+
+ static bool ShouldSkip(ITaskItem asmItem)
+ => bool.TryParse(asmItem.GetMetadata("AOT_InternalForceToInterpret"), out bool skip) && skip;
+ }
+
private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
{
- string assembly = assemblyItem.ItemSpec;
+ string assembly = assemblyItem.GetMetadata("FullPath");
string assemblyDir = Path.GetDirectoryName(assembly)!;
var aotAssembly = new TaskItem(assembly);
var aotArgs = new List<string>();
// values, which wont work.
processArgs.Add($"\"--aot={string.Join(",", aotArgs)}\"");
- string paths = "";
if (isDedup)
{
- StringBuilder sb = new StringBuilder();
- HashSet<string> allPaths = new HashSet<string>();
- foreach (var aItem in Assemblies)
- {
- string filename = aItem.ItemSpec;
- processArgs.Add(filename);
- string dir = Path.GetDirectoryName(filename)!;
- if (!allPaths.Contains(dir))
- {
- allPaths.Add(dir);
- if (sb.Length > 0)
- sb.Append(Path.PathSeparator);
- sb.Append(dir);
- }
- }
- if (sb.Length > 0)
- sb.Append(Path.PathSeparator);
- sb.Append(monoPaths);
- paths = sb.ToString();
+ foreach (var aItem in _assembliesToCompile!)
+ processArgs.Add(aItem.ItemSpec);
}
else
{
- paths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}";
processArgs.Add('"' + assemblyFilename + '"');
}
+ monoPaths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}";
var envVariables = new Dictionary<string, string>
{
- {"MONO_PATH", paths},
+ {"MONO_PATH", monoPaths },
{"MONO_ENV_OPTIONS", string.Empty} // we do not want options to be provided out of band to the cross compilers
};
state.Break();
}
- private bool GenerateAotModulesTable(ITaskItem[] assemblies, string[]? profilers, string outputFile)
+ private bool GenerateAotModulesTable(IEnumerable<ITaskItem> assemblies, string[]? profilers, string outputFile)
{
var symbols = new List<string>();
foreach (var asm in assemblies)
}
}
- private IList<ITaskItem> ConvertAssembliesDictToOrderedList(ConcurrentDictionary<string, ITaskItem> dict, ITaskItem[] items)
+ private static IList<ITaskItem> ConvertAssembliesDictToOrderedList(ConcurrentDictionary<string, ITaskItem> dict, IList<ITaskItem> originalAssemblies)
{
- List<ITaskItem> outItems = new(items.Length);
- foreach (ITaskItem item in items)
+ List<ITaskItem> outItems = new(originalAssemblies.Count);
+ foreach (ITaskItem item in originalAssemblies)
{
- if (!dict.TryGetValue(item.ItemSpec, out ITaskItem? dictItem))
+ if (!dict.TryGetValue(item.GetMetadata("FullPath"), out ITaskItem? dictItem))
throw new LogAsErrorException($"Bug: Could not find item in the dict with key {item.ItemSpec}");
outItems.Add(dictItem);
if (!_cache.Enabled)
return true;
- if (!File.Exists(TempFile))
- throw new LogAsErrorException($"Could not find output file {TempFile}");
-
- if (!_cache.ShouldCopy(this, out string? cause))
+ try
{
- _cache.Log.LogMessage(MessageImportance.Low, $"Skipping copying over {TargetFile} as the contents are unchanged");
- return false;
- }
+ if (!_cache.ShouldCopy(this, out string? cause))
+ {
+ _cache.Log.LogMessage(MessageImportance.Low, $"Skipping copying over {TargetFile} as the contents are unchanged");
+ return false;
+ }
- if (File.Exists(TargetFile))
- File.Delete(TargetFile);
+ if (File.Exists(TargetFile))
+ File.Delete(TargetFile);
- File.Copy(TempFile, TargetFile);
- File.Delete(TempFile);
+ File.Copy(TempFile, TargetFile);
- _cache.Log.LogMessage(MessageImportance.Low, $"Copying {TempFile} to {TargetFile} because {cause}");
- return true;
+ _cache.Log.LogMessage(MessageImportance.Low, $"Copying {TempFile} to {TargetFile} because {cause}");
+ return true;
+ }
+ finally
+ {
+ File.Delete(TempFile);
+ }
}
}
throw new ArgumentException($"Cannot find {src} file to copy", nameof(src));
bool areDifferent = !File.Exists(dst) ||
- (useHash && Utils.ComputeHash(src) != Utils.ComputeHash(dst)) ||
+ (useHash && ComputeHash(src) != ComputeHash(dst)) ||
(File.ReadAllText(src) != File.ReadAllText(dst));
if (areDifferent)
public override bool Execute()
{
+ try
+ {
+ return ExecuteActual();
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
+ }
+ }
+
+ private bool ExecuteActual()
+ {
if (SourceFiles.Length == 0)
{
Log.LogError($"No SourceFiles to compile");
});
if (!result.IsCompleted && !Log.HasLoggedErrors)
- Log.LogError("Unknown failed occured while compiling");
+ Log.LogError("Unknown failure occured while compiling. Check logs to get more details.");
}
if (!Log.HasLoggedErrors)
private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, out string reason)
{
if (!File.Exists(srcFile))
- throw new ArgumentException($"Could not find source file {srcFile}");
+ throw new LogAsErrorException($"Could not find source file {srcFile}");
if (!File.Exists(objFile))
{
return true;
}
- reason = "everything is up-to-date.";
+ reason = "everything is up-to-date";
return false;
bool IsNewerThanOutput(string inFile, string outFile, out string reason)
[Required, NotNull]
public string? OutputPath { get; set; }
+ [Output]
+ public string? FileWrites { get; private set; } = "";
+
private List<Icall> _icalls = new List<Icall> ();
private Dictionary<string, IcallClass> _runtimeIcalls = new Dictionary<string, IcallClass> ();
Log.LogMessage(MessageImportance.Low, $"Generating icall table to '{OutputPath}'.");
else
Log.LogMessage(MessageImportance.Low, $"Icall table in {OutputPath} is unchanged.");
+ FileWrites = OutputPath;
File.Delete(tmpFileName);
}
public class PInvokeTableGenerator : Task
{
- [Required]
- public ITaskItem[]? Modules { get; set; }
- [Required]
- public ITaskItem[]? Assemblies { get; set; }
+ [Required, NotNull]
+ public string[]? Modules { get; set; }
+ [Required, NotNull]
+ public string[]? Assemblies { get; set; }
[Required, NotNull]
public string? OutputPath { get; set; }
+ [Output]
+ public string FileWrites { get; private set; } = string.Empty;
+
private static char[] s_charsToReplace = new[] { '.', '-', };
public override bool Execute()
{
- GenPInvokeTable(Modules!.Select(item => item.ItemSpec).ToArray(), Assemblies!.Select(item => item.ItemSpec).ToArray());
- return true;
+ if (Assemblies.Length == 0)
+ {
+ Log.LogError($"No assemblies given to scan for pinvokes");
+ return false;
+ }
+
+ if (Modules.Length == 0)
+ {
+ Log.LogError($"{nameof(PInvokeTableGenerator)}.{nameof(Modules)} cannot be empty");
+ return false;
+ }
+
+ try
+ {
+ GenPInvokeTable(Modules, Assemblies);
+ return !Log.HasLoggedErrors;
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
+ }
}
public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies)
Log.LogMessage(MessageImportance.Low, $"Generating pinvoke table to '{OutputPath}'.");
else
Log.LogMessage(MessageImportance.Low, $"PInvoke table in {OutputPath} is unchanged.");
+ FileWrites = OutputPath;
File.Delete(tmpFileName);
}
return false;
}
- private static void Error (string msg)
- {
- // FIXME:
- throw new Exception(msg);
- }
+ private static void Error (string msg) => throw new LogAsErrorException(msg);
}
internal class PInvoke
public override bool Execute ()
{
+ try
+ {
+ return ExecuteInternal();
+ }
+ catch (LogAsErrorException laee)
+ {
+ Log.LogError(laee.Message);
+ return false;
+ }
+ }
+
+ private bool ExecuteInternal ()
+ {
if (!File.Exists(MainJS))
- throw new ArgumentException($"File MainJS='{MainJS}' doesn't exist.");
+ throw new LogAsErrorException($"File MainJS='{MainJS}' doesn't exist.");
if (!InvariantGlobalization && string.IsNullOrEmpty(IcuDataFileName))
- throw new ArgumentException("IcuDataFileName property shouldn't be empty if InvariantGlobalization=false");
+ throw new LogAsErrorException("IcuDataFileName property shouldn't be empty if InvariantGlobalization=false");
if (Assemblies?.Length == 0)
{
}
FileCopyChecked(MainJS!, Path.Combine(AppDir, "runtime.js"), string.Empty);
- var html = @"<html><body><script type=""text/javascript"" src=""runtime.js""></script></body></html>";
- File.WriteAllText(Path.Combine(AppDir, "index.html"), html);
+ string indexHtmlPath = Path.Combine(AppDir, "index.html");
+ if (!File.Exists(indexHtmlPath))
+ {
+ var html = @"<html><body><script type=""text/javascript"" src=""runtime.js""></script></body></html>";
+ File.WriteAllText(indexHtmlPath, html);
+ }
foreach (var assembly in _assemblies)
{
<ItemGroup>
<Compile Include="..\Common\Utils.cs" />
+ <Compile Include="..\Common\LogAsErrorException.cs" />
<PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" />
<PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Linq;
+using Xunit;
+using Xunit.Abstractions;
+
+#nullable enable
+
+namespace Wasm.Build.Tests
+{
+ public class BlazorWasmBuildPublishTests : BuildTestBase
+ {
+ public BlazorWasmBuildPublishTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ _enablePerTestCleanup = true;
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void DefaultTemplate_WithoutWorkload(string config)
+ {
+ string id = $"blz_no_workload_{config}";
+ CreateBlazorWasmTemplateProject(id);
+
+ // Build
+ BuildInternal(id, config, publish: false);
+ AssertBlazorBootJson(config, isPublish: false);
+
+ // Publish
+ BuildInternal(id, config, publish: true);
+ AssertBlazorBootJson(config, isPublish: true);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void DefaultTemplate_NoAOT_WithWorkload(string config)
+ {
+ string id = $"blz_no_aot_{config}";
+ CreateBlazorWasmTemplateProject(id);
+
+ BlazorBuild(id, config, NativeFilesType.FromRuntimePack);
+ if (config == "Release")
+ {
+ // relinking in publish for Release config
+ BlazorPublish(id, config, NativeFilesType.Relinked);
+ }
+ else
+ {
+ BlazorPublish(id, config, NativeFilesType.FromRuntimePack);
+ }
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void DefaultTemplate_AOT_InProjectFile(string config)
+ {
+ string id = $"blz_aot_prj_file_{config}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: "<RunAOTCompilation>true</RunAOTCompilation>");
+
+ // No relinking, no AOT
+ BlazorBuild(id, config, NativeFilesType.FromRuntimePack);
+
+ // will aot
+ BlazorPublish(id, config, NativeFilesType.AOT);
+
+ // build again
+ BlazorBuild(id, config, NativeFilesType.FromRuntimePack);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug", true)]
+ [InlineData("Debug", false)]
+ [InlineData("Release", true)]
+ [InlineData("Release", false)]
+ public void NativeBuild_WithDeployOnBuild_UsedByVS(string config, bool nativeRelink)
+ {
+ string id = $"blz_deploy_on_build_{config}_{nativeRelink}";
+ string projectFile = CreateProjectWithNativeReference(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: nativeRelink ? string.Empty : "<RunAOTCompilation>true</RunAOTCompilation>");
+
+ // build with -p:DeployOnBuild=true, and that will trigger a publish
+ (CommandResult res, _) = BuildInternal(id, config, publish: false, "-p:DeployOnBuild=true");
+
+ var expectedFileType = nativeRelink ? NativeFilesType.Relinked : NativeFilesType.AOT;
+
+ AssertDotNetNativeFiles(expectedFileType, config, forPublish: true);
+ AssertBlazorBundle(config, isPublish: true, dotnetWasmFromRuntimePack: false);
+
+ if (expectedFileType == NativeFilesType.AOT)
+ {
+ // check for this too, so we know the format is correct for the negative
+ // test for jsinterop.webassembly.dll
+ Assert.Contains("Microsoft.JSInterop.dll -> Microsoft.JSInterop.dll.bc", res.Output);
+
+ // make sure this assembly gets skipped
+ Assert.DoesNotContain("Microsoft.JSInterop.WebAssembly.dll -> Microsoft.JSInterop.WebAssembly.dll.bc", res.Output);
+ }
+
+ // Check that we linked only for publish
+ string objBuildDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", "for-build");
+ Assert.False(Directory.Exists(objBuildDir), $"Found unexpected {objBuildDir}, which gets creating when relinking during Build");
+
+ // double check!
+ int index = res.Output.IndexOf("pinvoke.c -> pinvoke.o");
+ Assert.NotEqual(-1, index);
+
+ // there should be only one instance of this string!
+ index = res.Output.IndexOf("pinvoke.c -> pinvoke.o", index + 1);
+ Assert.Equal(-1, index);
+ }
+
+ // Disabling for now - publish folder can have more than one dotnet*hash*js, and not sure
+ // how to pick which one to check, for the test
+ //[ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ //[InlineData("Debug")]
+ //[InlineData("Release")]
+ //public void DefaultTemplate_AOT_OnlyWithPublishCommandLine_Then_PublishNoAOT(string config)
+ //{
+ //string id = $"blz_aot_pub_{config}";
+ //CreateBlazorWasmTemplateProject(id);
+
+ //// No relinking, no AOT
+ //BlazorBuild(id, config, NativeFilesType.FromRuntimePack);
+
+ //// AOT=true only for the publish command line, similar to what
+ //// would happen when setting it in Publish dialog for VS
+ //BlazorPublish(id, config, expectedFileType: NativeFilesType.AOT, "-p:RunAOTCompilation=true");
+
+ //// publish again, no AOT
+ //BlazorPublish(id, config, NativeFilesType.Relinked);
+ //}
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void WithNativeReference_AOTInProjectFile(string config)
+ {
+ string id = $"blz_nativeref_aot_{config}";
+ string projectFile = CreateProjectWithNativeReference(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: "<RunAOTCompilation>true</RunAOTCompilation>");
+
+ BlazorBuild(id, config, expectedFileType: NativeFilesType.Relinked);
+
+ BlazorPublish(id, config, expectedFileType: NativeFilesType.AOT);
+
+ // will relink
+ BlazorBuild(id, config, expectedFileType: NativeFilesType.Relinked);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void WithNativeReference_AOTOnCommandLine(string config)
+ {
+ string id = $"blz_nativeref_aot_{config}";
+ CreateProjectWithNativeReference(id);
+
+ BlazorBuild(id, config, expectedFileType: NativeFilesType.Relinked);
+
+ BlazorPublish(id, config, expectedFileType: NativeFilesType.AOT, "-p:RunAOTCompilation=true");
+
+ // no aot!
+ BlazorPublish(id, config, expectedFileType: NativeFilesType.Relinked);
+ }
+
+ private string CreateProjectWithNativeReference(string id)
+ {
+ CreateBlazorWasmTemplateProject(id);
+
+ string extraItems = @$"
+ <PackageReference Include=""SkiaSharp"" Version=""2.80.3"" />
+ <PackageReference Include=""SkiaSharp.NativeAssets.WebAssembly"" Version=""2.80.3"" />
+
+ <NativeFileReference Include=""$(SkiaSharpStaticLibraryPath)\2.0.9\*.a"" />
+ <WasmFilesToIncludeInFileSystem Include=""{Path.Combine(BuildEnvironment.TestAssetsPath, "mono.png")}"" />
+ ";
+ string projectFile = Path.Combine(_projectDir!, $"{id}.csproj");
+ AddItemsPropertiesToProject(projectFile, extraItems: extraItems);
+
+ return projectFile;
+ }
+
+ }
+
+ public enum NativeFilesType { FromRuntimePack, Relinked, AOT };
+}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Collections.Generic;
using System.IO;
using Xunit;
using Xunit.Abstractions;
{
}
- // TODO: invariant case?
-
- [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
- [InlineData("Debug", false)]
- [InlineData("Debug", true)] // just aot
- [InlineData("Release", false)] // should re-link
- [InlineData("Release", true)]
- public void PublishTemplateProject(string config, bool aot)
- {
- string id = $"blazorwasm_{config}_aot_{aot}_{Path.GetRandomFileName()}";
- InitBlazorWasmProjectDir(id);
-
- new DotNetCommand(s_buildEnv, useDefaultArgs: false)
- .WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput("new blazorwasm")
- .EnsureSuccessful();
-
- string publishLogPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}.binlog");
- new DotNetCommand(s_buildEnv)
- .WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput("publish", $"-bl:{publishLogPath}", aot ? "-p:RunAOTCompilation=true" : "", $"-p:Configuration={config}")
- .EnsureSuccessful();
-
- //TODO: validate the build somehow?
- // compare dotnet.wasm?
- // relinking - dotnet.wasm should be smaller
- //
- // playwright?
- }
-
[ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))]
[InlineData("Debug")]
[InlineData("Release")]
{
CommandResult res = PublishForRequiresWorkloadTest(config, extraItems: "<NativeFileReference Include=\"native-lib.o\" />");
res.EnsureSuccessful();
+ AssertBlazorBundle(config, isPublish: true, dotnetWasmFromRuntimePack: true);
Assert.Contains("but the native references won't be linked in", res.Output);
}
[ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))]
[InlineData("Debug")]
[InlineData("Release")]
- public void AOT_And_NativeRef_FailsBecauseItRequireWorkload(string config)
+ public void AOT_And_NativeRef_FailBecauseTheyRequireWorkload(string config)
{
CommandResult res = PublishForRequiresWorkloadTest(config,
extraProperties: "<RunAOTCompilation>true</RunAOTCompilation>",
private CommandResult PublishForRequiresWorkloadTest(string config, string extraItems="", string extraProperties="")
{
string id = $"needs_workload_{config}_{Path.GetRandomFileName()}";
- InitBlazorWasmProjectDir(id);
-
- new DotNetCommand(s_buildEnv, useDefaultArgs: false)
- .WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput("new blazorwasm")
- .EnsureSuccessful();
+ CreateBlazorWasmTemplateProject(id);
if (IsNotUsingWorkloads)
{
// no packs installed, so no need to update the paths for runtime pack etc
- File.WriteAllText(Path.Combine(_projectDir!, "Directory.Build.props"), "<Project />");
- File.WriteAllText(Path.Combine(_projectDir!, "Directory.Build.targets"), "<Project />");
+ File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Local.Directory.Build.props"), Path.Combine(_projectDir!, "Directory.Build.props"), overwrite: true);
+ File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Local.Directory.Build.targets"), Path.Combine(_projectDir!, "Directory.Build.targets"), overwrite: true);
}
AddItemsPropertiesToProject(Path.Combine(_projectDir!, $"{id}.csproj"),
InitBlazorWasmProjectDir(id);
string directoryBuildTargets = @"<Project>
- <Target Name=""PrintAllProjects"" BeforeTargets=""Build"">
+ <Target Name=""PrintAllProjects"" BeforeTargets=""Build"">
<Message Text=""** UsingBrowserRuntimeWorkload: '$(UsingBrowserRuntimeWorkload)'"" Importance=""High"" />
</Target>
</Project>";
string publishLogPath = Path.Combine(logPath, $"{id}.binlog");
CommandResult result = new DotNetCommand(s_buildEnv)
- .WithWorkingDirectory(_projectDir!)
- .ExecuteWithCapturedOutput("publish",
- $"-bl:{publishLogPath}",
- (aot ? "-p:RunAOTCompilation=true" : ""),
- $"-p:Configuration={config}");
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("publish",
+ $"-bl:{publishLogPath}",
+ (aot ? "-p:RunAOTCompilation=true" : ""),
+ $"-p:Configuration={config}");
if (expectError)
{
{
result.EnsureSuccessful();
Assert.Contains("** UsingBrowserRuntimeWorkload: 'false'", result.Output);
+
+ string binFrameworkDir = FindBlazorBinFrameworkDir(config, forPublish: true, framework: "net5.0");
+ AssertBlazorBootJson(config, isPublish: true, binFrameworkDir: binFrameworkDir);
+ // dotnet.wasm here would be from 5.0 nuget like:
+ // /Users/radical/.nuget/packages/microsoft.netcore.app.runtime.browser-wasm/5.0.9/runtimes/browser-wasm/native/dotnet.wasm
}
}
}
.UnwrapItemsAsArrays().ToList().Dump();
}
- public BuildAndRunAttribute(bool aot=false, RunHost host = RunHost.All, params object?[] parameters)
+ public BuildAndRunAttribute(bool aot=false, RunHost host = RunHost.All, string? config=null, params object?[] parameters)
{
- _data = BuildTestBase.ConfigWithAOTData(aot)
+ _data = BuildTestBase.ConfigWithAOTData(aot, config)
.Multiply(parameters)
.WithRunHosts(host)
.UnwrapItemsAsArrays().ToList().Dump();
protected static string s_directoryBuildPropsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.props"));
protected static string s_directoryBuildTargetsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.targets"));
+
+ protected static string s_directoryBuildPropsForBlazorLocal = File.ReadAllText(Path.Combine(TestDataPath, "Blazor.Local.Directory.Build.props"));
+ protected static string s_directoryBuildTargetsForBlazorLocal = File.ReadAllText(Path.Combine(TestDataPath, "Blazor.Local.Directory.Build.targets"));
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Linq;
+using Wasm.Build.NativeRebuild.Tests;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+#nullable enable
+
+namespace Wasm.Build.Tests
+{
+ public class BuildPublishTests : NativeRebuildTestsBase
+ {
+ public BuildPublishTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [Theory]
+ [BuildAndRun(host: RunHost.V8, aot: false, config: "Release")]
+ [BuildAndRun(host: RunHost.V8, aot: false, config: "Debug")]
+ public void BuildThenPublishNoAOT(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string projectName = $"build_publish_{buildArgs.Config}";
+
+ buildArgs = buildArgs with { ProjectName = projectName };
+ buildArgs = ExpandBuildArgs(buildArgs);
+
+ // no relinking for build
+ bool relinked = false;
+ BuildProject(buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: !relinked,
+ id: id,
+ createProject: true,
+ publish: false);
+
+ Run();
+
+ if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product))
+ throw new XunitException($"Test bug: could not get the build product in the cache");
+
+ File.Move(product!.LogFile, Path.ChangeExtension(product.LogFile!, ".first.binlog"));
+
+ _testOutput.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}");
+ Console.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}");
+
+ // relink by default for Release+publish
+ relinked = buildArgs.Config == "Release";
+ BuildProject(buildArgs,
+ id: id,
+ dotnetWasmFromRuntimePack: !relinked,
+ createProject: false,
+ publish: true,
+ useCache: false);
+
+ Run();
+
+ void Run() => RunAndTestWasmApp(
+ buildArgs, buildDir: _projectDir, expectedExitCode: 42,
+ test: output => {},
+ host: host, id: id);
+ }
+
+ [Theory]
+ [BuildAndRun(host: RunHost.V8, aot: true, config: "Release")]
+ [BuildAndRun(host: RunHost.V8, aot: true, config: "Debug")]
+ public void BuildThenPublishWithAOT(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string projectName = $"build_publish_{buildArgs.Config}";
+
+ buildArgs = buildArgs with { ProjectName = projectName };
+ buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<_WasmDevel>true</_WasmDevel>");
+
+ // no relinking for build
+ bool relinked = false;
+ (_, string output) = BuildProject(buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: !relinked,
+ id: id,
+ createProject: true,
+ publish: false,
+ label: "first_build");
+
+ BuildPaths paths = GetBuildPaths(buildArgs);
+ var pathsDict = GetFilesTable(buildArgs, paths, unchanged: false);
+
+ string mainDll = $"{buildArgs.ProjectName}.dll";
+ var firstBuildStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+ Assert.False(firstBuildStat["pinvoke.o"].Exists);
+ Assert.False(firstBuildStat[$"{mainDll}.bc"].Exists);
+
+ CheckOutputForNativeBuild(expectAOT: false, expectRelinking: relinked, buildArgs, output);
+
+ Run(expectAOT: false);
+
+ if (!_buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product))
+ throw new XunitException($"Test bug: could not get the build product in the cache");
+
+ File.Move(product!.LogFile, Path.ChangeExtension(product.LogFile!, ".first.binlog"));
+
+ _testOutput.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}");
+ Console.WriteLine($"{Environment.NewLine}Publishing with no changes ..{Environment.NewLine}");
+
+ // relink by default for Release+publish
+ (_, output) = BuildProject(buildArgs,
+ id: id,
+ dotnetWasmFromRuntimePack: false,
+ createProject: false,
+ publish: true,
+ useCache: false,
+ label: "first_publish");
+
+ var publishStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+ Assert.True(publishStat["pinvoke.o"].Exists);
+ Assert.True(publishStat[$"{mainDll}.bc"].Exists);
+ CheckOutputForNativeBuild(expectAOT: true, expectRelinking: false, buildArgs, output);
+ CompareStat(firstBuildStat, publishStat, pathsDict.Values);
+
+ Run(expectAOT: true);
+
+ // second build
+ (_, output) = BuildProject(buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: !relinked,
+ id: id,
+ createProject: true,
+ publish: false,
+ label: "second_build");
+ var secondBuildStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ // no relinking, or AOT
+ CheckOutputForNativeBuild(expectAOT: false, expectRelinking: false, buildArgs, output);
+
+ // no native files changed
+ pathsDict.UpdateTo(unchanged: true);
+ CompareStat(publishStat, secondBuildStat, pathsDict.Values);
+
+ void Run(bool expectAOT) => RunAndTestWasmApp(
+ buildArgs with { AOT = expectAOT },
+ buildDir: _projectDir, expectedExitCode: 42,
+ host: host, id: id);
+ }
+
+ void CheckOutputForNativeBuild(bool expectAOT, bool expectRelinking, BuildArgs buildArgs, string buildOutput)
+ {
+ AssertSubstring($"{buildArgs.ProjectName}.dll -> {buildArgs.ProjectName}.dll.bc", buildOutput, expectAOT);
+ AssertSubstring($"{buildArgs.ProjectName}.dll.bc -> {buildArgs.ProjectName}.dll.o", buildOutput, expectAOT);
+
+ AssertSubstring("pinvoke.c -> pinvoke.o", buildOutput, expectRelinking || expectAOT);
+ }
+
+ }
+}
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
+using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using System.Xml;
using Xunit;
bool useCache = true,
bool expectSuccess = true,
bool createProject = true,
- string? verbosity=null)
+ bool publish = true,
+ string? verbosity=null,
+ string? label=null)
{
+ string msgPrefix = label != null ? $"[{label}] " : string.Empty;
if (useCache && _buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product))
{
Console.WriteLine ($"Using existing build found at {product.ProjectDir}, with build log at {product.LogFile}");
}
StringBuilder sb = new();
- sb.Append("publish");
+ sb.Append(publish ? "publish" : "build");
sb.Append($" {s_buildEnv.DefaultBuildArgs}");
sb.Append($" /p:Configuration={buildArgs.Config}");
- string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}.binlog");
+ string logFileSuffix = label == null ? string.Empty : label.Replace(' ', '_');
+ string logFilePath = Path.Combine(_logPath, $"{buildArgs.ProjectName}{logFileSuffix}.binlog");
_testOutput.WriteLine($"-------- Building ---------");
_testOutput.WriteLine($"Binlog path: {logFilePath}");
Console.WriteLine($"Binlog path: {logFilePath}");
File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.targets"), Path.Combine(_projectDir, "Directory.Build.targets"));
}
+ public string CreateBlazorWasmTemplateProject(string id)
+ {
+ InitBlazorWasmProjectDir(id);
+ new DotNetCommand(s_buildEnv, useDefaultArgs: false)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("new blazorwasm")
+ .EnsureSuccessful();
+
+ return Path.Combine(_projectDir!, $"{id}.csproj");
+ }
+
+ protected (CommandResult, string) BlazorBuild(string id, string config, NativeFilesType expectedFileType, params string[] extraArgs)
+ {
+ var res = BuildInternal(id, config, publish: false, extraArgs);
+ AssertDotNetNativeFiles(expectedFileType, config, forPublish: false);
+ AssertBlazorBundle(config, isPublish: false, dotnetWasmFromRuntimePack: expectedFileType == NativeFilesType.FromRuntimePack);
+
+ return res;
+ }
+
+ protected (CommandResult, string) BlazorPublish(string id, string config, NativeFilesType expectedFileType, params string[] extraArgs)
+ {
+ var res = BuildInternal(id, config, publish: true, extraArgs);
+ AssertDotNetNativeFiles(expectedFileType, config, forPublish: true);
+ AssertBlazorBundle(config, isPublish: true, dotnetWasmFromRuntimePack: expectedFileType == NativeFilesType.FromRuntimePack);
+
+ if (expectedFileType == NativeFilesType.AOT)
+ {
+ // check for this too, so we know the format is correct for the negative
+ // test for jsinterop.webassembly.dll
+ Assert.Contains("Microsoft.JSInterop.dll -> Microsoft.JSInterop.dll.bc", res.Item1.Output);
+
+ // make sure this assembly gets skipped
+ Assert.DoesNotContain("Microsoft.JSInterop.WebAssembly.dll -> Microsoft.JSInterop.WebAssembly.dll.bc", res.Item1.Output);
+ }
+ return res;
+ }
+
+ protected (CommandResult, string) BuildInternal(string id, string config, bool publish=false, params string[] extraArgs)
+ {
+ string label = publish ? "publish" : "build";
+ Console.WriteLine($"{Environment.NewLine}** {label} **{Environment.NewLine}");
+
+ string logPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-{label}.binlog");
+ string[] combinedArgs = new[]
+ {
+ label, // same as the command name
+ $"-bl:{logPath}",
+ $"-p:Configuration={config}",
+ "-p:BlazorEnableCompression=false",
+ "-p:_WasmDevel=true"
+ }.Concat(extraArgs).ToArray();
+
+ CommandResult res = new DotNetCommand(s_buildEnv)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput(combinedArgs)
+ .EnsureSuccessful();
+
+ return (res, logPath);
+ }
+
+ protected void AssertDotNetNativeFiles(NativeFilesType type, string config, bool forPublish)
+ {
+ string label = forPublish ? "publish" : "build";
+ string objBuildDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", forPublish ? "for-publish" : "for-build");
+ string binFrameworkDir = FindBlazorBinFrameworkDir(config, forPublish);
+
+ string srcDir = type switch
+ {
+ NativeFilesType.FromRuntimePack => s_buildEnv.RuntimeNativeDir,
+ NativeFilesType.Relinked => objBuildDir,
+ NativeFilesType.AOT => objBuildDir,
+ _ => throw new ArgumentOutOfRangeException(nameof(type))
+ };
+
+ AssertSameFile(Path.Combine(srcDir, "dotnet.wasm"), Path.Combine(binFrameworkDir, "dotnet.wasm"), label);
+
+ // find dotnet*js
+ string? dotnetJsPath = Directory.EnumerateFiles(binFrameworkDir)
+ .Where(p => Path.GetFileName(p).StartsWith("dotnet.", StringComparison.OrdinalIgnoreCase) &&
+ Path.GetFileName(p).EndsWith(".js", StringComparison.OrdinalIgnoreCase))
+ .SingleOrDefault();
+
+ Assert.True(!string.IsNullOrEmpty(dotnetJsPath), $"[{label}] Expected to find dotnet*js in {binFrameworkDir}");
+ AssertSameFile(Path.Combine(srcDir, "dotnet.js"), dotnetJsPath!, label);
+
+ if (type != NativeFilesType.FromRuntimePack)
+ {
+ // check that the files are *not* from runtime pack
+ AssertNotSameFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.wasm"), Path.Combine(binFrameworkDir, "dotnet.wasm"), label);
+ AssertNotSameFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.js"), dotnetJsPath!, label);
+ }
+ }
+
static void AssertRuntimePackPath(string buildOutput)
{
var match = s_runtimePackPathRegex.Match(buildOutput);
protected static void AssertBasicAppBundle(string bundleDir, string projectName, string config, bool hasIcudt=true, bool dotnetWasmFromRuntimePack=true)
{
- Console.WriteLine ($"AssertBasicAppBundle: {dotnetWasmFromRuntimePack}");
AssertFilesExist(bundleDir, new []
{
"index.html",
return result;
}
+ protected void AssertBlazorBundle(string config, bool isPublish, bool dotnetWasmFromRuntimePack, string? binFrameworkDir=null)
+ {
+ binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish);
+
+ AssertBlazorBootJson(config, isPublish, binFrameworkDir: binFrameworkDir);
+ AssertFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.wasm"),
+ Path.Combine(binFrameworkDir, "dotnet.wasm"),
+ "Expected dotnet.wasm to be same as the runtime pack",
+ same: dotnetWasmFromRuntimePack);
+
+ string? dotnetJsPath = Directory.EnumerateFiles(binFrameworkDir, "dotnet.*.js").FirstOrDefault();
+ Assert.True(dotnetJsPath != null, $"Could not find blazor's dotnet*js in {binFrameworkDir}");
+
+ AssertFile(Path.Combine(s_buildEnv.RuntimeNativeDir, "dotnet.js"),
+ dotnetJsPath!,
+ "Expected dotnet.js to be same as the runtime pack",
+ same: dotnetWasmFromRuntimePack);
+ }
+
+ protected void AssertBlazorBootJson(string config, bool isPublish, string? binFrameworkDir=null)
+ {
+ binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish);
+
+ string bootJsonPath = Path.Combine(binFrameworkDir, "blazor.boot.json");
+ Assert.True(File.Exists(bootJsonPath), $"Expected to find {bootJsonPath}");
+
+ string bootJson = File.ReadAllText(bootJsonPath);
+ var bootJsonNode = JsonNode.Parse(bootJson);
+ var runtimeObj = bootJsonNode?["resources"]?["runtime"]?.AsObject();
+ Assert.NotNull(runtimeObj);
+
+ string msgPrefix=$"[{( isPublish ? "publish" : "build" )}]";
+ Assert.True(runtimeObj!.Where(kvp => kvp.Key == "dotnet.wasm").Any(), $"{msgPrefix} Could not find dotnet.wasm entry in blazor.boot.json");
+ Assert.True(runtimeObj!.Where(kvp => kvp.Key.StartsWith("dotnet.", StringComparison.OrdinalIgnoreCase) &&
+ kvp.Key.EndsWith(".js", StringComparison.OrdinalIgnoreCase)).Any(),
+ $"{msgPrefix} Could not find dotnet.*js in {bootJson}");
+ }
+
+ protected string FindBlazorBinFrameworkDir(string config, bool forPublish, string framework="net6.0")
+ {
+ string basePath = Path.Combine(_projectDir!, "bin", config, framework);
+ if (forPublish)
+ basePath = FindSubDirIgnoringCase(basePath, "publish");
+
+ return Path.Combine(basePath, "wwwroot", "_framework");
+ }
+
+ private string FindSubDirIgnoringCase(string parentDir, string dirName)
+ {
+ IEnumerable<string> matchingDirs = Directory.EnumerateDirectories(parentDir,
+ dirName,
+ new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive });
+
+ string? first = matchingDirs.FirstOrDefault();
+ if (matchingDirs.Count() > 1)
+ throw new Exception($"Found multiple directories with names that differ only in case. {string.Join(", ", matchingDirs.ToArray())}");
+
+ return first ?? Path.Combine(parentDir, dirName);
+ }
+
protected string GetBinDir(string config, string targetFramework=s_targetFramework, string? baseDir=null)
{
var dir = baseDir ?? _projectDir;
}
}
- public static string AddItemsPropertiesToProject(string projectFile, string? extraProperties=null, string? extraItems=null)
+ public static string AddItemsPropertiesToProject(string projectFile, string? extraProperties=null, string? extraItems=null, string? atTheEnd=null)
{
- if (extraProperties == null && extraItems == null)
+ if (extraProperties == null && extraItems == null && atTheEnd == null)
return projectFile;
XmlDocument doc = new();
doc.Load(projectFile);
+ XmlNode root = doc.DocumentElement ?? throw new Exception();
if (extraItems != null)
{
XmlNode node = doc.CreateNode(XmlNodeType.Element, "ItemGroup", null);
node.InnerXml = extraItems;
- doc.DocumentElement!.AppendChild(node);
+ root.AppendChild(node);
}
if (extraProperties != null)
{
XmlNode node = doc.CreateNode(XmlNodeType.Element, "PropertyGroup", null);
node.InnerXml = extraProperties;
- doc.DocumentElement!.AppendChild(node);
+ root.AppendChild(node);
+ }
+
+ if (atTheEnd != null)
+ {
+ XmlNode node = doc.CreateNode(XmlNodeType.DocumentFragment, "foo", null);
+ node.InnerXml = atTheEnd;
+ root.InsertAfter(node, root.LastChild);
}
doc.Save(projectFile);
return string.IsNullOrEmpty(value) ? defaultValue : value;
}
+ internal BuildPaths GetBuildPaths(BuildArgs buildArgs, bool forPublish=true)
+ {
+ string objDir = GetObjDir(buildArgs.Config);
+ string bundleDir = Path.Combine(GetBinDir(baseDir: _projectDir, config: buildArgs.Config), "AppBundle");
+ string wasmDir = Path.Combine(objDir, "wasm", forPublish ? "for-publish" : "for-build");
+
+ return new BuildPaths(wasmDir, objDir, GetBinDir(buildArgs.Config), bundleDir);
+ }
+
+ internal IDictionary<string, FileStat> StatFiles(IEnumerable<string> fullpaths)
+ {
+ Dictionary<string, FileStat> table = new();
+ foreach (string file in fullpaths)
+ {
+ if (File.Exists(file))
+ table.Add(Path.GetFileName(file), new FileStat(FullPath: file, Exists: true, LastWriteTimeUtc: File.GetLastWriteTimeUtc(file), Length: new FileInfo(file).Length));
+ else
+ table.Add(Path.GetFileName(file), new FileStat(FullPath: file, Exists: false, LastWriteTimeUtc: DateTime.MinValue, Length: 0));
+ }
+
+ return table;
+ }
+
protected static string s_mainReturns42 = @"
public class TestClass {
public static int Main()
string ProjectFileContents,
string? ExtraBuildArgs);
public record BuildProduct(string ProjectDir, string LogFile, bool Result);
+ internal record FileStat (bool Exists, DateTime LastWriteTimeUtc, long Length, string FullPath);
+ internal record BuildPaths(string ObjWasmDir, string ObjDir, string BinDir, string BundleDir);
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Linq;
+using Wasm.Build.NativeRebuild.Tests;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+#nullable enable
+
+namespace Wasm.Build.Tests;
+
+public class CleanTests : NativeRebuildTestsBase
+{
+ public CleanTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void Blazor_BuildThenClean_NativeRelinking(string config)
+ {
+ string id = Path.GetRandomFileName();
+
+ InitBlazorWasmProjectDir(id);
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+
+ string extraProperties = @"<_WasmDevel>true</_WasmDevel>
+ <WasmBuildNative>true</WasmBuildNative>";
+
+ AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
+ BlazorBuild(id, config, NativeFilesType.Relinked);
+
+ string relinkDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", "for-build");
+ Assert.True(Directory.Exists(relinkDir), $"Could not find expected relink dir: {relinkDir}");
+
+ string logPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-clean.binlog");
+ new DotNetCommand(s_buildEnv)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("build", "-t:Clean", $"-p:Configuration={config}", $"-bl:{logPath}")
+ .EnsureSuccessful();
+
+ AssertEmptyOrNonExistantDirectory(relinkDir);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void Blazor_BuildNoNative_ThenBuildNative_ThenClean(string config)
+ => Blazor_BuildNativeNonNative_ThenCleanTest(config, firstBuildNative: false);
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void Blazor_BuildNative_ThenBuildNonNative_ThenClean(string config)
+ => Blazor_BuildNativeNonNative_ThenCleanTest(config, firstBuildNative: true);
+
+ private void Blazor_BuildNativeNonNative_ThenCleanTest(string config, bool firstBuildNative)
+ {
+ string id = Path.GetRandomFileName();
+
+ InitBlazorWasmProjectDir(id);
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+
+ string extraProperties = @"<_WasmDevel>true</_WasmDevel>";
+
+ AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
+
+ bool relink = firstBuildNative;
+ BuildInternal(id, config, publish: false,
+ extraArgs: relink ? "-p:WasmBuildNative=true" : string.Empty);
+
+ string relinkDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", "for-build");
+ if (relink)
+ Assert.True(Directory.Exists(relinkDir), $"Could not find expected relink dir: {relinkDir}");
+
+ relink = !firstBuildNative;
+ BuildInternal(id, config, publish: false,
+ extraArgs: relink ? "-p:WasmBuildNative=true" : string.Empty);
+
+ if (relink)
+ Assert.True(Directory.Exists(relinkDir), $"Could not find expected relink dir: {relinkDir}");
+
+ string logPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-clean.binlog");
+ new DotNetCommand(s_buildEnv)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("build", "-t:Clean", $"-p:Configuration={config}", $"-bl:{logPath}")
+ .EnsureSuccessful();
+
+ AssertEmptyOrNonExistantDirectory(relinkDir);
+ }
+ private void AssertEmptyOrNonExistantDirectory(string dirPath)
+ {
+ Console.WriteLine($"dirPath: {dirPath}");
+ if (!Directory.Exists(dirPath))
+ return;
+
+ var files = Directory.GetFileSystemEntries(dirPath);
+ if (files.Length == 0)
+ return;
+
+ string found = string.Join(',', files.Select(p => Path.GetFileName(p)));
+ throw new XunitException($"Expected dir {dirPath} to be empty, but found: {found}");
+ }
+}
Output = output;
}
- public void EnsureSuccessful(string messagePrefix = "", bool suppressOutput = false)
+ public CommandResult EnsureSuccessful(string messagePrefix = "", bool suppressOutput = false)
=> EnsureExitCode(0, messagePrefix, suppressOutput);
- public void EnsureExitCode(int expectedExitCode = 0, string messagePrefix = "", bool suppressOutput = false)
+ public CommandResult EnsureExitCode(int expectedExitCode = 0, string messagePrefix = "", bool suppressOutput = false)
{
if (ExitCode != expectedExitCode)
{
throw new XunitException(message.ToString());
}
+
+ return this;
}
}
}
using System.Linq;
using System.IO;
using System.Text;
+using System.Collections;
#nullable enable
public static void UpdateTo(this IDictionary<string, (string fullPath, bool unchanged)> dict, bool unchanged, params string[] filenames)
{
- foreach (var filename in filenames)
+ IEnumerable<string> keys = filenames.Length == 0 ? dict.Keys.ToList() : filenames;
+
+ foreach (var filename in keys)
{
if (!dict.TryGetValue(filename, out var oldValue))
{
[Theory]
[BuildAndRun]
public void SimpleNativeBuild(BuildArgs buildArgs, RunHost host, string id)
- => NativeBuild("simple_native_build", s_mainReturns42, buildArgs, host, id);
-
- private void NativeBuild(string projectNamePrefix, string projectContents, BuildArgs buildArgs, RunHost host, string id)
{
- string projectName = $"{projectNamePrefix}_{buildArgs.Config}_{buildArgs.AOT}";
+ string projectName = $"simple_native_build_{buildArgs.Config}_{buildArgs.AOT}";
buildArgs = buildArgs with { ProjectName = projectName };
buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<WasmBuildNative>true</WasmBuildNative>");
BuildProject(buildArgs,
- initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), projectContents),
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
dotnetWasmFromRuntimePack: false,
id: id);
}
[Theory]
+ [BuildAndRun(aot: true, host: RunHost.None)]
+ public void MonoAOTCross_WorksWithNoTrimming(BuildArgs buildArgs, string id)
+ {
+ // stop once `mono-aot-cross` part of the build is done
+ string target = @"<Target Name=""StopAfterWasmAOT"" AfterTargets=""_WasmAotCompileApp"">
+ <Error Text=""Stopping after AOT"" Condition=""'$(WasmBuildingForNestedPublish)' == 'true'"" />
+ </Target>";
+
+ string projectName = $"mono_aot_cross_{buildArgs.Config}_{buildArgs.AOT}";
+
+ buildArgs = buildArgs with { ProjectName = projectName, ExtraBuildArgs = "-p:PublishTrimmed=false -v:n" };
+ buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<WasmBuildNative>true</WasmBuildNative>", insertAtEnd: target);
+
+ (_, string output) = BuildProject(
+ buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: false,
+ id: id,
+ expectSuccess: false);
+
+ Assert.Contains("Stopping after AOT", output);
+ }
+
+ [Theory]
[BuildAndRun(host: RunHost.None, aot: true)]
public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, string id)
{
string printFileTypeTarget = @"
- <Target Name=""PrintIntermediateFileType"" AfterTargets=""WasmBuildApp"">
+ <Target Name=""PrintIntermediateFileType"" AfterTargets=""WasmNestedPublishApp"">
<Exec Command=""wasm-dis $(_WasmIntermediateOutputPath)System.Private.CoreLib.dll.o -o $(_WasmIntermediateOutputPath)wasm-dis-out.txt""
- ConsoleToMSBuild=""true""
EnvironmentVariables=""@(EmscriptenEnvVars)""
IgnoreExitCode=""true"">
<Output TaskParameter=""ExitCode"" PropertyName=""ExitCode"" />
</Exec>
- <Message Text=""wasm-dis exit code: $(ExitCode)"" Importance=""High"" />
+ <Message Text=""
+ ** wasm-dis exit code: $(ExitCode)
+ "" Importance=""High"" />
</Target>
";
string projectName = $"bc_to_o_{buildArgs.Config}";
dotnetWasmFromRuntimePack: false,
id: id);
- if (!output.Contains("wasm-dis exit code: 0"))
+ if (!output.Contains("** wasm-dis exit code: 0"))
throw new XunitException($"Expected to successfully run wasm-dis on System.Private.CoreLib.dll.o ."
+ " It might fail if it was incorrectly compiled to a bitcode file, instead of wasm.");
}
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void BlazorWasm_CanRunMonoAOTCross_WithNoTrimming(string config)
+ {
+ string id = $"blazorwasm_{config}_aot";
+ CreateBlazorWasmTemplateProject(id);
+
+ // We don't want to emcc compile, and link ~180 assemblies!
+ // So, stop once `mono-aot-cross` part of the build is done
+ string target = @"<Target Name=""StopAfterWasmAOT"" AfterTargets=""_WasmAotCompileApp"">
+ <Error Text=""Stopping after AOT"" Condition=""'$(WasmBuildingForNestedPublish)' == 'true'"" />
+ </Target>
+ ";
+ AddItemsPropertiesToProject(Path.Combine(_projectDir!, $"{id}.csproj"),
+ extraItems: null,
+ extraProperties: null,
+ atTheEnd: target);
+
+ string publishLogPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}.binlog");
+ CommandResult res = new DotNetCommand(s_buildEnv)
+ .WithWorkingDirectory(_projectDir!)
+ .ExecuteWithCapturedOutput("publish",
+ $"-bl:{publishLogPath}",
+ "-p:RunAOTCompilation=true",
+ "-p:PublishTrimmed=false",
+ $"-p:Configuration={config}");
+
+ Assert.True(res.ExitCode != 0, "Expected publish to fail");
+ Assert.Contains("Stopping after AOT", res.Output);
+ }
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.IO;
using Xunit;
using Xunit.Abstractions;
using System.IO;
using System.Collections.Generic;
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
public class FlagsChangeRebuildTest : NativeRebuildTestsBase
{
using System.IO;
using System.Collections.Generic;
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
// TODO: test for runtime components
public class NativeRebuildTestsBase : BuildTestBase
throw new XunitException($"CompareStat failed:{Environment.NewLine}{msg}");
}
- internal IDictionary<string, FileStat> StatFiles(IEnumerable<string> fullpaths)
- {
- Dictionary<string, FileStat> table = new();
- foreach (string file in fullpaths)
- {
- if (File.Exists(file))
- table.Add(Path.GetFileName(file), new FileStat(FullPath: file, Exists: true, LastWriteTimeUtc: File.GetLastWriteTimeUtc(file), Length: new FileInfo(file).Length));
- else
- table.Add(Path.GetFileName(file), new FileStat(FullPath: file, Exists: false, LastWriteTimeUtc: DateTime.MinValue, Length: 0));
- }
-
- return table;
- }
-
- internal BuildPaths GetBuildPaths(BuildArgs buildArgs)
- {
- string objDir = GetObjDir(buildArgs.Config);
- string bundleDir = Path.Combine(GetBinDir(baseDir: _projectDir, config: buildArgs.Config), "AppBundle");
- string wasmDir = Path.Combine(objDir, "wasm");
-
- return new BuildPaths(wasmDir, objDir, GetBinDir(buildArgs.Config), bundleDir);
- }
-
internal IDictionary<string, (string fullPath, bool unchanged)> GetFilesTable(BuildArgs buildArgs, BuildPaths paths, bool unchanged)
{
List<string> files = new()
Assert.DoesNotContain(substring, full);
}
}
-
- internal record FileStat (bool Exists, DateTime LastWriteTimeUtc, long Length, string FullPath);
- internal record BuildPaths(string ObjWasmDir, string ObjDir, string BinDir, string BundleDir);
}
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
public class NoopNativeRebuildTest : NativeRebuildTestsBase
{
using System.IO;
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
public class ReferenceNewAssemblyRebuildTest : NativeRebuildTestsBase
{
using System.IO;
using System.Linq;
+using Wasm.Build.Tests;
using Xunit;
using Xunit.Abstractions;
#nullable enable
-namespace Wasm.Build.Tests
+namespace Wasm.Build.NativeRebuild.Tests
{
public class SimpleSourceChangeRebuildTest : NativeRebuildTestsBase
{
string id)
{
string projectName = $"sat_asm_from_main_asm";
- bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT;
+ // Release+publish defaults to native relinking
+ bool dotnetWasmFromRuntimePack = !nativeRelink && !buildArgs.AOT && buildArgs.Config != "Release";
buildArgs = buildArgs with { ProjectName = projectName };
buildArgs = ExpandBuildArgs(buildArgs,
projectTemplate: s_resourcesProjectTemplate,
- extraProperties: $"<WasmBuildNative>{(nativeRelink ? "true" : "false")}</WasmBuildNative>");
+ extraProperties: nativeRelink ? $"<WasmBuildNative>true</WasmBuildNative>" : string.Empty);
BuildProject(buildArgs,
initProject: () =>
public Dictionary<BuildArgs, BuildProduct> _buildPaths = new();
public void CacheBuild(BuildArgs buildArgs, BuildProduct product)
- => _buildPaths.Add(buildArgs, product);
+ {
+ if (product == null)
+ throw new ArgumentNullException(nameof(product));
+ if (buildArgs == null)
+ throw new ArgumentNullException(nameof(buildArgs));
+ _buildPaths.Add(buildArgs, product);
+ }
public void RemoveFromCache(string buildPath, bool keepDir=true)
{
- KeyValuePair<BuildArgs, BuildProduct>? foundKvp = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).SingleOrDefault();
- if (foundKvp == null)
- throw new Exception($"Could not find build path {buildPath} in cache to remove.");
+ BuildArgs? foundBuildArgs = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).Select(kvp => kvp.Key).SingleOrDefault();
+ if (foundBuildArgs is not null)
+ _buildPaths.Remove(foundBuildArgs);
- _buildPaths.Remove(foundKvp.Value.Key);
if (!keepDir)
RemoveDirectory(buildPath);
}
<BundleXunitRunner>true</BundleXunitRunner>
<CLRTestKind>BuildAndRun</CLRTestKind>
<TestFramework>xunit</TestFramework>
- <WasmGenerateAppBundle>false</WasmGenerateAppBundle>
<EnableDefaultItems>true</EnableDefaultItems>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<DefineConstants Condition="'$(ContinuousIntegrationBuild)' != 'true'">TEST_DEBUG_CONFIG_ALSO</DefineConstants>
<InstallWorkloadForTesting>true</InstallWorkloadForTesting>
<!-- don't run any wasm build steps -->
- <WasmBuildAppAfterThisTarget />
+ <IsWasmProject>false</IsWasmProject>
</PropertyGroup>
<PropertyGroup>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+#nullable enable
+
+namespace Wasm.Build.Tests
+{
+ public class WasmNativeDefaultsTests : BuildTestBase
+ {
+ public WasmNativeDefaultsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [Theory]
+ /* relink by default for publish+Release */
+ [InlineData("Release", "", /*aot*/ false, /*build*/ false, /*publish*/ true)]
+ /* NO relink by default for publish+Release, even when not trimming */
+ [InlineData("Release", "<PublishTrimmed>false</PublishTrimmed>", /*aot*/ false, /*build*/ false, /*publish*/ false)]
+
+ [InlineData("Debug", "", /*aot*/ false, /*build*/ false, /*publish*/ false)]
+
+ /* AOT */
+ [InlineData("Release", "", /*aot*/ true, /*build*/ false, /*publish*/ true)]
+ [InlineData("Debug", "", /*aot*/ true, /*build*/ false, /*publish*/ true)]
+ // FIXME: separate test
+ // [InlineData("Release", "<RunAOTCompilationAfterBuild>true</RunAOTCompilationAfterBuild>",
+ // /*aot*/ true, /*build*/ true, /*publish*/ true)]
+
+ /* AOT not affected by trimming */
+ [InlineData("Release", "<PublishTrimmed>false</PublishTrimmed>", /*aot*/ true, /*build*/ false, /*publish*/ true)]
+ [InlineData("Debug", "<PublishTrimmed>false</PublishTrimmed>", /*aot*/ true, /*build*/ false, /*publish*/ true)]
+ public void Defaults(string config, string extraProperties, bool aot, bool buildValue, bool publishValue)
+ {
+ string output = CheckWasmNativeDefaultValue("native_defaults_publish", config, extraProperties, aot, dotnetWasmFromRuntimePack: !publishValue);
+
+ Assert.Contains($"** WasmBuildNative: '{buildValue.ToString().ToLower()}', WasmBuildingForNestedPublish: ''", output);
+ Assert.Contains($"** WasmBuildNative: '{publishValue.ToString().ToLower()}', WasmBuildingForNestedPublish: 'true'", output);
+ Assert.Contains("Stopping the build", output);
+ }
+
+ [Theory]
+ /* always relink */
+ [InlineData("Release", "", /*build*/ true, /*publish*/ true)]
+ [InlineData("Debug", "", /*build*/ true, /*publish*/ true)]
+ [InlineData("Release", "<PublishTrimmed>false</PublishTrimmed>", /*build*/ true, /*publish*/ true)]
+ public void WithNativeReference(string config, string extraProperties, bool buildValue, bool publishValue)
+ {
+ string nativeLibPath = Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", "native-lib.o");
+ string nativeRefItem = @$"<NativeFileReference Include=""{nativeLibPath}"" />";
+ string output = CheckWasmNativeDefaultValue("native_defaults_publish",
+ config,
+ extraProperties,
+ aot: false,
+ dotnetWasmFromRuntimePack: !publishValue,
+ extraItems: nativeRefItem);
+
+ Assert.Contains($"** WasmBuildNative: '{buildValue.ToString().ToLower()}', WasmBuildingForNestedPublish: ''", output);
+ Assert.Contains($"** WasmBuildNative: '{publishValue.ToString().ToLower()}', WasmBuildingForNestedPublish: 'true'", output);
+ Assert.Contains("Stopping the build", output);
+ }
+
+ private string CheckWasmNativeDefaultValue(string projectName,
+ string config,
+ string extraProperties,
+ bool aot,
+ bool dotnetWasmFromRuntimePack,
+ string extraItems = "")
+ {
+ // builds with -O0
+ extraProperties += "<_WasmDevel>true</_WasmDevel>";
+
+ string printValueTarget = @"
+ <Target Name=""PrintWasmBuildNative"" AfterTargets=""_SetWasmBuildNativeDefaults"">
+ <Message Text=""** WasmBuildNative: '$(WasmBuildNative)', WasmBuildingForNestedPublish: '$(WasmBuildingForNestedPublish)'"" Importance=""High"" />
+ <Error Text=""Stopping the build"" Condition=""$(WasmBuildingForNestedPublish) == 'true'"" />
+ </Target>";
+
+ BuildArgs buildArgs = new(ProjectName: projectName, Config: config, AOT: aot, string.Empty, null);
+ buildArgs = ExpandBuildArgs(buildArgs,
+ extraProperties: extraProperties,
+ extraItems: extraItems,
+ insertAtEnd: printValueTarget);
+
+ (_, string output) = BuildProject(buildArgs,
+ initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
+ dotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack,
+ id: Path.GetRandomFileName(),
+ expectSuccess: false,
+ useCache: false);
+
+ return output;
+ }
+ }
+}
<!-- SDK tries to download runtime packs when RuntimeIdentifier is set, remove them from PackageDownload item. -->
<Target Name="RemoveRuntimePackFromDownloadItem"
- AfterTargets="ProcessFrameworkReferences">
+ AfterTargets="ProcessFrameworkReferences"
+ Condition="'$(WasmNativeWorkload)' == 'true'">
<ItemGroup>
<PackageDownload Remove="@(PackageDownload)"
Condition="'$(UsePackageDownload)' == 'true' and $([System.String]::Copy('%(Identity)').StartsWith('Microsoft.NETCore.App.Runtime'))" />
<!-- Use local targeting pack for NetCoreAppCurrent. -->
<Target Name="UpdateTargetingAndRuntimePack"
- AfterTargets="ResolveFrameworkReferences">
+ AfterTargets="ResolveFrameworkReferences"
+ Condition="'$(WasmNativeWorkload)' == 'true'">
<ItemGroup>
<ResolvedTargetingPack Path="$(_MicrosoftNetCoreAppRefDir.TrimEnd('/\'))"
NuGetPackageVersion="$(RuntimePackInWorkloadVersion)"
<!-- Update the local targeting pack's version as it's written into the runtimeconfig.json file to select the right framework. -->
<Target Name="UpdateRuntimeFrameworkVersion"
- AfterTargets="ResolveTargetingPackAssets">
+ AfterTargets="ResolveTargetingPackAssets"
+ Condition="'$(WasmNativeWorkload)' == 'true'">
<ItemGroup>
<RuntimeFramework Version="$(RuntimePackInWorkloadVersion)"
Condition="'%(RuntimeFramework.FrameworkName)' == 'Microsoft.NETCore.App'" />
<!-- Filter out conflicting implicit assembly references. -->
<Target Name="FilterImplicitAssemblyReferences"
- Condition="'$(DisableImplicitAssemblyReferences)' != 'true'"
+ Condition="'$(DisableImplicitAssemblyReferences)' != 'true' and '$(WasmNativeWorkload)' == 'true'"
DependsOnTargets="ResolveProjectReferences"
AfterTargets="ResolveTargetingPackAssets">
<ItemGroup>
--- /dev/null
+<Project>
+ <PropertyGroup Condition="'$(RuntimeSrcDir)' != '' and '$(WasmBuildSupportDir)' == ''">
+ <ArtifactsBinDir>$(RuntimeSrcDir)\artifacts\bin\</ArtifactsBinDir>
+ <MicrosoftNetCoreAppRuntimePackLocationToUse>$([MSBuild]::NormalizeDirectory($(ArtifactsBinDir), 'microsoft.netcore.app.runtime.browser-wasm', $(RuntimeConfig)))</MicrosoftNetCoreAppRuntimePackLocationToUse>
+ </PropertyGroup>
+
+ <PropertyGroup Condition="'$(RuntimeSrcDir)' == '' and '$(WasmBuildSupportDir)' != ''">
+ <BuildBaseDir>$(WasmBuildSupportDir)\</BuildBaseDir>
+ <MicrosoftNetCoreAppRuntimePackLocationToUse>$([MSBuild]::NormalizeDirectory($(BuildBaseDir), 'microsoft.netcore.app.runtime.browser-wasm'))</MicrosoftNetCoreAppRuntimePackLocationToUse>
+ </PropertyGroup>
+</Project>
--- /dev/null
+<Project>
+ <PropertyGroup>
+ <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\</_MicrosoftNetCoreAppRefDir>
+ </PropertyGroup>
+
+ <Target Name="PrintRuntimePackPath" BeforeTargets="Publish">
+ <Message Text="** MicrosoftNetCoreAppRuntimePackDir : %(ResolvedRuntimePack.PackageDirectory)" Importance="High" />
+ </Target>
+
+ <!-- Use local targeting pack for NetCoreAppCurrent. -->
+ <Target Name="UpdateTargetingAndRuntimePack"
+ AfterTargets="ResolveFrameworkReferences">
+ <ItemGroup>
+ <ResolvedTargetingPack Path="$(_MicrosoftNetCoreAppRefDir.TrimEnd('/\'))"
+ NuGetPackageVersion="$(RuntimePackInWorkloadVersion)"
+ PackageDirectory="$(_MicrosoftNetCoreAppRefDir.TrimEnd('/\'))"
+ Condition="'%(ResolvedTargetingPack.RuntimeFrameworkName)' == 'Microsoft.NETCore.App' and
+ Exists('$(_MicrosoftNetCoreAppRefDir)data\FrameworkList.xml')" />
+
+ <ResolvedRuntimePack
+ Update="Microsoft.NETCore.App.Runtime.Mono.browser-wasm"
+ FrameworkName="Microsoft.NETCore.App"
+ NuGetPackageId="Microsoft.NETCore.App.Runtime.Mono.browser-wasm"
+ NuGetPackageVersion="$(RuntimePackInWorkloadVersion)"
+ PackageDirectory="$(MicrosoftNetCoreAppRuntimePackLocationToUse)"
+ RuntimeIdentifier="browser-wasm" />
+
+ <ResolvedFrameworkReference Update="Microsoft.NETCore.App"
+ TargetingPackPath="$(_MicrosoftNetCoreAppRefDir.TrimEnd('/\'))"
+ RuntimePackName="Microsoft.NETCore.App.Runtime.Mono.browser-wasm"
+ RuntimePackVersion="$(RuntimePackInWorkloadVersion)"
+ RuntimePackPath="$(MicrosoftNetCoreAppRuntimePackLocationToUse)"
+ RuntimeIdentifier="browser-wasm" />
+ </ItemGroup>
+ </Target>
+</Project>
</PropertyGroup>
<Import Project="$(_WasmTargetsDir)WasmApp.LocalBuild.props" Condition="Exists('$(_WasmTargetsDir)WasmApp.LocalBuild.props')" />
-
- <PropertyGroup>
- <WasmBuildAppDependsOn>PrepareForWasmBuild;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
- </PropertyGroup>
</Project>
Text="%24(WasmMainJS) is set when %24(WasmGenerateAppBundle) is not true: it won't be used because an app bundle is not being generated. Possible build authoring error" />
</Target>
- <Target Name="PrepareForWasmBuild">
- <ItemGroup>
- <WasmAssembliesToBundle Include="$(TargetDir)publish\**\*.dll" />
- </ItemGroup>
- </Target>
-
<Target Name="PrintRuntimePackPath" BeforeTargets="Build">
<Message Text="** MicrosoftNetCoreAppRuntimePackDir : %(ResolvedRuntimePack.PackageDirectory)" Importance="High" />
</Target>
<Project>
<PropertyGroup>
- <WasmBuildAppDependsOn>PrepareForWasmBuild;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
<_MicrosoftNetCoreAppRefDir>$(AppRefDir)\</_MicrosoftNetCoreAppRefDir>
</PropertyGroup>
- <Target Name="PrepareForWasmBuild">
- <ItemGroup>
- <WasmAssembliesToBundle Include="$(TargetDir)publish\**\*.dll" />
- </ItemGroup>
- </Target>
-
<Target Name="PrintRuntimePackPath" BeforeTargets="Publish">
<Message Text="** MicrosoftNetCoreAppRuntimePackDir : %(ResolvedRuntimePack.PackageDirectory)" Importance="High" />
</Target>
<BuildDir>$(MSBuildThisFileDirectory)\obj\$(Configuration)\wasm</BuildDir>
<AppDir>$(TestBinDir)/WasmApp/</AppDir>
<NETCoreAppMaximumVersion>99.0</NETCoreAppMaximumVersion>
+ <IsWasmProject>true</IsWasmProject>
+ <WasmGenerateAppBundle>true</WasmGenerateAppBundle>
<WasmAppBuilderTasksAssemblyPath>$(CORE_ROOT)\WasmAppBuilder\WasmAppBuilder.dll</WasmAppBuilderTasksAssemblyPath>
<MonoAOTCompilerTasksAssemblyPath>$(CORE_ROOT)\MonoAOTCompiler\MonoAOTCompiler.dll</MonoAOTCompilerTasksAssemblyPath>
<JsonToItemsTaskFactoryTasksAssemblyPath>$(CORE_ROOT)\JsonToItemsTaskFactory\JsonToItemsTaskFactory.dll</JsonToItemsTaskFactoryTasksAssemblyPath>
<RuntimeConfigParserTasksAssemblyPath>$(CORE_ROOT)\RuntimeConfigParser\RuntimeConfigParser.dll</RuntimeConfigParserTasksAssemblyPath>
+ <WasmBuildAppDependsOn>BuildApp;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
</PropertyGroup>
- <Target Name="BuildApp" BeforeTargets="WasmBuildApp">
+ <Target Name="BuildApp">
<PropertyGroup>
<WasmMainAssemblyFileName>$(TestAssemblyFileName)</WasmMainAssemblyFileName>
<WasmAppDir>$(AppDir)</WasmAppDir>
<Content Include="index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
+ <WasmExtraFilesToDeploy Include="index.html" />
<ProjectReference Include="ApplyUpdateReferencedAssembly\ApplyUpdateReferencedAssembly.csproj" />
</ItemGroup>
- <Target Name="AfterWasmBuildApp" AfterTargets="WasmBuildApp">
- <Copy SourceFiles="$(OutDir)\index.html" DestinationFolder="$(WasmAppDir)" />
- </Target>
-
<Target Name="PreserveEnCAssembliesFromLinking"
Condition="'$(TargetOS)' == 'Browser' and '$(EnableAggressiveTrimming)' == 'true'"
BeforeTargets="ConfigureTrimming">
<ExpectedExitCode>42</ExpectedExitCode>
<WasmMainJSPath>runtime.js</WasmMainJSPath>
</PropertyGroup>
-
+
<ItemGroup>
<Compile Include="Program.cs" />
+ <WasmExtraFilesToDeploy Include="index.html" />
</ItemGroup>
-
- <Target Name="AfterWasmBuildApp" AfterTargets="WasmBuildApp">
- <Copy SourceFiles="$(MSBuildThisFileDirectory)\index.html" DestinationFolder="$(WasmAppDir)" />
- </Target>
-
</Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets, '$(MSBuildThisFileDirectory)..'))" />
<PropertyGroup>
- <WasmBuildAppDependsOn>PrepareForWasmBuild;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
<WasmAppDir>$(OutputPath)\$(Configuration)\AppBundle\</WasmAppDir>
</PropertyGroup>
-
- <Target Name="PrepareForWasmBuild">
- <ItemGroup>
- <WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
- </ItemGroup>
- </Target>
</Project>