未验证 提交 4af01264 编写于 作者: A Ankit Jain 提交者: GitHub

[wasm] Add support for native relinking after Build, and… (#59153)

… AOT after publish (#58913)

Forward port #58913

(cherry picked from commit f38d58f1)
上级 16a0ab39
...@@ -164,7 +164,7 @@ ...@@ -164,7 +164,7 @@
<SQLitePCLRawbundle_greenVersion>2.0.4</SQLitePCLRawbundle_greenVersion> <SQLitePCLRawbundle_greenVersion>2.0.4</SQLitePCLRawbundle_greenVersion>
<MoqVersion>4.12.0</MoqVersion> <MoqVersion>4.12.0</MoqVersion>
<FsCheckVersion>2.14.3</FsCheckVersion> <FsCheckVersion>2.14.3</FsCheckVersion>
<SdkVersionForWorkloadTesting>6.0.100-rc.2.21425.12</SdkVersionForWorkloadTesting> <SdkVersionForWorkloadTesting>6.0.100-rc.2.21463.12</SdkVersionForWorkloadTesting>
<!-- Docs --> <!-- Docs -->
<MicrosoftPrivateIntellisenseVersion>6.0.0-preview-20210916.1</MicrosoftPrivateIntellisenseVersion> <MicrosoftPrivateIntellisenseVersion>6.0.0-preview-20210916.1</MicrosoftPrivateIntellisenseVersion>
<!-- ILLink --> <!-- ILLink -->
......
BlazorWasmTests Wasm.Build.NativeRebuild.Tests.FlagsChangeRebuildTest
FlagsChangeRebuildTest Wasm.Build.NativeRebuild.Tests.NoopNativeRebuildTest
InvariantGlobalizationTests Wasm.Build.NativeRebuild.Tests.ReferenceNewAssemblyRebuildTest
LocalEMSDKTests Wasm.Build.NativeRebuild.Tests.SimpleSourceChangeRebuildTest
MainWithArgsTests Wasm.Build.Tests.BlazorWasmBuildPublishTests
NativeBuildTests Wasm.Build.Tests.BlazorWasmTests
NativeLibraryTests Wasm.Build.Tests.BuildPublishTests
NoopNativeRebuildTest Wasm.Build.Tests.CleanTests
RebuildTests Wasm.Build.Tests.InvariantGlobalizationTests
ReferenceNewAssemblyRebuildTest Wasm.Build.Tests.LocalEMSDKTests
SatelliteAssembliesTests Wasm.Build.Tests.MainWithArgsTests
SimpleSourceChangeRebuildTest Wasm.Build.Tests.NativeBuildTests
WasmBuildAppTest Wasm.Build.Tests.NativeLibraryTests
WorkloadTests Wasm.Build.Tests.RebuildTests
Wasm.Build.Tests.SatelliteAssembliesTests
Wasm.Build.Tests.WasmBuildAppTest
Wasm.Build.Tests.WasmNativeDefaultsTests
Wasm.Build.Tests.WorkloadTests
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
<PublishingTestsRun>true</PublishingTestsRun> <PublishingTestsRun>true</PublishingTestsRun>
<BundleTestAppTargets>BundleTestAppleApp;BundleTestAndroidApp</BundleTestAppTargets> <BundleTestAppTargets>BundleTestAppleApp;BundleTestAndroidApp</BundleTestAppTargets>
<PublishTestAsSelfContainedDependsOn>Publish</PublishTestAsSelfContainedDependsOn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(EnableAggressiveTrimming)' == 'true' or '$(EnableSoftTrimming)' == 'true'"> <PropertyGroup Condition="'$(EnableAggressiveTrimming)' == 'true' or '$(EnableSoftTrimming)' == 'true'">
...@@ -111,6 +112,7 @@ ...@@ -111,6 +112,7 @@
OutputType="AsmOnly" OutputType="AsmOnly"
Assemblies="@(AotInputAssemblies)" Assemblies="@(AotInputAssemblies)"
AotModulesTablePath="$(BundleDir)\modules.c" AotModulesTablePath="$(BundleDir)\modules.c"
IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(MonoEnableLLVM)" UseLLVM="$(MonoEnableLLVM)"
LLVMPath="$(MonoAotCrossDir)"> LLVMPath="$(MonoAotCrossDir)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" /> <Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
...@@ -200,6 +202,7 @@ ...@@ -200,6 +202,7 @@
Assemblies="@(AotInputAssemblies)" Assemblies="@(AotInputAssemblies)"
AotModulesTablePath="$(BundleDir)\modules.m" AotModulesTablePath="$(BundleDir)\modules.m"
AotModulesTableLanguage="ObjC" AotModulesTableLanguage="ObjC"
IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(MonoEnableLLVM)" UseLLVM="$(MonoEnableLLVM)"
LLVMPath="$(MonoAotCrossDir)"> LLVMPath="$(MonoAotCrossDir)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" /> <Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
...@@ -306,7 +309,7 @@ ...@@ -306,7 +309,7 @@
<Target Name="PublishTestAsSelfContained" <Target Name="PublishTestAsSelfContained"
Condition="'$(IsCrossTargetingBuild)' != 'true'" Condition="'$(IsCrossTargetingBuild)' != 'true'"
AfterTargets="Build" AfterTargets="Build"
DependsOnTargets="Publish;$(BundleTestAppTargets);ArchiveTests" /> DependsOnTargets="$(PublishTestAsSelfContainedDependsOn);$(BundleTestAppTargets);ArchiveTests" />
<Target Name="PrepareForTestUsingWorkloads" <Target Name="PrepareForTestUsingWorkloads"
BeforeTargets="Test" BeforeTargets="Test"
......
<Project> <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 --> <!-- 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> <PropertyGroup>
<IsWasmProject Condition="'$(IsWasmProject)' == ''">true</IsWasmProject>
<WasmGenerateAppBundle Condition="'$(WasmGenerateAppBundle)' == ''">true</WasmGenerateAppBundle>
<BundleTestAppTargets>$(BundleTestAppTargets);BundleTestWasmApp</BundleTestAppTargets> <BundleTestAppTargets>$(BundleTestAppTargets);BundleTestWasmApp</BundleTestAppTargets>
<DebuggerSupport Condition="'$(DebuggerSupport)' == '' and '$(Configuration)' == 'Debug'">true</DebuggerSupport> <DebuggerSupport Condition="'$(DebuggerSupport)' == '' and '$(Configuration)' == 'Debug'">true</DebuggerSupport>
<!-- Some tests expect to load satellite assemblies by path, eg. System.Runtime.Loader.Tests, <!-- Some tests expect to load satellite assemblies by path, eg. System.Runtime.Loader.Tests,
...@@ -55,12 +57,22 @@ ...@@ -55,12 +57,22 @@
Condition="'$(BuildAOTTestsOn)' == 'local'" /> Condition="'$(BuildAOTTestsOn)' == 'local'" />
<PropertyGroup> <PropertyGroup>
<WasmBuildAppDependsOn>PrepareForWasmBuildApp;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn> <BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOn)' == 'local'">WasmTriggerPublishApp</BundleTestWasmAppDependsOn>
<BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOn)' == 'local'">WasmBuildApp</BundleTestWasmAppDependsOn>
<BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOnHelix)' == 'true'">$(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix</BundleTestWasmAppDependsOn> <BundleTestWasmAppDependsOn Condition="'$(BuildAOTTestsOnHelix)' == 'true'">$(BundleTestWasmAppDependsOn);_BundleAOTTestWasmAppForHelix</BundleTestWasmAppDependsOn>
</PropertyGroup> </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>
<!-- wasm's publish targets will trigger publish, so we shouldn't do that -->
<PublishTestAsSelfContainedDependsOn />
<WasmNestedPublishAppDependsOn>PrepareForWasmBuildApp;$(WasmNestedPublishAppDependsOn)</WasmNestedPublishAppDependsOn>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<WorkloadIdForTesting Include="wasm-tools" <WorkloadIdForTesting Include="wasm-tools"
ManifestName="Microsoft.NET.Workload.Mono.ToolChain" ManifestName="Microsoft.NET.Workload.Mono.ToolChain"
...@@ -80,6 +92,8 @@ ...@@ -80,6 +92,8 @@
<RuntimeConfigFilePath>$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))</RuntimeConfigFilePath> <RuntimeConfigFilePath>$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))</RuntimeConfigFilePath>
</PropertyGroup> </PropertyGroup>
<Error Text="Item WasmAssembliesToBundle is empty. This is likely an authoring error." Condition="@(WasmAssembliesToBundle->Count()) == 0" />
<ItemGroup> <ItemGroup>
<BundleFiles Include="$(WasmMainJSPath)" TargetDir="publish" /> <BundleFiles Include="$(WasmMainJSPath)" TargetDir="publish" />
<BundleFiles Include="@(WasmAssembliesToBundle)" TargetDir="publish\%(WasmAssembliesToBundle.RecursiveDir)" /> <BundleFiles Include="@(WasmAssembliesToBundle)" TargetDir="publish\%(WasmAssembliesToBundle.RecursiveDir)" />
...@@ -154,7 +168,7 @@ ...@@ -154,7 +168,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<WasmAssembliesToBundle Include="$(PublishDir)\**\*.dll"/> <WasmAssembliesToBundle Include="$(PublishDir)\**\*.dll" Condition="'$(BuildAOTTestsOnHelix)' == 'true'" />
<WasmFilesToIncludeInFileSystem Include="@(ContentWithTargetPath)" /> <WasmFilesToIncludeInFileSystem Include="@(ContentWithTargetPath)" />
<_CopyLocalPaths <_CopyLocalPaths
......
...@@ -426,10 +426,10 @@ ...@@ -426,10 +426,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(Scenario)' == 'BuildWasmApps'"> <ItemGroup Condition="'$(Scenario)' == 'BuildWasmApps'">
<HelixWorkItem Include="@(BuildWasmApps_PerJobList->'$(WorkItemPrefix)%(FileName)')"> <HelixWorkItem Include="@(BuildWasmApps_PerJobList->'$(WorkItemPrefix)%(Extension)')">
<PayloadArchive>$(_BuildWasmAppsPayloadArchive)</PayloadArchive> <PayloadArchive>$(_BuildWasmAppsPayloadArchive)</PayloadArchive>
<PreCommands Condition="'$(OS)' == 'Windows_NT'">set &quot;HELIX_XUNIT_ARGS=-class Wasm.Build.Tests.%(Identity)&quot;</PreCommands> <PreCommands Condition="'$(OS)' == 'Windows_NT'">set &quot;HELIX_XUNIT_ARGS=-class %(Identity)&quot;</PreCommands>
<PreCommands Condition="'$(OS)' != 'Windows_NT'">export &quot;HELIX_XUNIT_ARGS=-class Wasm.Build.Tests.%(Identity)&quot;</PreCommands> <PreCommands Condition="'$(OS)' != 'Windows_NT'">export &quot;HELIX_XUNIT_ARGS=-class %(Identity)&quot;</PreCommands>
<Command>$(HelixCommand)</Command> <Command>$(HelixCommand)</Command>
<Timeout>$(_workItemTimeout)</Timeout> <Timeout>$(_workItemTimeout)</Timeout>
</HelixWorkItem> </HelixWorkItem>
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<Error Text="%24(SdkWithWorkloadForTestingPath) is not set" Condition="'$(SdkWithWorkloadForTestingPath)' == ''" /> <Error Text="%24(SdkWithWorkloadForTestingPath) is not set" Condition="'$(SdkWithWorkloadForTestingPath)' == ''" />
<Error Text="%24(SdkVersionForWorkloadTesting) is not set" Condition="'$(SdkVersionForWorkloadTesting)' == ''" /> <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)" /> <RemoveDir Directories="$(SdkWithWorkloadForTestingPath)" />
<MakeDir Directories="$(SdkWithWorkloadForTestingPath)" /> <MakeDir Directories="$(SdkWithWorkloadForTestingPath)" />
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
</ItemGroup> </ItemGroup>
<Copy SourceFiles="@(_SourceFiles)" DestinationFolder="$(SdkWithWorkloadForTestingPath)\%(_SourceFiles.RecursiveDir)" /> <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" /> <WriteLinesToFile File="$(SdkWithWorkloadStampPath)" Lines="" Overwrite="true" />
</Target> </Target>
...@@ -23,7 +25,7 @@ ...@@ -23,7 +25,7 @@
<Error Text="%24(SdkWithNoWorkloadForTestingPath) is not set" Condition="'$(SdkWithNoWorkloadForTestingPath)' == ''" /> <Error Text="%24(SdkWithNoWorkloadForTestingPath) is not set" Condition="'$(SdkWithNoWorkloadForTestingPath)' == ''" />
<Error Text="%24(SdkVersionForWorkloadTesting) is not set" Condition="'$(SdkVersionForWorkloadTesting)' == ''" /> <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)" /> <RemoveDir Directories="$(SdkWithNoWorkloadForTestingPath)" />
<MakeDir Directories="$(SdkWithNoWorkloadForTestingPath)" /> <MakeDir Directories="$(SdkWithNoWorkloadForTestingPath)" />
...@@ -39,6 +41,7 @@ ...@@ -39,6 +41,7 @@
<Exec Condition="$([MSBuild]::IsOSPlatform('windows'))" <Exec Condition="$([MSBuild]::IsOSPlatform('windows'))"
Command='powershell -ExecutionPolicy ByPass -NoProfile -command "&amp; $(_DotNetInstallScriptPath) -InstallDir $(SdkWithNoWorkloadForTestingPath) -Version $(SdkVersionForWorkloadTesting)"' /> Command='powershell -ExecutionPolicy ByPass -NoProfile -command "&amp; $(_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" /> <WriteLinesToFile File="$(SdkWithNoWorkloadStampPath)" Lines="" Overwrite="true" />
</Target> </Target>
......
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
AotModulesTablePath="$(_AotModulesTablePath)" AotModulesTablePath="$(_AotModulesTablePath)"
ToolPrefix="$(_AotToolPrefix)" ToolPrefix="$(_AotToolPrefix)"
LibraryFormat="$(_AotLibraryFormat)" LibraryFormat="$(_AotLibraryFormat)"
IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(UseLLVM)" UseLLVM="$(UseLLVM)"
LLVMPath="$(MonoAotCrossDir)"> LLVMPath="$(MonoAotCrossDir)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" /> <Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
......
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
AotModulesTablePath="$(AppDir)\modules.m" AotModulesTablePath="$(AppDir)\modules.m"
AotModulesTableLanguage="ObjC" AotModulesTableLanguage="ObjC"
OutputDir="$(PublishDir)" OutputDir="$(PublishDir)"
IntermediateOutputPath="$(IntermediateOutputPath)"
UseLLVM="$(UseLLVM)" UseLLVM="$(UseLLVM)"
LLVMPath="$(MonoAotCrossDir)"> LLVMPath="$(MonoAotCrossDir)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" /> <Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
<Target Name="PrepareDeltasForWasmApp" DependsOnTargets="Build;CompileDiff;ComputeDeltaFileOutputNames"> <Target Name="PrepareDeltasForWasmApp" DependsOnTargets="Build;CompileDiff;ComputeDeltaFileOutputNames">
<ItemGroup> <ItemGroup>
<WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
<WasmFilesToIncludeInFileSystem Include="@(_DeltaFileForPublish)"> <WasmFilesToIncludeInFileSystem Include="@(_DeltaFileForPublish)">
<TargetPath>\%(_DeltaFileForPublish.Filename)%(_DeltaFileForPublish.Extension)</TargetPath> <TargetPath>\%(_DeltaFileForPublish.Filename)%(_DeltaFileForPublish.Extension)</TargetPath>
</WasmFilesToIncludeInFileSystem> </WasmFilesToIncludeInFileSystem>
......
<Project> <Project>
<Import Project="../Directory.Build.targets" /> <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" /> <Import Project="$(MonoProjectRoot)\wasm\build\WasmApp.InTree.targets" />
<Target Name="BuildSampleInTree" <Target Name="BuildSampleInTree"
......
...@@ -10,9 +10,12 @@ CONFIG?=Release ...@@ -10,9 +10,12 @@ CONFIG?=Release
WASM_DEFAULT_BUILD_ARGS?=/p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG) WASM_DEFAULT_BUILD_ARGS?=/p:TargetArchitecture=wasm /p:TargetOS=Browser /p:Configuration=$(CONFIG)
all: build all: publish
build: 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) EMSDK_PATH=$(realpath $(TOP)/src/mono/wasm/emsdk) $(DOTNET) publish $(DOTNET_Q_ARGS) $(WASM_DEFAULT_BUILD_ARGS) $(MSBUILD_ARGS) $(PROJECT_NAME)
clean: clean:
......
此差异已折叠。
# 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}` # `WasmApp.{props,targets}`, and `WasmApp.InTree.{props,targets}`
- Any project that wants to use this, can import the props+targets, and set up the - Any project that wants to use this, can import the props+targets, and set up the
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
Condition="'$(_LocalMicrosoftNetCoreAppRuntimePackDir)' != '' and Condition="'$(_LocalMicrosoftNetCoreAppRuntimePackDir)' != '' and
'%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App'" /> '%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App'" />
</ItemGroup> </ItemGroup>
<Message Text="Used runtime pack: %(ResolvedRuntimePack.PackageDirectory)" Importance="high" /> <Message Text="Used runtime pack: %(ResolvedRuntimePack.PackageDirectory) for $(MSBuildProjectName)" Importance="normal" />
</Target> </Target>
<Target Name="RebuildWasmAppBuilder"> <Target Name="RebuildWasmAppBuilder">
...@@ -35,9 +35,9 @@ ...@@ -35,9 +35,9 @@
</Target> </Target>
<Target Name="CopyAppZipToHelixTestDir" <Target Name="CopyAppZipToHelixTestDir"
Condition="'$(WasmCopyAppZipToHelixTestDir)' == 'true'" Condition="'$(WasmCopyAppZipToHelixTestDir)' == 'true' and '$(WasmBuildingForNestedPublish)' != 'true'"
AfterTargets="Build" AfterTargets="Build"
DependsOnTargets="Publish"> DependsOnTargets="WasmTriggerPublishApp">
<PropertyGroup> <PropertyGroup>
<WasmHelixTestAppRelativeDir Condition="'$(WasmHelixTestAppRelativeDir)' == ''">$(MSBuildProjectName)</WasmHelixTestAppRelativeDir> <WasmHelixTestAppRelativeDir Condition="'$(WasmHelixTestAppRelativeDir)' == ''">$(MSBuildProjectName)</WasmHelixTestAppRelativeDir>
<!-- Helix properties --> <!-- Helix properties -->
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
<UsingTask TaskName="RuntimeConfigParserTask" AssemblyFile="$(RuntimeConfigParserTasksAssemblyPath)" /> <UsingTask TaskName="RuntimeConfigParserTask" AssemblyFile="$(RuntimeConfigParserTasksAssemblyPath)" />
<PropertyGroup> <PropertyGroup>
<PublishTrimmed>true</PublishTrimmed> <PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
<TrimMode>link</TrimMode> <TrimMode>link</TrimMode>
</PropertyGroup> </PropertyGroup>
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
Condition="'$(MicrosoftNetCoreAppRuntimePackLocationToUse)' != '' and Condition="'$(MicrosoftNetCoreAppRuntimePackLocationToUse)' != '' and
'%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App'" /> '%(ResolvedRuntimePack.FrameworkName)' == 'Microsoft.NETCore.App'" />
</ItemGroup> </ItemGroup>
<Message Text="Used runtime pack: %(ResolvedRuntimePack.PackageDirectory)" Importance="high" /> <Message Text="Used runtime pack: %(ResolvedRuntimePack.PackageDirectory) for $(MSBuildProjectName)" Importance="normal" />
</Target> </Target>
<!-- the actual properties need to get set in the props, so because UsingTasks depend on those. --> <!-- the actual properties need to get set in the props, so because UsingTasks depend on those. -->
......
...@@ -17,12 +17,6 @@ ...@@ -17,12 +17,6 @@
_CompleteWasmBuildNative _CompleteWasmBuildNative
</_WasmBuildNativeCoreDependsOn> </_WasmBuildNativeCoreDependsOn>
<WasmBuildNativeOnlyDependsOn>
_InitializeCommonProperties;
_PrepareForWasmBuildNativeOnly;
_WasmBuildNativeCore;
</WasmBuildNativeOnlyDependsOn>
<_BeforeWasmBuildAppDependsOn> <_BeforeWasmBuildAppDependsOn>
$(_BeforeWasmBuildAppDependsOn); $(_BeforeWasmBuildAppDependsOn);
_SetupEmscripten; _SetupEmscripten;
...@@ -39,16 +33,6 @@ ...@@ -39,16 +33,6 @@
<Import Project="$(MSBuildThisFileDirectory)EmSdkRepo.Defaults.props" Condition="'$(WasmUseEMSDK_PATH)' == 'true'" /> <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"> <Target Name="_SetupEmscripten">
<PropertyGroup> <PropertyGroup>
<_EMSDKMissingPaths Condition="'$(_EMSDKMissingPaths)' == '' and ('$(EmscriptenSdkToolsPath)' == '' or !Exists('$(EmscriptenSdkToolsPath)'))">%24(EmscriptenSdkToolsPath)=$(EmscriptenSdkToolsPath) </_EMSDKMissingPaths> <_EMSDKMissingPaths Condition="'$(_EMSDKMissingPaths)' == '' and ('$(EmscriptenSdkToolsPath)' == '' or !Exists('$(EmscriptenSdkToolsPath)'))">%24(EmscriptenSdkToolsPath)=$(EmscriptenSdkToolsPath) </_EMSDKMissingPaths>
...@@ -117,11 +101,27 @@ ...@@ -117,11 +101,27 @@
<Error Condition="'$(RunAOTCompilation)' == 'true' and '$(_IsEMSDKMissing)' == 'true'" <Error Condition="'$(RunAOTCompilation)' == 'true' and '$(_IsEMSDKMissing)' == 'true'"
Text="$(_EMSDKMissingErrorMessage) Emscripten SDK is required for AOT'ing assemblies." /> 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="'$(RunAOTCompilation)' == 'true'">true</WasmBuildNative>
<WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >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> <WasmBuildNative Condition="'$(WasmBuildNative)' == ''">false</WasmBuildNative>
</PropertyGroup> </PropertyGroup>
...@@ -152,7 +152,7 @@ ...@@ -152,7 +152,7 @@
<_WasmPInvokeHPath>$(_WasmRuntimePackIncludeDir)wasm\pinvoke.h</_WasmPInvokeHPath> <_WasmPInvokeHPath>$(_WasmRuntimePackIncludeDir)wasm\pinvoke.h</_WasmPInvokeHPath>
<_DriverGenCPath>$(_WasmIntermediateOutputPath)driver-gen.c</_DriverGenCPath> <_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> <_EmccAssertionLevelDefault>0</_EmccAssertionLevelDefault>
<_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault)</_EmccOptimizationFlagDefault> <_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault)</_EmccOptimizationFlagDefault>
...@@ -190,8 +190,8 @@ ...@@ -190,8 +190,8 @@
<_EmccCFlags Include="$(EmccCompileOptimizationFlag)" /> <_EmccCFlags Include="$(EmccCompileOptimizationFlag)" />
<_EmccCFlags Include="@(_EmccCommonFlags)" /> <_EmccCFlags Include="@(_EmccCommonFlags)" />
<_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(RunAOTCompilation)' == 'true'" /> <_EmccCFlags Include="-DENABLE_AOT=1" Condition="'$(_WasmShouldAOT)' == 'true'" />
<_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(RunAOTCompilation)' == 'true'" /> <_EmccCFlags Include="-DDRIVER_GEN=1" Condition="'$(_WasmShouldAOT)' == 'true'" />
<_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" /> <_EmccCFlags Include="-DINVARIANT_GLOBALIZATION=1" Condition="'$(InvariantGlobalization)' == 'true'" />
<_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" /> <_EmccCFlags Include="-DLINK_ICALLS=1" Condition="'$(WasmLinkIcalls)' == 'true'" />
<_EmccCFlags Include="-DCORE_BINDINGS" /> <_EmccCFlags Include="-DCORE_BINDINGS" />
...@@ -236,7 +236,9 @@ ...@@ -236,7 +236,9 @@
<PInvokeTableGenerator <PInvokeTableGenerator
Modules="@(_WasmPInvokeModules)" Modules="@(_WasmPInvokeModules)"
Assemblies="@(_WasmAssembliesInternal)" Assemblies="@(_WasmAssembliesInternal)"
OutputPath="$(_WasmPInvokeTablePath)" /> OutputPath="$(_WasmPInvokeTablePath)">
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
</PInvokeTableGenerator>
</Target> </Target>
<Target Name="_GenerateICallTable" Condition="'$(WasmLinkIcalls)' == 'true'"> <Target Name="_GenerateICallTable" Condition="'$(WasmLinkIcalls)' == 'true'">
...@@ -244,10 +246,16 @@ ...@@ -244,10 +246,16 @@
Text="Could not find AOT cross compiler at %24(_MonoAotCrossCompilerPath)=$(_MonoAotCrossCompilerPath)" /> Text="Could not find AOT cross compiler at %24(_MonoAotCrossCompilerPath)=$(_MonoAotCrossCompilerPath)" />
<Exec Command='"$(_MonoAotCrossCompilerPath)" --print-icall-table > "$(_WasmRuntimeICallTablePath)"' /> <Exec Command='"$(_MonoAotCrossCompilerPath)" --print-icall-table > "$(_WasmRuntimeICallTablePath)"' />
<ItemGroup>
<FileWrites Include="$(_WasmRuntimeICallTablePath)" />
</ItemGroup>
<IcallTableGenerator <IcallTableGenerator
RuntimeIcallTableFile="$(_WasmRuntimeICallTablePath)" RuntimeIcallTableFile="$(_WasmRuntimeICallTablePath)"
Assemblies="@(_WasmAssembliesInternal)" Assemblies="@(_WasmAssembliesInternal)"
OutputPath="$(_WasmICallTablePath)" /> OutputPath="$(_WasmICallTablePath)">
<Output TaskParameter="FileWrites" ItemName="FileWrites" />
</IcallTableGenerator>
</Target> </Target>
<Target Name="_WasmSelectRuntimeComponentsForLinking" Condition="'$(WasmNativeWorkload)' == true" DependsOnTargets="_MonoSelectRuntimeComponents" /> <Target Name="_WasmSelectRuntimeComponentsForLinking" Condition="'$(WasmNativeWorkload)' == true" DependsOnTargets="_MonoSelectRuntimeComponents" />
...@@ -263,6 +271,9 @@ ...@@ -263,6 +271,9 @@
</ItemGroup> </ItemGroup>
<WriteLinesToFile Lines="@(_EmccCFlags)" File="$(_EmccCompileRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" /> <WriteLinesToFile Lines="@(_EmccCFlags)" File="$(_EmccCompileRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(_EmccCompileRsp)" />
</ItemGroup>
<!-- warm up the cache --> <!-- warm up the cache -->
<Exec Command="$(_EmBuilder) build MINIMAL" EnvironmentVariables="@(EmscriptenEnvVars)" StandardOutputImportance="Low" StandardErrorImportance="Low" /> <Exec Command="$(_EmBuilder) build MINIMAL" EnvironmentVariables="@(EmscriptenEnvVars)" StandardOutputImportance="Low" StandardErrorImportance="Low" />
...@@ -276,13 +287,15 @@ ...@@ -276,13 +287,15 @@
SourceFiles="@(_WasmSourceFileToCompile)" SourceFiles="@(_WasmSourceFileToCompile)"
Arguments='"@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileRsp)"' Arguments='"@$(_EmccDefaultFlagsRsp)" "@$(_EmccCompileRsp)"'
EnvironmentVariables="@(EmscriptenEnvVars)" EnvironmentVariables="@(EmscriptenEnvVars)"
OutputMessageImportance="$(_EmccCompileOutputMessageImportance)" /> OutputMessageImportance="$(_EmccCompileOutputMessageImportance)">
<Output TaskParameter="OutputFiles" ItemName="FileWrites" />
</EmccCompile>
</Target> </Target>
<Target Name="_WasmCompileAssemblyBitCodeFilesForAOT" <Target Name="_WasmCompileAssemblyBitCodeFilesForAOT"
Inputs="@(_BitcodeFile);$(_EmccDefaultFlagsRsp);$(_EmccCompileBitcodeRsp)" Inputs="@(_BitcodeFile);$(_EmccDefaultFlagsRsp);$(_EmccCompileBitcodeRsp)"
Outputs="@(_BitcodeFile->'%(ObjectFile)')" Outputs="@(_BitcodeFile->'%(ObjectFile)')"
Condition="'$(RunAOTCompilation)' == 'true' and @(_BitcodeFile->Count()) > 0" Condition="'$(_WasmShouldAOT)' == 'true' and @(_BitcodeFile->Count()) > 0"
DependsOnTargets="_WasmWriteRspForCompilingBitcode"> DependsOnTargets="_WasmWriteRspForCompilingBitcode">
<ItemGroup> <ItemGroup>
...@@ -294,7 +307,9 @@ ...@@ -294,7 +307,9 @@
SourceFiles="@(_BitCodeFile)" SourceFiles="@(_BitCodeFile)"
Arguments="&quot;@$(_EmccDefaultFlagsRsp)&quot; &quot;@$(_EmccCompileBitcodeRsp)&quot;" Arguments="&quot;@$(_EmccDefaultFlagsRsp)&quot; &quot;@$(_EmccCompileBitcodeRsp)&quot;"
EnvironmentVariables="@(EmscriptenEnvVars)" EnvironmentVariables="@(EmscriptenEnvVars)"
OutputMessageImportance="$(_EmccCompileOutputMessageImportance)" /> OutputMessageImportance="$(_EmccCompileOutputMessageImportance)">
<Output TaskParameter="OutputFiles" ItemName="FileWrites" />
</EmccCompile>
</Target> </Target>
<Target Name="_WasmWriteRspForCompilingBitcode"> <Target Name="_WasmWriteRspForCompilingBitcode">
...@@ -303,6 +318,9 @@ ...@@ -303,6 +318,9 @@
<_BitcodeLDFlags Include="$(EmccExtraBitcodeLDFlags)" /> <_BitcodeLDFlags Include="$(EmccExtraBitcodeLDFlags)" />
</ItemGroup> </ItemGroup>
<WriteLinesToFile Lines="@(_BitcodeLDFlags)" File="$(_EmccCompileBitcodeRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" /> <WriteLinesToFile Lines="@(_BitcodeLDFlags)" File="$(_EmccCompileBitcodeRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(_EmccCompileBitcodeRsp)" />
</ItemGroup>
</Target> </Target>
<Target Name="_WasmWriteRspFilesForLinking"> <Target Name="_WasmWriteRspFilesForLinking">
...@@ -312,8 +330,8 @@ ...@@ -312,8 +330,8 @@
<_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" /> <_WasmNativeFileForLinking Include="%(_WasmSourceFileToCompile.ObjectFile)" />
<_WasmNativeFileForLinking <_WasmNativeFileForLinking
Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\*.a" Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)*.a"
Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)\%(Identity)')" /> Exclude="@(_MonoRuntimeComponentDontLink->'$(MicrosoftNetCoreAppRuntimePackRidNativeDir)%(Identity)')" />
<_WasmExtraJSFile Include="@(Content)" Condition="'%(Content.Extension)' == '.js'" /> <_WasmExtraJSFile Include="@(Content)" Condition="'%(Content.Extension)' == '.js'" />
...@@ -336,6 +354,9 @@ ...@@ -336,6 +354,9 @@
</ItemGroup> </ItemGroup>
<WriteLinesToFile Lines="@(_EmccLinkStepArgs)" File="$(_EmccLinkRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" /> <WriteLinesToFile Lines="@(_EmccLinkStepArgs)" File="$(_EmccLinkRsp)" Overwrite="true" WriteOnlyWhenDifferent="true" />
<ItemGroup>
<FileWrites Include="$(_EmccLinkRsp)" />
</ItemGroup>
</Target> </Target>
<Target Name="_WasmLinkDotNet" <Target Name="_WasmLinkDotNet"
...@@ -346,6 +367,10 @@ ...@@ -346,6 +367,10 @@
<Message Text="Linking with emcc. This may take a while ..." Importance="High" /> <Message Text="Linking with emcc. This may take a while ..." Importance="High" />
<Message Text="Running emcc with @(_EmccLinkStepArgs->'%(Identity)', ' ')" Importance="Low" /> <Message Text="Running emcc with @(_EmccLinkStepArgs->'%(Identity)', ' ')" Importance="Low" />
<Exec Command='emcc "@$(_EmccDefaultFlagsRsp)" "@$(_EmccLinkRsp)"' EnvironmentVariables="@(EmscriptenEnvVars)" /> <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" /> <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)" /> <Exec Command='wasm-opt$(_ExeExt) --strip-dwarf "$(_WasmIntermediateOutputPath)dotnet.wasm" -o "$(_WasmIntermediateOutputPath)dotnet.wasm"' Condition="'$(WasmNativeStrip)' == 'true'" IgnoreStandardErrorWarningFormat="true" EnvironmentVariables="@(EmscriptenEnvVars)" />
...@@ -358,7 +383,7 @@ ...@@ -358,7 +383,7 @@
</ItemGroup> </ItemGroup>
</Target> </Target>
<Target Name="_GenerateDriverGenC" Condition="'$(RunAOTCompilation)' != 'true' and '$(WasmProfilers)' != ''"> <Target Name="_GenerateDriverGenC" Condition="'$(_WasmShouldAOT)' != 'true' and '$(WasmProfilers)' != ''">
<PropertyGroup> <PropertyGroup>
<EmccExtraCFlags>$(EmccExtraCFlags) -DDRIVER_GEN=1</EmccExtraCFlags> <EmccExtraCFlags>$(EmccExtraCFlags) -DDRIVER_GEN=1</EmccExtraCFlags>
<_DriverGenCNeeded>true</_DriverGenCNeeded> <_DriverGenCNeeded>true</_DriverGenCNeeded>
...@@ -414,7 +439,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ ...@@ -414,7 +439,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
******************************* *******************************
--> -->
<Target Name="_WasmAotCompileApp" Condition="'$(RunAOTCompilation)' == 'true'"> <Target Name="_WasmAotCompileApp" Condition="'$(_WasmShouldAOT)' == 'true'">
<PropertyGroup> <PropertyGroup>
<!-- FIXME: do it once --> <!-- FIXME: do it once -->
<_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm'))</_MonoAotCrossCompilerPath> <_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm'))</_MonoAotCrossCompilerPath>
...@@ -433,13 +458,11 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ ...@@ -433,13 +458,11 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
<MonoAOTCompilerDefaultAotArguments Include="deterministic" /> <MonoAOTCompilerDefaultAotArguments Include="deterministic" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<_AotInputAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'"> <_AotInputAssemblies Include="@(_WasmAssembliesInternal)">
<AotArguments>@(MonoAOTCompilerDefaultAotArguments, ';')</AotArguments> <AotArguments>@(MonoAOTCompilerDefaultAotArguments, ';')</AotArguments>
<ProcessArguments>@(MonoAOTCompilerDefaultProcessArguments, ';')</ProcessArguments> <ProcessArguments>@(MonoAOTCompilerDefaultProcessArguments, ';')</ProcessArguments>
</_AotInputAssemblies> </_AotInputAssemblies>
<_AOT_InternalForceInterpretAssemblies Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
<_WasmAOTSearchPaths Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)" /> <_WasmAOTSearchPaths Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)" />
...@@ -452,16 +475,13 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ ...@@ -452,16 +475,13 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
<_AOTCompilerCacheFile>$(_WasmIntermediateOutputPath)aot_compiler_cache.json</_AOTCompilerCacheFile> <_AOTCompilerCacheFile>$(_WasmIntermediateOutputPath)aot_compiler_cache.json</_AOTCompilerCacheFile>
</PropertyGroup> </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" /> <Message Text="AOT'ing @(_AotInputAssemblies->Count()) assemblies" Importance="High" />
<!-- Dedup --> <!-- Dedup -->
<PropertyGroup Condition="'$(WasmDedup)' == 'true'"> <PropertyGroup Condition="'$(WasmDedup)' == 'true'">
<_WasmDedupAssembly>$(_WasmIntermediateOutputPath)\aot-instances.dll</_WasmDedupAssembly> <_WasmDedupAssembly>$(_WasmIntermediateOutputPath)\aot-instances.dll</_WasmDedupAssembly>
</PropertyGroup> </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 <Csc
Condition="'$(WasmDedup)' == 'true'" Condition="'$(WasmDedup)' == 'true'"
Sources="$(_WasmIntermediateOutputPath)\aot-instances.cs" Sources="$(_WasmIntermediateOutputPath)\aot-instances.cs"
...@@ -494,34 +514,30 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ ...@@ -494,34 +514,30 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
DedupAssembly="$(_WasmDedupAssembly)" DedupAssembly="$(_WasmDedupAssembly)"
CacheFilePath="$(_AOTCompilerCacheFile)" CacheFilePath="$(_AOTCompilerCacheFile)"
LLVMDebug="dwarfdebug" LLVMDebug="dwarfdebug"
LLVMPath="$(EmscriptenUpstreamBinPath)" > LLVMPath="$(EmscriptenUpstreamBinPath)"
IntermediateOutputPath="$(_WasmIntermediateOutputPath)">
<Output TaskParameter="CompiledAssemblies" ItemName="_WasmAssembliesInternal" /> <Output TaskParameter="CompiledAssemblies" ItemName="_WasmAssembliesInternal" />
<Output TaskParameter="FileWrites" ItemName="FileWrites" /> <Output TaskParameter="FileWrites" ItemName="FileWrites" />
</MonoAOTCompiler> </MonoAOTCompiler>
<ItemGroup> <ItemGroup>
<!-- Add back the interpreter-only assemblies -->
<_WasmAssembliesInternal Include="@(_AOT_InternalForceInterpretAssemblies)" />
<_AOTAssemblies Include="@(_WasmAssembliesInternal)" Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" />
<_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" /> <_BitcodeFile Include="%(_WasmAssembliesInternal.LlvmBitcodeFile)" />
<_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> <_BitcodeFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" />
</ItemGroup> </ItemGroup>
</Target> </Target>
<!-- '$(ArchiveTests)' != 'true' is to skip on CI for now --> <!-- '$(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> <PropertyGroup>
<_WasmStrippedAssembliesPath>$([MSBuild]::NormalizeDirectory($(_WasmIntermediateOutputPath), 'stripped-assemblies'))</_WasmStrippedAssembliesPath> <_WasmStrippedAssembliesPath>$([MSBuild]::NormalizeDirectory($(_WasmIntermediateOutputPath), 'stripped-assemblies'))</_WasmStrippedAssembliesPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<_AOTedAssemblies Include="@(_WasmAssembliesInternal)" />
<_WasmStrippedAssemblies <_WasmStrippedAssemblies
Condition="'%(_WasmAssembliesInternal._InternalForceInterpret)' != 'true'" Include="@(_AOTedAssemblies)"
Include="@(_WasmAssembliesInternal->'$(_WasmStrippedAssembliesPath)%(FileName)%(Extension)')"
OriginalPath="%(_WasmAssembliesInternal.Identity)" /> OriginalPath="%(_WasmAssembliesInternal.Identity)" />
<_WasmInterpOnlyAssembly Include="@(_WasmAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" />
</ItemGroup> </ItemGroup>
<!-- Run mono-cil-strip on the assemblies --> <!-- Run mono-cil-strip on the assemblies -->
...@@ -531,7 +547,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_ ...@@ -531,7 +547,7 @@ EMSCRIPTEN_KEEPALIVE void mono_wasm_load_profiler_aot (const char *desc) { mono_
<ItemGroup> <ItemGroup>
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" /> <_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
<_WasmAssembliesInternal Include="@(_WasmStrippedAssemblies);@(_WasmInterpOnlyAssembly)" /> <_WasmAssembliesInternal Include="@(_WasmStrippedAssemblies)" />
</ItemGroup> </ItemGroup>
</Target> </Target>
......
...@@ -5,8 +5,7 @@ ...@@ -5,8 +5,7 @@
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier> <RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
<UseMonoRuntime>true</UseMonoRuntime> <UseMonoRuntime>true</UseMonoRuntime>
<WasmBuildAppAfterThisTarget Condition="'$(WasmBuildAppAfterThisTarget)' == ''">Publish</WasmBuildAppAfterThisTarget> <_WasmBuildCoreDependsOn>
<WasmBuildAppDependsOn>
_InitializeCommonProperties; _InitializeCommonProperties;
_BeforeWasmBuildApp; _BeforeWasmBuildApp;
_WasmResolveReferences; _WasmResolveReferences;
...@@ -15,6 +14,16 @@ ...@@ -15,6 +14,16 @@
_WasmBuildNativeCore; _WasmBuildNativeCore;
_WasmGenerateAppBundle; _WasmGenerateAppBundle;
_AfterWasmBuildApp _AfterWasmBuildApp
</_WasmBuildCoreDependsOn>
<WasmBuildAppDependsOn>
_PrepareForAfterBuild;
$(_WasmBuildCoreDependsOn)
</WasmBuildAppDependsOn> </WasmBuildAppDependsOn>
<WasmNestedPublishAppDependsOn>
_PrepareForNestedPublish;
$(_WasmBuildCoreDependsOn)
</WasmNestedPublishAppDependsOn>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
...@@ -5,9 +5,6 @@ ...@@ -5,9 +5,6 @@
<!-- <!--
Required public items/properties: Required public items/properties:
- $(WasmMainJSPath) - $(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. - $(EMSDK_PATH) - points to the emscripten sdk location.
...@@ -45,9 +42,6 @@ ...@@ -45,9 +42,6 @@
- $(WasmStripAOTAssemblies) - Whether to run `mono-cil-strip` on the assemblies. - $(WasmStripAOTAssemblies) - Whether to run `mono-cil-strip` on the assemblies.
Always set to false! 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. - $(EmccVerbose) - Set to false to disable verbose emcc output.
- $(EmccLinkOptimizationFlag) - Optimization flag to use for the link step - $(EmccLinkOptimizationFlag) - Optimization flag to use for the link step
...@@ -58,6 +52,15 @@ ...@@ -58,6 +52,15 @@
- $(EmccExtraCFlags) - Extra emcc flags for compiling native files - $(EmccExtraCFlags) - Extra emcc flags for compiling native files
- $(EmccTotalMemory) - Total memory specified with `emcc`. Default value: 536870912 - $(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: Public items:
- @(WasmExtraFilesToDeploy) - Files to copy to $(WasmAppDir). - @(WasmExtraFilesToDeploy) - Files to copy to $(WasmAppDir).
...@@ -83,14 +86,76 @@ ...@@ -83,14 +86,76 @@
<WasmStripAOTAssemblies>false</WasmStripAOTAssemblies> <WasmStripAOTAssemblies>false</WasmStripAOTAssemblies>
<_BeforeWasmBuildAppDependsOn /> <_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> </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 --> <PropertyGroup Condition="'$(_WasmRuntimeConfigFilePath)' == ''">
<Target Name="WasmBuildApp" AfterTargets="$(WasmBuildAppAfterThisTarget)" /> <_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"> <Target Name="_InitializeCommonProperties">
<Error Condition="'$(MicrosoftNetCoreAppRuntimePackDir)' == '' and ('%(ResolvedRuntimePack.PackageDirectory)' == '' or !Exists(%(ResolvedRuntimePack.PackageDirectory)))" <Error Condition="'$(MicrosoftNetCoreAppRuntimePackDir)' == '' and ('%(ResolvedRuntimePack.PackageDirectory)' == '' or !Exists(%(ResolvedRuntimePack.PackageDirectory)))"
...@@ -106,37 +171,50 @@ ...@@ -106,37 +171,50 @@
<_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include'))</_WasmRuntimePackIncludeDir> <_WasmRuntimePackIncludeDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'include'))</_WasmRuntimePackIncludeDir>
<_WasmRuntimePackSrcDir>$([MSBuild]::NormalizeDirectory($(MicrosoftNetCoreAppRuntimePackRidNativeDir), 'src'))</_WasmRuntimePackSrcDir> <_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> <_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> </PropertyGroup>
<MakeDir Directories="$(_WasmIntermediateOutputPath)" /> <MakeDir Directories="$(_WasmIntermediateOutputPath)" />
</Target> </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)"> <Target Name="_BeforeWasmBuildApp" DependsOnTargets="$(_BeforeWasmBuildAppDependsOn)">
<Error Condition="!Exists('$(MicrosoftNetCoreAppRuntimePackRidDir)')" Text="MicrosoftNetCoreAppRuntimePackRidDir=$(MicrosoftNetCoreAppRuntimePackRidDir) doesn't exist" /> <Error Condition="!Exists('$(MicrosoftNetCoreAppRuntimePackRidDir)')" Text="MicrosoftNetCoreAppRuntimePackRidDir=$(MicrosoftNetCoreAppRuntimePackRidDir) doesn't exist" />
<Error Condition="@(WasmAssembliesToBundle->Count()) == 0" Text="WasmAssembliesToBundle item is empty. No assemblies to process" /> <Error Condition="@(WasmAssembliesToBundle->Count()) == 0" Text="WasmAssembliesToBundle item is empty. No assemblies to process" />
<PropertyGroup> <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> <WasmAppDir Condition="'$(WasmAppDir)' == ''">$([MSBuild]::NormalizeDirectory($(OutputPath), 'AppBundle'))</WasmAppDir>
<WasmMainAssemblyFileName Condition="'$(WasmMainAssemblyFileName)' == ''">$(TargetFileName)</WasmMainAssemblyFileName> <WasmMainAssemblyFileName Condition="'$(WasmMainAssemblyFileName)' == ''">$(TargetFileName)</WasmMainAssemblyFileName>
<WasmAppDir>$([MSBuild]::NormalizeDirectory($(WasmAppDir)))</WasmAppDir> <WasmAppDir>$([MSBuild]::NormalizeDirectory($(WasmAppDir)))</WasmAppDir>
<_MainAssemblyPath Condition="'%(WasmAssembliesToBundle.FileName)' == $(AssemblyName) and '%(WasmAssembliesToBundle.Extension)' == '.dll' and $(WasmGenerateAppBundle) == 'true'">%(WasmAssembliesToBundle.Identity)</_MainAssemblyPath> <_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> <_WasmRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' == '' and $(_MainAssemblyPath) != ''">$([System.IO.Path]::ChangeExtension($(_MainAssemblyPath), '.runtimeconfig.json'))</_WasmRuntimeConfigFilePath>
<_ParsedRuntimeConfigFilePath Condition="'$(_MainAssemblyPath)' != ''">$([System.IO.Path]::GetDirectoryName($(_MainAssemblyPath)))\runtimeconfig.bin</_ParsedRuntimeConfigFilePath> <_ParsedRuntimeConfigFilePath Condition="'$(_WasmRuntimeConfigFilePath)' != ''">$([System.IO.Path]::GetDirectoryName($(_WasmRuntimeConfigFilePath)))\runtimeconfig.bin</_ParsedRuntimeConfigFilePath>
</PropertyGroup> </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))" <Warning Condition="'$(WasmGenerateAppBundle)' == 'true' and $(_WasmRuntimeConfigFilePath) != '' and !Exists($(_WasmRuntimeConfigFilePath))"
Text="Could not find $(_WasmRuntimeConfigFilePath) for $(_MainAssemblyPath)." /> Text="Could not find $(_WasmRuntimeConfigFilePath) for $(_MainAssemblyPath)." />
<ItemGroup> <ItemGroup>
<_WasmAssembliesInternal Remove="@(_WasmAssembliesInternal)" />
<_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" /> <_WasmAssembliesInternal Include="@(WasmAssembliesToBundle->Distinct())" />
<_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" />
<_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" /> <_WasmSatelliteAssemblies Include="@(_WasmAssembliesInternal)" />
<_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" /> <_WasmSatelliteAssemblies Remove="@(_WasmSatelliteAssemblies)" Condition="!$([System.String]::Copy('%(Identity)').EndsWith('.resources.dll'))" />
<!-- FIXME: Only include the ones with valid culture name --> <!-- FIXME: Only include the ones with valid culture name -->
...@@ -166,9 +244,7 @@ ...@@ -166,9 +244,7 @@
</ItemGroup> </ItemGroup>
</Target> </Target>
<Target Name="_WasmGenerateAppBundle" Condition="'$(WasmGenerateAppBundle)' == 'true'" DependsOnTargets="_WasmGenerateRuntimeConfig"> <Target Name="_GetWasmGenerateAppBundleDependencies">
<Error Condition="'$(WasmMainJSPath)' == ''" Text="%24(WasmMainJSPath) property needs to be set" />
<PropertyGroup> <PropertyGroup>
<WasmIcuDataFileName Condition="'$(InvariantGlobalization)' != 'true'">icudt.dat</WasmIcuDataFileName> <WasmIcuDataFileName Condition="'$(InvariantGlobalization)' != 'true'">icudt.dat</WasmIcuDataFileName>
...@@ -184,7 +260,16 @@ ...@@ -184,7 +260,16 @@
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)$(WasmIcuDataFileName)" Condition="'$(InvariantGlobalization)' != 'true'" /> <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)$(WasmIcuDataFileName)" Condition="'$(InvariantGlobalization)' != 'true'" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.timezones.blat" /> <WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.timezones.blat" />
</ItemGroup> </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 <WasmAppBuilder
AppDir="$(WasmAppDir)" AppDir="$(WasmAppDir)"
MainJS="$(WasmMainJSPath)" MainJS="$(WasmMainJSPath)"
...@@ -202,6 +287,8 @@ ...@@ -202,6 +287,8 @@
</WasmAppBuilder> </WasmAppBuilder>
<CallTarget Targets="_GenerateRunV8Script" Condition="'$(WasmGenerateRunV8Script)' == 'true'" /> <CallTarget Targets="_GenerateRunV8Script" Condition="'$(WasmGenerateRunV8Script)' == 'true'" />
<WriteLinesToFile File="$(WasmAppDir)\.stamp" Lines="" Overwrite="true" />
</Target> </Target>
<Target Name="_GenerateRunV8Script"> <Target Name="_GenerateRunV8Script">
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<TestRootDir Condition="'$(HELIX_WORKITEM_ROOT)' == ''">$(MSBuildThisFileDirectory)..\wasm_build\</TestRootDir> <TestRootDir Condition="'$(HELIX_WORKITEM_ROOT)' == ''">$(MSBuildThisFileDirectory)..\wasm_build\</TestRootDir>
<RunAOTCompilation Condition="'$(RunAOTCompilation)' == ''">true</RunAOTCompilation> <RunAOTCompilation Condition="'$(RunAOTCompilation)' == ''">true</RunAOTCompilation>
<RunAOTCompilationAfterBuild>true</RunAOTCompilationAfterBuild>
<OriginalPublishDir>$(TestRootDir)..\publish\</OriginalPublishDir> <OriginalPublishDir>$(TestRootDir)..\publish\</OriginalPublishDir>
<ExtraFilesPath>$(OriginalPublishDir)..\extraFiles\</ExtraFilesPath> <ExtraFilesPath>$(OriginalPublishDir)..\extraFiles\</ExtraFilesPath>
<IntermediateOutputPath>$(TestRootDir)\obj\</IntermediateOutputPath> <IntermediateOutputPath>$(TestRootDir)\obj\</IntermediateOutputPath>
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>$(AspNetCoreAppCurrent)</TargetFramework> <TargetFramework>$(AspNetCoreAppCurrent)</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<IsWasmProject>true</IsWasmProject>
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
<RuntimeConfiguration Condition="'$(RuntimeConfiguration)'==''">Release</RuntimeConfiguration> <RuntimeConfiguration Condition="'$(RuntimeConfiguration)'==''">Release</RuntimeConfiguration>
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<RunAnalyzers>false</RunAnalyzers> <RunAnalyzers>false</RunAnalyzers>
<WasmBuildAppDependsOn>PrepareForWasmBuildApp;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn> <WasmBuildAppDependsOn>PrepareForWasmBuildApp;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
<WasmGenerateAppBundle>true</WasmGenerateAppBundle>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
......
...@@ -191,11 +191,15 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task ...@@ -191,11 +191,15 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
/// </summary> /// </summary>
public string? CacheFilePath { get; set; } public string? CacheFilePath { get; set; }
[Required]
public string IntermediateOutputPath { get; set; } = string.Empty;
[Output] [Output]
public string[]? FileWrites { get; private set; } public string[]? FileWrites { get; private set; }
private List<string> _fileWrites = new(); private List<string> _fileWrites = new();
private IList<ITaskItem>? _assembliesToCompile;
private ConcurrentDictionary<string, ITaskItem> compiledAssemblies = new(); private ConcurrentDictionary<string, ITaskItem> compiledAssemblies = new();
private MonoAotMode parsedAotMode; private MonoAotMode parsedAotMode;
...@@ -207,7 +211,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task ...@@ -207,7 +211,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
private int _numCompiled; private int _numCompiled;
private int _totalNumAssemblies; private int _totalNumAssemblies;
public override bool Execute() private bool ProcessAndValidateArguments()
{ {
if (!File.Exists(CompilerBinaryPath)) if (!File.Exists(CompilerBinaryPath))
{ {
...@@ -230,6 +234,9 @@ public override bool Execute() ...@@ -230,6 +234,9 @@ public override bool Execute()
return false; return false;
} }
if (!Directory.Exists(IntermediateOutputPath))
Directory.CreateDirectory(IntermediateOutputPath);
if (AotProfilePath != null) if (AotProfilePath != null)
{ {
foreach (var path in AotProfilePath) foreach (var path in AotProfilePath)
...@@ -246,7 +253,7 @@ public override bool Execute() ...@@ -246,7 +253,7 @@ public override bool Execute()
{ {
if (string.IsNullOrEmpty(LLVMPath)) if (string.IsNullOrEmpty(LLVMPath))
// prevent using some random llc/opt from PATH (installed with clang) // 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)) if (!Directory.Exists(LLVMPath))
{ {
...@@ -270,7 +277,7 @@ public override bool Execute() ...@@ -270,7 +277,7 @@ public override bool Execute()
Log.LogWarning($"'{nameof(OutputType)}=Normal' is deprecated, use 'ObjectFile' instead."); Log.LogWarning($"'{nameof(OutputType)}=Normal' is deprecated, use 'ObjectFile' instead.");
parsedOutputType = MonoAotOutputType.ObjectFile; break; parsedOutputType = MonoAotOutputType.ObjectFile; break;
default: 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) switch (LibraryFormat)
...@@ -280,13 +287,13 @@ public override bool Execute() ...@@ -280,13 +287,13 @@ public override bool Execute()
case "So": parsedLibraryFormat = MonoAotLibraryFormat.So; break; case "So": parsedLibraryFormat = MonoAotLibraryFormat.So; break;
default: default:
if (parsedOutputType == MonoAotOutputType.Library) 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; break;
} }
if (parsedAotMode == MonoAotMode.LLVMOnly && !UseLLVM) 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) switch (AotModulesTableLanguage)
...@@ -294,33 +301,56 @@ public override bool Execute() ...@@ -294,33 +301,56 @@ public override bool Execute()
case "C": parsedAotModulesTableLanguage = MonoAotModulesTableLanguage.C; break; case "C": parsedAotModulesTableLanguage = MonoAotModulesTableLanguage.C; break;
case "ObjC": parsedAotModulesTableLanguage = MonoAotModulesTableLanguage.ObjC; break; case "ObjC": parsedAotModulesTableLanguage = MonoAotModulesTableLanguage.ObjC; break;
default: 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)) if (!string.IsNullOrEmpty(AotModulesTablePath))
{ {
// AOT modules for static linking, needs the aot modules table // AOT modules for static linking, needs the aot modules table
UseStaticLinking = true; UseStaticLinking = true;
if (!GenerateAotModulesTable(Assemblies, Profilers, AotModulesTablePath))
return false;
} }
if (UseDirectIcalls && !UseStaticLinking) 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) 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)) 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; string? monoPaths = null;
if (AdditionalAssemblySearchPaths != null) if (AdditionalAssemblySearchPaths != null)
monoPaths = string.Join(Path.PathSeparator.ToString(), AdditionalAssemblySearchPaths); monoPaths = string.Join(Path.PathSeparator.ToString(), AdditionalAssemblySearchPaths);
...@@ -329,14 +359,14 @@ public override bool Execute() ...@@ -329,14 +359,14 @@ public override bool Execute()
//FIXME: check the nothing changed at all case //FIXME: check the nothing changed at all case
_totalNumAssemblies = Assemblies.Length; _totalNumAssemblies = _assembliesToCompile.Count;
int allowedParallelism = Math.Min(Assemblies.Length, Environment.ProcessorCount); int allowedParallelism = Math.Min(_assembliesToCompile.Count, Environment.ProcessorCount);
if (BuildEngine is IBuildEngine9 be9) if (BuildEngine is IBuildEngine9 be9)
allowedParallelism = be9.RequestCores(allowedParallelism); allowedParallelism = be9.RequestCores(allowedParallelism);
if (DisableParallelAot || allowedParallelism == 1) if (DisableParallelAot || allowedParallelism == 1)
{ {
foreach (var assemblyItem in Assemblies) foreach (var assemblyItem in _assembliesToCompile)
{ {
if (!PrecompileLibrarySerial(assemblyItem, monoPaths)) if (!PrecompileLibrarySerial(assemblyItem, monoPaths))
return !Log.HasLoggedErrors; return !Log.HasLoggedErrors;
...@@ -345,15 +375,12 @@ public override bool Execute() ...@@ -345,15 +375,12 @@ public override bool Execute()
else else
{ {
ParallelLoopResult result = Parallel.ForEach( ParallelLoopResult result = Parallel.ForEach(
Assemblies, _assembliesToCompile,
new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism }, new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
(assemblyItem, state) => PrecompileLibraryParallel(assemblyItem, monoPaths, state)); (assemblyItem, state) => PrecompileLibraryParallel(assemblyItem, monoPaths, state));
if (!result.IsCompleted) if (!result.IsCompleted)
{ {
if (!Log.HasLoggedErrors)
Log.LogError("Unknown failure occured while compiling");
return false; return false;
} }
} }
...@@ -365,15 +392,71 @@ public override bool Execute() ...@@ -365,15 +392,71 @@ public override bool Execute()
if (_cache.Save(CacheFilePath!)) if (_cache.Save(CacheFilePath!))
_fileWrites.Add(CacheFilePath!); _fileWrites.Add(CacheFilePath!);
CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, Assemblies).ToArray(); CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, _assembliesToCompile).ToArray();
FileWrites = _fileWrites.ToArray(); FileWrites = _fileWrites.ToArray();
return !Log.HasLoggedErrors; 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) private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
{ {
string assembly = assemblyItem.ItemSpec; string assembly = assemblyItem.GetMetadata("FullPath");
string assemblyDir = Path.GetDirectoryName(assembly)!; string assemblyDir = Path.GetDirectoryName(assembly)!;
var aotAssembly = new TaskItem(assembly); var aotAssembly = new TaskItem(assembly);
var aotArgs = new List<string>(); var aotArgs = new List<string>();
...@@ -567,38 +650,20 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths) ...@@ -567,38 +650,20 @@ private bool PrecompileLibrary(ITaskItem assemblyItem, string? monoPaths)
// values, which wont work. // values, which wont work.
processArgs.Add($"\"--aot={string.Join(",", aotArgs)}\""); processArgs.Add($"\"--aot={string.Join(",", aotArgs)}\"");
string paths = "";
if (isDedup) if (isDedup)
{ {
StringBuilder sb = new StringBuilder(); foreach (var aItem in _assembliesToCompile!)
HashSet<string> allPaths = new HashSet<string>(); processArgs.Add(aItem.ItemSpec);
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();
} }
else else
{ {
paths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}";
processArgs.Add('"' + assemblyFilename + '"'); processArgs.Add('"' + assemblyFilename + '"');
} }
monoPaths = $"{assemblyDir}{Path.PathSeparator}{monoPaths}";
var envVariables = new Dictionary<string, string> 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 {"MONO_ENV_OPTIONS", string.Empty} // we do not want options to be provided out of band to the cross compilers
}; };
...@@ -713,7 +778,7 @@ private void PrecompileLibraryParallel(ITaskItem assemblyItem, string? monoPaths ...@@ -713,7 +778,7 @@ private void PrecompileLibraryParallel(ITaskItem assemblyItem, string? monoPaths
state.Break(); 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>(); var symbols = new List<string>();
foreach (var asm in assemblies) foreach (var asm in assemblies)
...@@ -831,12 +896,12 @@ private bool TryGetAssemblyName(string asmPath, [NotNullWhen(true)] out string? ...@@ -831,12 +896,12 @@ private bool TryGetAssemblyName(string asmPath, [NotNullWhen(true)] out string?
} }
} }
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); List<ITaskItem> outItems = new(originalAssemblies.Count);
foreach (ITaskItem item in items) 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}"); throw new LogAsErrorException($"Bug: Could not find item in the dict with key {item.ItemSpec}");
outItems.Add(dictItem); outItems.Add(dictItem);
...@@ -931,9 +996,8 @@ public bool CopyOutputFileIfChanged() ...@@ -931,9 +996,8 @@ public bool CopyOutputFileIfChanged()
if (!_cache.Enabled) if (!_cache.Enabled)
return true; return true;
if (!File.Exists(TempFile)) try
throw new LogAsErrorException($"Could not find output file {TempFile}"); {
if (!_cache.ShouldCopy(this, out string? cause)) if (!_cache.ShouldCopy(this, out string? cause))
{ {
_cache.Log.LogMessage(MessageImportance.Low, $"Skipping copying over {TargetFile} as the contents are unchanged"); _cache.Log.LogMessage(MessageImportance.Low, $"Skipping copying over {TargetFile} as the contents are unchanged");
...@@ -944,11 +1008,15 @@ public bool CopyOutputFileIfChanged() ...@@ -944,11 +1008,15 @@ public bool CopyOutputFileIfChanged()
File.Delete(TargetFile); File.Delete(TargetFile);
File.Copy(TempFile, TargetFile); File.Copy(TempFile, TargetFile);
File.Delete(TempFile);
_cache.Log.LogMessage(MessageImportance.Low, $"Copying {TempFile} to {TargetFile} because {cause}"); _cache.Log.LogMessage(MessageImportance.Low, $"Copying {TempFile} to {TargetFile} because {cause}");
return true; return true;
} }
finally
{
File.Delete(TempFile);
}
}
} }
public enum MonoAotMode public enum MonoAotMode
......
...@@ -212,7 +212,7 @@ public static bool CopyIfDifferent(string src, string dst, bool useHash) ...@@ -212,7 +212,7 @@ public static bool CopyIfDifferent(string src, string dst, bool useHash)
throw new ArgumentException($"Cannot find {src} file to copy", nameof(src)); throw new ArgumentException($"Cannot find {src} file to copy", nameof(src));
bool areDifferent = !File.Exists(dst) || bool areDifferent = !File.Exists(dst) ||
(useHash && Utils.ComputeHash(src) != Utils.ComputeHash(dst)) || (useHash && ComputeHash(src) != ComputeHash(dst)) ||
(File.ReadAllText(src) != File.ReadAllText(dst)); (File.ReadAllText(src) != File.ReadAllText(dst));
if (areDifferent) if (areDifferent)
......
...@@ -44,6 +44,19 @@ public class EmccCompile : Microsoft.Build.Utilities.Task ...@@ -44,6 +44,19 @@ public class EmccCompile : Microsoft.Build.Utilities.Task
private int _numCompiled; private int _numCompiled;
public override bool Execute() public override bool Execute()
{
try
{
return ExecuteActual();
}
catch (LogAsErrorException laee)
{
Log.LogError(laee.Message);
return false;
}
}
private bool ExecuteActual()
{ {
if (SourceFiles.Length == 0) if (SourceFiles.Length == 0)
{ {
...@@ -133,7 +146,7 @@ public override bool Execute() ...@@ -133,7 +146,7 @@ public override bool Execute()
}); });
if (!result.IsCompleted && !Log.HasLoggedErrors) 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) if (!Log.HasLoggedErrors)
...@@ -211,7 +224,7 @@ bool ProcessSourceFile(string srcFile, string objFile) ...@@ -211,7 +224,7 @@ bool ProcessSourceFile(string srcFile, string objFile)
private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, out string reason) private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, out string reason)
{ {
if (!File.Exists(srcFile)) 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)) if (!File.Exists(objFile))
{ {
...@@ -228,7 +241,7 @@ private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, ou ...@@ -228,7 +241,7 @@ private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, ou
return true; return true;
} }
reason = "everything is up-to-date."; reason = "everything is up-to-date";
return false; return false;
bool IsNewerThanOutput(string inFile, string outFile, out string reason) bool IsNewerThanOutput(string inFile, string outFile, out string reason)
......
...@@ -23,6 +23,9 @@ public class IcallTableGenerator : Task ...@@ -23,6 +23,9 @@ public class IcallTableGenerator : Task
[Required, NotNull] [Required, NotNull]
public string? OutputPath { get; set; } public string? OutputPath { get; set; }
[Output]
public string? FileWrites { get; private set; } = "";
private List<Icall> _icalls = new List<Icall> (); private List<Icall> _icalls = new List<Icall> ();
private Dictionary<string, IcallClass> _runtimeIcalls = new Dictionary<string, IcallClass> (); private Dictionary<string, IcallClass> _runtimeIcalls = new Dictionary<string, IcallClass> ();
...@@ -58,6 +61,7 @@ public void GenIcallTable(string runtimeIcallTableFile, string[] assemblies) ...@@ -58,6 +61,7 @@ public void GenIcallTable(string runtimeIcallTableFile, string[] assemblies)
Log.LogMessage(MessageImportance.Low, $"Generating icall table to '{OutputPath}'."); Log.LogMessage(MessageImportance.Low, $"Generating icall table to '{OutputPath}'.");
else else
Log.LogMessage(MessageImportance.Low, $"Icall table in {OutputPath} is unchanged."); Log.LogMessage(MessageImportance.Low, $"Icall table in {OutputPath} is unchanged.");
FileWrites = OutputPath;
File.Delete(tmpFileName); File.Delete(tmpFileName);
} }
......
...@@ -16,20 +16,43 @@ ...@@ -16,20 +16,43 @@
public class PInvokeTableGenerator : Task public class PInvokeTableGenerator : Task
{ {
[Required] [Required, NotNull]
public ITaskItem[]? Modules { get; set; } public string[]? Modules { get; set; }
[Required] [Required, NotNull]
public ITaskItem[]? Assemblies { get; set; } public string[]? Assemblies { get; set; }
[Required, NotNull] [Required, NotNull]
public string? OutputPath { get; set; } public string? OutputPath { get; set; }
[Output]
public string FileWrites { get; private set; } = string.Empty;
private static char[] s_charsToReplace = new[] { '.', '-', }; private static char[] s_charsToReplace = new[] { '.', '-', };
public override bool Execute() public override bool Execute()
{ {
GenPInvokeTable(Modules!.Select(item => item.ItemSpec).ToArray(), Assemblies!.Select(item => item.ItemSpec).ToArray()); if (Assemblies.Length == 0)
return true; {
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) public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies)
...@@ -61,6 +84,7 @@ public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies) ...@@ -61,6 +84,7 @@ public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies)
Log.LogMessage(MessageImportance.Low, $"Generating pinvoke table to '{OutputPath}'."); Log.LogMessage(MessageImportance.Low, $"Generating pinvoke table to '{OutputPath}'.");
else else
Log.LogMessage(MessageImportance.Low, $"PInvoke table in {OutputPath} is unchanged."); Log.LogMessage(MessageImportance.Low, $"PInvoke table in {OutputPath} is unchanged.");
FileWrites = OutputPath;
File.Delete(tmpFileName); File.Delete(tmpFileName);
} }
...@@ -339,11 +363,7 @@ private static bool IsBlittable (Type type) ...@@ -339,11 +363,7 @@ private static bool IsBlittable (Type type)
return false; return false;
} }
private static void Error (string msg) private static void Error (string msg) => throw new LogAsErrorException(msg);
{
// FIXME:
throw new Exception(msg);
}
} }
internal class PInvoke internal class PInvoke
......
...@@ -117,11 +117,24 @@ private class IcuData : AssetEntry ...@@ -117,11 +117,24 @@ private class IcuData : AssetEntry
} }
public override bool Execute () public override bool Execute ()
{
try
{
return ExecuteInternal();
}
catch (LogAsErrorException laee)
{
Log.LogError(laee.Message);
return false;
}
}
private bool ExecuteInternal ()
{ {
if (!File.Exists(MainJS)) 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)) 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) if (Assemblies?.Length == 0)
{ {
...@@ -162,8 +175,12 @@ public override bool Execute () ...@@ -162,8 +175,12 @@ public override bool Execute ()
} }
FileCopyChecked(MainJS!, Path.Combine(AppDir, "runtime.js"), string.Empty); FileCopyChecked(MainJS!, Path.Combine(AppDir, "runtime.js"), string.Empty);
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>"; var html = @"<html><body><script type=""text/javascript"" src=""runtime.js""></script></body></html>";
File.WriteAllText(Path.Combine(AppDir, "index.html"), html); File.WriteAllText(indexHtmlPath, html);
}
foreach (var assembly in _assemblies) foreach (var assembly in _assemblies)
{ {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="..\Common\Utils.cs" /> <Compile Include="..\Common\Utils.cs" />
<Compile Include="..\Common\LogAsErrorException.cs" />
<PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" /> <PackageReference Include="Microsoft.Build" Version="$(RefOnlyMicrosoftBuildVersion)" />
<PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" /> <PackageReference Include="Microsoft.Build.Framework" Version="$(RefOnlyMicrosoftBuildFrameworkVersion)" />
......
// 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. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.IO; using System.IO;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
...@@ -17,36 +16,6 @@ public BlazorWasmTests(ITestOutputHelper output, SharedBuildPerTestClassFixture ...@@ -17,36 +16,6 @@ public BlazorWasmTests(ITestOutputHelper output, SharedBuildPerTestClassFixture
{ {
} }
// TODO: invariant case?
[ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
[InlineData("Debug", false)]
[InlineData("Debug", true)] // just aot
[InlineData("Release", false)] // should re-link
[InlineData("Release", true)]
public void PublishTemplateProject(string config, bool aot)
{
string id = $"blazorwasm_{config}_aot_{aot}_{Path.GetRandomFileName()}";
InitBlazorWasmProjectDir(id);
new DotNetCommand(s_buildEnv, useDefaultArgs: false)
.WithWorkingDirectory(_projectDir!)
.ExecuteWithCapturedOutput("new blazorwasm")
.EnsureSuccessful();
string publishLogPath = Path.Combine(s_buildEnv.LogRootPath, id, $"{id}.binlog");
new DotNetCommand(s_buildEnv)
.WithWorkingDirectory(_projectDir!)
.ExecuteWithCapturedOutput("publish", $"-bl:{publishLogPath}", aot ? "-p:RunAOTCompilation=true" : "", $"-p:Configuration={config}")
.EnsureSuccessful();
//TODO: validate the build somehow?
// compare dotnet.wasm?
// relinking - dotnet.wasm should be smaller
//
// playwright?
}
[ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))] [ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))]
[InlineData("Debug")] [InlineData("Debug")]
[InlineData("Release")] [InlineData("Release")]
...@@ -54,6 +23,7 @@ public void NativeRef_EmitsWarningBecauseItRequiresWorkload(string config) ...@@ -54,6 +23,7 @@ public void NativeRef_EmitsWarningBecauseItRequiresWorkload(string config)
{ {
CommandResult res = PublishForRequiresWorkloadTest(config, extraItems: "<NativeFileReference Include=\"native-lib.o\" />"); CommandResult res = PublishForRequiresWorkloadTest(config, extraItems: "<NativeFileReference Include=\"native-lib.o\" />");
res.EnsureSuccessful(); res.EnsureSuccessful();
AssertBlazorBundle(config, isPublish: true, dotnetWasmFromRuntimePack: true);
Assert.Contains("but the native references won't be linked in", res.Output); Assert.Contains("but the native references won't be linked in", res.Output);
} }
...@@ -71,7 +41,7 @@ public void AOT_FailsBecauseItRequiresWorkload(string config) ...@@ -71,7 +41,7 @@ public void AOT_FailsBecauseItRequiresWorkload(string config)
[ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))] [ConditionalTheory(typeof(BuildTestBase), nameof(IsNotUsingWorkloads))]
[InlineData("Debug")] [InlineData("Debug")]
[InlineData("Release")] [InlineData("Release")]
public void AOT_And_NativeRef_FailsBecauseItRequireWorkload(string config) public void AOT_And_NativeRef_FailBecauseTheyRequireWorkload(string config)
{ {
CommandResult res = PublishForRequiresWorkloadTest(config, CommandResult res = PublishForRequiresWorkloadTest(config,
extraProperties: "<RunAOTCompilation>true</RunAOTCompilation>", extraProperties: "<RunAOTCompilation>true</RunAOTCompilation>",
...@@ -84,18 +54,13 @@ public void AOT_And_NativeRef_FailsBecauseItRequireWorkload(string config) ...@@ -84,18 +54,13 @@ public void AOT_And_NativeRef_FailsBecauseItRequireWorkload(string config)
private CommandResult PublishForRequiresWorkloadTest(string config, string extraItems="", string extraProperties="") private CommandResult PublishForRequiresWorkloadTest(string config, string extraItems="", string extraProperties="")
{ {
string id = $"needs_workload_{config}_{Path.GetRandomFileName()}"; string id = $"needs_workload_{config}_{Path.GetRandomFileName()}";
InitBlazorWasmProjectDir(id); CreateBlazorWasmTemplateProject(id);
new DotNetCommand(s_buildEnv, useDefaultArgs: false)
.WithWorkingDirectory(_projectDir!)
.ExecuteWithCapturedOutput("new blazorwasm")
.EnsureSuccessful();
if (IsNotUsingWorkloads) if (IsNotUsingWorkloads)
{ {
// no packs installed, so no need to update the paths for runtime pack etc // no packs installed, so no need to update the paths for runtime pack etc
File.WriteAllText(Path.Combine(_projectDir!, "Directory.Build.props"), "<Project />"); File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Local.Directory.Build.props"), Path.Combine(_projectDir!, "Directory.Build.props"), overwrite: true);
File.WriteAllText(Path.Combine(_projectDir!, "Directory.Build.targets"), "<Project />"); 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"), AddItemsPropertiesToProject(Path.Combine(_projectDir!, $"{id}.csproj"),
...@@ -167,6 +132,11 @@ private void BuildNet50Project(string config, bool aot, bool expectError, string ...@@ -167,6 +132,11 @@ private void BuildNet50Project(string config, bool aot, bool expectError, string
{ {
result.EnsureSuccessful(); result.EnsureSuccessful();
Assert.Contains("** UsingBrowserRuntimeWorkload: 'false'", result.Output); 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
} }
} }
} }
......
...@@ -34,9 +34,9 @@ public BuildAndRunAttribute(BuildArgs buildArgs, RunHost host = RunHost.All, par ...@@ -34,9 +34,9 @@ public BuildAndRunAttribute(BuildArgs buildArgs, RunHost host = RunHost.All, par
.UnwrapItemsAsArrays().ToList().Dump(); .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) .Multiply(parameters)
.WithRunHosts(host) .WithRunHosts(host)
.UnwrapItemsAsArrays().ToList().Dump(); .UnwrapItemsAsArrays().ToList().Dump();
......
...@@ -135,5 +135,8 @@ public BuildEnvironment() ...@@ -135,5 +135,8 @@ public BuildEnvironment()
protected static string s_directoryBuildPropsForLocal = File.ReadAllText(Path.Combine(TestDataPath, "Local.Directory.Build.props")); 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_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"));
} }
} }
// 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);
}
}
}
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using Xunit; using Xunit;
...@@ -285,8 +286,11 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp ...@@ -285,8 +286,11 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp
bool useCache = true, bool useCache = true,
bool expectSuccess = true, bool expectSuccess = true,
bool createProject = 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)) if (useCache && _buildContext.TryGetBuildFor(buildArgs, out BuildProduct? product))
{ {
Console.WriteLine ($"Using existing build found at {product.ProjectDir}, with build log at {product.LogFile}"); Console.WriteLine ($"Using existing build found at {product.ProjectDir}, with build log at {product.LogFile}");
...@@ -314,12 +318,13 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp ...@@ -314,12 +318,13 @@ protected static BuildArgs ExpandBuildArgs(BuildArgs buildArgs, string extraProp
} }
StringBuilder sb = new(); StringBuilder sb = new();
sb.Append("publish"); sb.Append(publish ? "publish" : "build");
sb.Append($" {s_buildEnv.DefaultBuildArgs}"); sb.Append($" {s_buildEnv.DefaultBuildArgs}");
sb.Append($" /p:Configuration={buildArgs.Config}"); 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($"-------- Building ---------");
_testOutput.WriteLine($"Binlog path: {logFilePath}"); _testOutput.WriteLine($"Binlog path: {logFilePath}");
Console.WriteLine($"Binlog path: {logFilePath}"); Console.WriteLine($"Binlog path: {logFilePath}");
...@@ -371,6 +376,100 @@ public void InitBlazorWasmProjectDir(string id) ...@@ -371,6 +376,100 @@ public void InitBlazorWasmProjectDir(string id)
File.Copy(Path.Combine(BuildEnvironment.TestDataPath, "Blazor.Directory.Build.targets"), Path.Combine(_projectDir, "Directory.Build.targets")); 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) static void AssertRuntimePackPath(string buildOutput)
{ {
var match = s_runtimePackPathRegex.Match(buildOutput); var match = s_runtimePackPathRegex.Match(buildOutput);
...@@ -384,7 +483,6 @@ static void AssertRuntimePackPath(string buildOutput) ...@@ -384,7 +483,6 @@ static void AssertRuntimePackPath(string buildOutput)
protected static void AssertBasicAppBundle(string bundleDir, string projectName, string config, bool hasIcudt=true, bool dotnetWasmFromRuntimePack=true) protected static void AssertBasicAppBundle(string bundleDir, string projectName, string config, bool hasIcudt=true, bool dotnetWasmFromRuntimePack=true)
{ {
Console.WriteLine ($"AssertBasicAppBundle: {dotnetWasmFromRuntimePack}");
AssertFilesExist(bundleDir, new [] AssertFilesExist(bundleDir, new []
{ {
"index.html", "index.html",
...@@ -486,6 +584,66 @@ protected static void AssertFile(string file0, string file1, string? label=null, ...@@ -486,6 +584,66 @@ protected static void AssertFile(string file0, string file1, string? label=null,
return result; 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) protected string GetBinDir(string config, string targetFramework=s_targetFramework, string? baseDir=null)
{ {
var dir = baseDir ?? _projectDir; var dir = baseDir ?? _projectDir;
...@@ -615,26 +773,34 @@ void LogData(string label, string? message) ...@@ -615,26 +773,34 @@ void LogData(string label, string? message)
} }
} }
public static string AddItemsPropertiesToProject(string projectFile, string? extraProperties=null, string? extraItems=null) public static string AddItemsPropertiesToProject(string projectFile, string? extraProperties=null, string? extraItems=null, string? atTheEnd=null)
{ {
if (extraProperties == null && extraItems == null) if (extraProperties == null && extraItems == null && atTheEnd == null)
return projectFile; return projectFile;
XmlDocument doc = new(); XmlDocument doc = new();
doc.Load(projectFile); doc.Load(projectFile);
XmlNode root = doc.DocumentElement ?? throw new Exception();
if (extraItems != null) if (extraItems != null)
{ {
XmlNode node = doc.CreateNode(XmlNodeType.Element, "ItemGroup", null); XmlNode node = doc.CreateNode(XmlNodeType.Element, "ItemGroup", null);
node.InnerXml = extraItems; node.InnerXml = extraItems;
doc.DocumentElement!.AppendChild(node); root.AppendChild(node);
} }
if (extraProperties != null) if (extraProperties != null)
{ {
XmlNode node = doc.CreateNode(XmlNodeType.Element, "PropertyGroup", null); XmlNode node = doc.CreateNode(XmlNodeType.Element, "PropertyGroup", null);
node.InnerXml = extraProperties; 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); doc.Save(projectFile);
...@@ -654,6 +820,29 @@ private static string GetEnvironmentVariableOrDefault(string envVarName, string ...@@ -654,6 +820,29 @@ private static string GetEnvironmentVariableOrDefault(string envVarName, string
return string.IsNullOrEmpty(value) ? defaultValue : value; 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 = @" protected static string s_mainReturns42 = @"
public class TestClass { public class TestClass {
public static int Main() public static int Main()
...@@ -669,4 +858,6 @@ public static int Main() ...@@ -669,4 +858,6 @@ public static int Main()
string ProjectFileContents, string ProjectFileContents,
string? ExtraBuildArgs); string? ExtraBuildArgs);
public record BuildProduct(string ProjectDir, string LogFile, bool Result); 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);
} }
// 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}");
}
}
...@@ -24,10 +24,10 @@ public CommandResult(ProcessStartInfo startInfo, int exitCode, string output) ...@@ -24,10 +24,10 @@ public CommandResult(ProcessStartInfo startInfo, int exitCode, string output)
Output = output; Output = output;
} }
public void EnsureSuccessful(string messagePrefix = "", bool suppressOutput = false) public CommandResult EnsureSuccessful(string messagePrefix = "", bool suppressOutput = false)
=> EnsureExitCode(0, messagePrefix, suppressOutput); => 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) if (ExitCode != expectedExitCode)
{ {
...@@ -43,6 +43,8 @@ public void EnsureExitCode(int expectedExitCode = 0, string messagePrefix = "", ...@@ -43,6 +43,8 @@ public void EnsureExitCode(int expectedExitCode = 0, string messagePrefix = "",
throw new XunitException(message.ToString()); throw new XunitException(message.ToString());
} }
return this;
} }
} }
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Collections;
#nullable enable #nullable enable
...@@ -96,7 +97,9 @@ public static class HelperExtensions ...@@ -96,7 +97,9 @@ public static class HelperExtensions
public static void UpdateTo(this IDictionary<string, (string fullPath, bool unchanged)> dict, bool unchanged, params string[] filenames) 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)) if (!dict.TryGetValue(filename, out var oldValue))
{ {
......
...@@ -25,17 +25,14 @@ public NativeBuildTests(ITestOutputHelper output, SharedBuildPerTestClassFixture ...@@ -25,17 +25,14 @@ public NativeBuildTests(ITestOutputHelper output, SharedBuildPerTestClassFixture
[Theory] [Theory]
[BuildAndRun] [BuildAndRun]
public void SimpleNativeBuild(BuildArgs buildArgs, RunHost host, string id) 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 = buildArgs with { ProjectName = projectName };
buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<WasmBuildNative>true</WasmBuildNative>"); buildArgs = ExpandBuildArgs(buildArgs, extraProperties: "<WasmBuildNative>true</WasmBuildNative>");
BuildProject(buildArgs, BuildProject(buildArgs,
initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), projectContents), initProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), s_mainReturns42),
dotnetWasmFromRuntimePack: false, dotnetWasmFromRuntimePack: false,
id: id); id: id);
...@@ -44,21 +41,46 @@ private void NativeBuild(string projectNamePrefix, string projectContents, Build ...@@ -44,21 +41,46 @@ private void NativeBuild(string projectNamePrefix, string projectContents, Build
host: host, id: id); host: host, id: id);
} }
[Theory]
[BuildAndRun(aot: true, host: RunHost.None)]
public void MonoAOTCross_WorksWithNoTrimming(BuildArgs buildArgs, string id)
{
// stop once `mono-aot-cross` part of the build is done
string target = @"<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] [Theory]
[BuildAndRun(host: RunHost.None, aot: true)] [BuildAndRun(host: RunHost.None, aot: true)]
public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, string id) public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, string id)
{ {
string printFileTypeTarget = @" 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"" <Exec Command=""wasm-dis $(_WasmIntermediateOutputPath)System.Private.CoreLib.dll.o -o $(_WasmIntermediateOutputPath)wasm-dis-out.txt""
ConsoleToMSBuild=""true""
EnvironmentVariables=""@(EmscriptenEnvVars)"" EnvironmentVariables=""@(EmscriptenEnvVars)""
IgnoreExitCode=""true""> IgnoreExitCode=""true"">
<Output TaskParameter=""ExitCode"" PropertyName=""ExitCode"" /> <Output TaskParameter=""ExitCode"" PropertyName=""ExitCode"" />
</Exec> </Exec>
<Message Text=""wasm-dis exit code: $(ExitCode)"" Importance=""High"" /> <Message Text=""
** wasm-dis exit code: $(ExitCode)
"" Importance=""High"" />
</Target> </Target>
"; ";
string projectName = $"bc_to_o_{buildArgs.Config}"; string projectName = $"bc_to_o_{buildArgs.Config}";
...@@ -71,9 +93,41 @@ public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, st ...@@ -71,9 +93,41 @@ public void IntermediateBitcodeToObjectFilesAreNotLLVMIR(BuildArgs buildArgs, st
dotnetWasmFromRuntimePack: false, dotnetWasmFromRuntimePack: false,
id: id); 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 ." 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."); + " 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. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO; using System.IO;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
......
...@@ -5,12 +5,13 @@ ...@@ -5,12 +5,13 @@
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Wasm.Build.Tests;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
#nullable enable #nullable enable
namespace Wasm.Build.Tests namespace Wasm.Build.NativeRebuild.Tests
{ {
public class FlagsChangeRebuildTest : NativeRebuildTestsBase public class FlagsChangeRebuildTest : NativeRebuildTestsBase
{ {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Wasm.Build.Tests;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
using Xunit.Sdk; using Xunit.Sdk;
...@@ -12,7 +13,7 @@ ...@@ -12,7 +13,7 @@
#nullable enable #nullable enable
namespace Wasm.Build.Tests namespace Wasm.Build.NativeRebuild.Tests
{ {
// TODO: test for runtime components // TODO: test for runtime components
public class NativeRebuildTestsBase : BuildTestBase public class NativeRebuildTestsBase : BuildTestBase
...@@ -136,29 +137,6 @@ internal void CompareStat(IDictionary<string, FileStat> oldStat, IDictionary<str ...@@ -136,29 +137,6 @@ internal void CompareStat(IDictionary<string, FileStat> oldStat, IDictionary<str
throw new XunitException($"CompareStat failed:{Environment.NewLine}{msg}"); 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) internal IDictionary<string, (string fullPath, bool unchanged)> GetFilesTable(BuildArgs buildArgs, BuildPaths paths, bool unchanged)
{ {
List<string> files = new() List<string> files = new()
...@@ -203,7 +181,4 @@ protected void AssertSubstring(string substring, string full, bool contains) ...@@ -203,7 +181,4 @@ protected void AssertSubstring(string substring, string full, bool contains)
Assert.DoesNotContain(substring, full); 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);
} }
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using System.Linq; using System.Linq;
using Wasm.Build.Tests;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
#nullable enable #nullable enable
namespace Wasm.Build.Tests namespace Wasm.Build.NativeRebuild.Tests
{ {
public class NoopNativeRebuildTest : NativeRebuildTestsBase public class NoopNativeRebuildTest : NativeRebuildTestsBase
{ {
......
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Wasm.Build.Tests;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
#nullable enable #nullable enable
namespace Wasm.Build.Tests namespace Wasm.Build.NativeRebuild.Tests
{ {
public class ReferenceNewAssemblyRebuildTest : NativeRebuildTestsBase public class ReferenceNewAssemblyRebuildTest : NativeRebuildTestsBase
{ {
......
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Wasm.Build.Tests;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
#nullable enable #nullable enable
namespace Wasm.Build.Tests namespace Wasm.Build.NativeRebuild.Tests
{ {
public class SimpleSourceChangeRebuildTest : NativeRebuildTestsBase public class SimpleSourceChangeRebuildTest : NativeRebuildTestsBase
{ {
......
...@@ -40,12 +40,13 @@ public SatelliteAssembliesTests(ITestOutputHelper output, SharedBuildPerTestClas ...@@ -40,12 +40,13 @@ public SatelliteAssembliesTests(ITestOutputHelper output, SharedBuildPerTestClas
string id) string id)
{ {
string projectName = $"sat_asm_from_main_asm"; 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 = buildArgs with { ProjectName = projectName };
buildArgs = ExpandBuildArgs(buildArgs, buildArgs = ExpandBuildArgs(buildArgs,
projectTemplate: s_resourcesProjectTemplate, projectTemplate: s_resourcesProjectTemplate,
extraProperties: $"<WasmBuildNative>{(nativeRelink ? "true" : "false")}</WasmBuildNative>"); extraProperties: nativeRelink ? $"<WasmBuildNative>true</WasmBuildNative>" : string.Empty);
BuildProject(buildArgs, BuildProject(buildArgs,
initProject: () => initProject: () =>
......
...@@ -16,15 +16,20 @@ public class SharedBuildPerTestClassFixture : IDisposable ...@@ -16,15 +16,20 @@ public class SharedBuildPerTestClassFixture : IDisposable
public Dictionary<BuildArgs, BuildProduct> _buildPaths = new(); public Dictionary<BuildArgs, BuildProduct> _buildPaths = new();
public void CacheBuild(BuildArgs buildArgs, BuildProduct product) 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) public void RemoveFromCache(string buildPath, bool keepDir=true)
{ {
KeyValuePair<BuildArgs, BuildProduct>? foundKvp = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).SingleOrDefault(); BuildArgs? foundBuildArgs = _buildPaths.Where(kvp => kvp.Value.ProjectDir == buildPath).Select(kvp => kvp.Key).SingleOrDefault();
if (foundKvp == null) if (foundBuildArgs is not null)
throw new Exception($"Could not find build path {buildPath} in cache to remove."); _buildPaths.Remove(foundBuildArgs);
_buildPaths.Remove(foundKvp.Value.Key);
if (!keepDir) if (!keepDir)
RemoveDirectory(buildPath); RemoveDirectory(buildPath);
} }
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
<BundleXunitRunner>true</BundleXunitRunner> <BundleXunitRunner>true</BundleXunitRunner>
<CLRTestKind>BuildAndRun</CLRTestKind> <CLRTestKind>BuildAndRun</CLRTestKind>
<TestFramework>xunit</TestFramework> <TestFramework>xunit</TestFramework>
<WasmGenerateAppBundle>false</WasmGenerateAppBundle>
<EnableDefaultItems>true</EnableDefaultItems> <EnableDefaultItems>true</EnableDefaultItems>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems> <EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<DefineConstants Condition="'$(ContinuousIntegrationBuild)' != 'true'">TEST_DEBUG_CONFIG_ALSO</DefineConstants> <DefineConstants Condition="'$(ContinuousIntegrationBuild)' != 'true'">TEST_DEBUG_CONFIG_ALSO</DefineConstants>
...@@ -18,7 +17,7 @@ ...@@ -18,7 +17,7 @@
<InstallWorkloadForTesting>true</InstallWorkloadForTesting> <InstallWorkloadForTesting>true</InstallWorkloadForTesting>
<!-- don't run any wasm build steps --> <!-- don't run any wasm build steps -->
<WasmBuildAppAfterThisTarget /> <IsWasmProject>false</IsWasmProject>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
......
// 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;
}
}
}
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
<!-- SDK tries to download runtime packs when RuntimeIdentifier is set, remove them from PackageDownload item. --> <!-- SDK tries to download runtime packs when RuntimeIdentifier is set, remove them from PackageDownload item. -->
<Target Name="RemoveRuntimePackFromDownloadItem" <Target Name="RemoveRuntimePackFromDownloadItem"
AfterTargets="ProcessFrameworkReferences"> AfterTargets="ProcessFrameworkReferences"
Condition="'$(WasmNativeWorkload)' == 'true'">
<ItemGroup> <ItemGroup>
<PackageDownload Remove="@(PackageDownload)" <PackageDownload Remove="@(PackageDownload)"
Condition="'$(UsePackageDownload)' == 'true' and $([System.String]::Copy('%(Identity)').StartsWith('Microsoft.NETCore.App.Runtime'))" /> Condition="'$(UsePackageDownload)' == 'true' and $([System.String]::Copy('%(Identity)').StartsWith('Microsoft.NETCore.App.Runtime'))" />
...@@ -21,7 +22,8 @@ ...@@ -21,7 +22,8 @@
<!-- Use local targeting pack for NetCoreAppCurrent. --> <!-- Use local targeting pack for NetCoreAppCurrent. -->
<Target Name="UpdateTargetingAndRuntimePack" <Target Name="UpdateTargetingAndRuntimePack"
AfterTargets="ResolveFrameworkReferences"> AfterTargets="ResolveFrameworkReferences"
Condition="'$(WasmNativeWorkload)' == 'true'">
<ItemGroup> <ItemGroup>
<ResolvedTargetingPack Path="$(_MicrosoftNetCoreAppRefDir.TrimEnd('/\'))" <ResolvedTargetingPack Path="$(_MicrosoftNetCoreAppRefDir.TrimEnd('/\'))"
NuGetPackageVersion="$(RuntimePackInWorkloadVersion)" NuGetPackageVersion="$(RuntimePackInWorkloadVersion)"
...@@ -47,7 +49,8 @@ ...@@ -47,7 +49,8 @@
<!-- Update the local targeting pack's version as it's written into the runtimeconfig.json file to select the right framework. --> <!-- Update the local targeting pack's version as it's written into the runtimeconfig.json file to select the right framework. -->
<Target Name="UpdateRuntimeFrameworkVersion" <Target Name="UpdateRuntimeFrameworkVersion"
AfterTargets="ResolveTargetingPackAssets"> AfterTargets="ResolveTargetingPackAssets"
Condition="'$(WasmNativeWorkload)' == 'true'">
<ItemGroup> <ItemGroup>
<RuntimeFramework Version="$(RuntimePackInWorkloadVersion)" <RuntimeFramework Version="$(RuntimePackInWorkloadVersion)"
Condition="'%(RuntimeFramework.FrameworkName)' == 'Microsoft.NETCore.App'" /> Condition="'%(RuntimeFramework.FrameworkName)' == 'Microsoft.NETCore.App'" />
...@@ -56,7 +59,7 @@ ...@@ -56,7 +59,7 @@
<!-- Filter out conflicting implicit assembly references. --> <!-- Filter out conflicting implicit assembly references. -->
<Target Name="FilterImplicitAssemblyReferences" <Target Name="FilterImplicitAssemblyReferences"
Condition="'$(DisableImplicitAssemblyReferences)' != 'true'" Condition="'$(DisableImplicitAssemblyReferences)' != 'true' and '$(WasmNativeWorkload)' == 'true'"
DependsOnTargets="ResolveProjectReferences" DependsOnTargets="ResolveProjectReferences"
AfterTargets="ResolveTargetingPackAssets"> AfterTargets="ResolveTargetingPackAssets">
<ItemGroup> <ItemGroup>
......
<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>
<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>
...@@ -7,8 +7,4 @@ ...@@ -7,8 +7,4 @@
</PropertyGroup> </PropertyGroup>
<Import Project="$(_WasmTargetsDir)WasmApp.LocalBuild.props" Condition="Exists('$(_WasmTargetsDir)WasmApp.LocalBuild.props')" /> <Import Project="$(_WasmTargetsDir)WasmApp.LocalBuild.props" Condition="Exists('$(_WasmTargetsDir)WasmApp.LocalBuild.props')" />
<PropertyGroup>
<WasmBuildAppDependsOn>PrepareForWasmBuild;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
</PropertyGroup>
</Project> </Project>
...@@ -16,12 +16,6 @@ ...@@ -16,12 +16,6 @@
Text="%24(WasmMainJS) is set when %24(WasmGenerateAppBundle) is not true: it won't be used because an app bundle is not being generated. Possible build authoring error" /> 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>
<Target Name="PrepareForWasmBuild">
<ItemGroup>
<WasmAssembliesToBundle Include="$(TargetDir)publish\**\*.dll" />
</ItemGroup>
</Target>
<Target Name="PrintRuntimePackPath" BeforeTargets="Build"> <Target Name="PrintRuntimePackPath" BeforeTargets="Build">
<Message Text="** MicrosoftNetCoreAppRuntimePackDir : %(ResolvedRuntimePack.PackageDirectory)" Importance="High" /> <Message Text="** MicrosoftNetCoreAppRuntimePackDir : %(ResolvedRuntimePack.PackageDirectory)" Importance="High" />
</Target> </Target>
......
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<WasmBuildAppDependsOn>PrepareForWasmBuild;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
<_MicrosoftNetCoreAppRefDir>$(AppRefDir)\</_MicrosoftNetCoreAppRefDir> <_MicrosoftNetCoreAppRefDir>$(AppRefDir)\</_MicrosoftNetCoreAppRefDir>
</PropertyGroup> </PropertyGroup>
<Target Name="PrepareForWasmBuild">
<ItemGroup>
<WasmAssembliesToBundle Include="$(TargetDir)publish\**\*.dll" />
</ItemGroup>
</Target>
<Target Name="PrintRuntimePackPath" BeforeTargets="Publish"> <Target Name="PrintRuntimePackPath" BeforeTargets="Publish">
<Message Text="** MicrosoftNetCoreAppRuntimePackDir : %(ResolvedRuntimePack.PackageDirectory)" Importance="High" /> <Message Text="** MicrosoftNetCoreAppRuntimePackDir : %(ResolvedRuntimePack.PackageDirectory)" Importance="High" />
</Target> </Target>
......
...@@ -10,14 +10,17 @@ ...@@ -10,14 +10,17 @@
<BuildDir>$(MSBuildThisFileDirectory)\obj\$(Configuration)\wasm</BuildDir> <BuildDir>$(MSBuildThisFileDirectory)\obj\$(Configuration)\wasm</BuildDir>
<AppDir>$(TestBinDir)/WasmApp/</AppDir> <AppDir>$(TestBinDir)/WasmApp/</AppDir>
<NETCoreAppMaximumVersion>99.0</NETCoreAppMaximumVersion> <NETCoreAppMaximumVersion>99.0</NETCoreAppMaximumVersion>
<IsWasmProject>true</IsWasmProject>
<WasmGenerateAppBundle>true</WasmGenerateAppBundle>
<WasmAppBuilderTasksAssemblyPath>$(CORE_ROOT)\WasmAppBuilder\WasmAppBuilder.dll</WasmAppBuilderTasksAssemblyPath> <WasmAppBuilderTasksAssemblyPath>$(CORE_ROOT)\WasmAppBuilder\WasmAppBuilder.dll</WasmAppBuilderTasksAssemblyPath>
<MonoAOTCompilerTasksAssemblyPath>$(CORE_ROOT)\MonoAOTCompiler\MonoAOTCompiler.dll</MonoAOTCompilerTasksAssemblyPath> <MonoAOTCompilerTasksAssemblyPath>$(CORE_ROOT)\MonoAOTCompiler\MonoAOTCompiler.dll</MonoAOTCompilerTasksAssemblyPath>
<JsonToItemsTaskFactoryTasksAssemblyPath>$(CORE_ROOT)\JsonToItemsTaskFactory\JsonToItemsTaskFactory.dll</JsonToItemsTaskFactoryTasksAssemblyPath> <JsonToItemsTaskFactoryTasksAssemblyPath>$(CORE_ROOT)\JsonToItemsTaskFactory\JsonToItemsTaskFactory.dll</JsonToItemsTaskFactoryTasksAssemblyPath>
<RuntimeConfigParserTasksAssemblyPath>$(CORE_ROOT)\RuntimeConfigParser\RuntimeConfigParser.dll</RuntimeConfigParserTasksAssemblyPath> <RuntimeConfigParserTasksAssemblyPath>$(CORE_ROOT)\RuntimeConfigParser\RuntimeConfigParser.dll</RuntimeConfigParserTasksAssemblyPath>
<WasmBuildAppDependsOn>BuildApp;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
</PropertyGroup> </PropertyGroup>
<Target Name="BuildApp" BeforeTargets="WasmBuildApp"> <Target Name="BuildApp">
<PropertyGroup> <PropertyGroup>
<WasmMainAssemblyFileName>$(TestAssemblyFileName)</WasmMainAssemblyFileName> <WasmMainAssemblyFileName>$(TestAssemblyFileName)</WasmMainAssemblyFileName>
<WasmAppDir>$(AppDir)</WasmAppDir> <WasmAppDir>$(AppDir)</WasmAppDir>
......
...@@ -16,13 +16,10 @@ ...@@ -16,13 +16,10 @@
<Content Include="index.html"> <Content Include="index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<WasmExtraFilesToDeploy Include="index.html" />
<ProjectReference Include="ApplyUpdateReferencedAssembly\ApplyUpdateReferencedAssembly.csproj" /> <ProjectReference Include="ApplyUpdateReferencedAssembly\ApplyUpdateReferencedAssembly.csproj" />
</ItemGroup> </ItemGroup>
<Target Name="AfterWasmBuildApp" AfterTargets="WasmBuildApp">
<Copy SourceFiles="$(OutDir)\index.html" DestinationFolder="$(WasmAppDir)" />
</Target>
<Target Name="PreserveEnCAssembliesFromLinking" <Target Name="PreserveEnCAssembliesFromLinking"
Condition="'$(TargetOS)' == 'Browser' and '$(EnableAggressiveTrimming)' == 'true'" Condition="'$(TargetOS)' == 'Browser' and '$(EnableAggressiveTrimming)' == 'true'"
BeforeTargets="ConfigureTrimming"> BeforeTargets="ConfigureTrimming">
......
...@@ -8,10 +8,6 @@ ...@@ -8,10 +8,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<WasmExtraFilesToDeploy Include="index.html" />
</ItemGroup> </ItemGroup>
<Target Name="AfterWasmBuildApp" AfterTargets="WasmBuildApp">
<Copy SourceFiles="$(MSBuildThisFileDirectory)\index.html" DestinationFolder="$(WasmAppDir)" />
</Target>
</Project> </Project>
...@@ -2,13 +2,6 @@ ...@@ -2,13 +2,6 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets, '$(MSBuildThisFileDirectory)..'))" /> <Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets, '$(MSBuildThisFileDirectory)..'))" />
<PropertyGroup> <PropertyGroup>
<WasmBuildAppDependsOn>PrepareForWasmBuild;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
<WasmAppDir>$(OutputPath)\$(Configuration)\AppBundle\</WasmAppDir> <WasmAppDir>$(OutputPath)\$(Configuration)\AppBundle\</WasmAppDir>
</PropertyGroup> </PropertyGroup>
<Target Name="PrepareForWasmBuild">
<ItemGroup>
<WasmAssembliesToBundle Include="$(TargetDir)publish\*.dll" />
</ItemGroup>
</Target>
</Project> </Project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册