diff --git a/eng/Versions.props b/eng/Versions.props
index a7c00e5d82ee79..7b78ac2770c964 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -166,7 +166,7 @@
2.0.4
4.12.0
2.14.3
- 6.0.100-rc.2.21463.12
+ 6.0.100-rc.2.21474.31
6.0.0-preview-20210916.1
diff --git a/eng/testing/scenarios/BuildWasmAppsJobsList.txt b/eng/testing/scenarios/BuildWasmAppsJobsList.txt
index c4c5a1875c3bfc..8afeea6cdeeb13 100644
--- a/eng/testing/scenarios/BuildWasmAppsJobsList.txt
+++ b/eng/testing/scenarios/BuildWasmAppsJobsList.txt
@@ -11,6 +11,7 @@ Wasm.Build.Tests.LocalEMSDKTests
Wasm.Build.Tests.MainWithArgsTests
Wasm.Build.Tests.NativeBuildTests
Wasm.Build.Tests.NativeLibraryTests
+Wasm.Build.Tests.PInvokeTableGeneratorTests
Wasm.Build.Tests.RebuildTests
Wasm.Build.Tests.SatelliteAssembliesTests
Wasm.Build.Tests.WasmBuildAppTest
diff --git a/src/libraries/workloads-testing.targets b/src/libraries/workloads-testing.targets
index d3dc57267a608a..6a8fa5055bbbb3 100644
--- a/src/libraries/workloads-testing.targets
+++ b/src/libraries/workloads-testing.targets
@@ -16,7 +16,6 @@
-
@@ -41,7 +40,6 @@
-
diff --git a/src/mono/wasm/BlazorOverwrite.targets b/src/mono/wasm/BlazorOverwrite.targets
deleted file mode 100644
index a276d385723725..00000000000000
--- a/src/mono/wasm/BlazorOverwrite.targets
+++ /dev/null
@@ -1,741 +0,0 @@
-
-
-
-
- true
-
-
- true
-
-
-
-
- $(MSBuildThisFileDirectory)..\
- <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net6.0
- <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net472
- <_BlazorWebAssemblySdkTasksAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\$(_BlazorWebAssemblySdkTasksTFM)\Microsoft.NET.Sdk.BlazorWebAssembly.Tasks.dll
- <_BlazorWebAssemblySdkToolAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\net6.0\Microsoft.NET.Sdk.BlazorWebAssembly.Tool.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
- true
-
-
- false
- false
- true
- false
- false
- false
- <_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true
- false
- true
-
-
- false
- false
- false
- false
- true
-
-
- false
-
- <_TargetingNET60OrLater Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '6.0'))">true
-
-
-
- false
-
- true
- true
- false
- ComputeFilesToPublish;_GatherWasmFilesToPublish;$(WasmNestedPublishAppDependsOn)
- <_ScrambleDotnetJsFileNameAfterThisTarget Condition="'$(UsingBrowserRuntimeWorkload)' != 'true'">ResolveRuntimePackAssets
- <_ScrambleDotnetJsFileNameAfterThisTarget Condition="'$(UsingBrowserRuntimeWorkload)' == 'true'">WasmBuildApp
-
-
- Publish
-
-
-
-
-
-
-
-
-
-
-
- $(ResolveStaticWebAssetsInputsDependsOn);
- _AddBlazorWasmStaticWebAssets;
-
-
-
- _GenerateBuildBlazorBootJson;
- $(StaticWebAssetsPrepareForRunDependsOn)
-
-
-
- $(ResolvePublishStaticWebAssetsDependsOn);
- ProcessPublishFilesForBlazor;
- ComputeBlazorExtensions;
- _AddPublishBlazorBootJsonToStaticWebAssets;
-
-
-
- $(GenerateStaticWebAssetsPublishManifestDependsOn);
- GeneratePublishBlazorBootJson;
-
-
-
-
-
-
-
-
-
- <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)
- <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)
- <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js
-
-
-
- <_DotNetJsItem Remove="@(_DotNetJsItem)" />
- <_DotNetJsItem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">
- _framework/$(_BlazorDotnetJsFileName)
- native
- true
-
-
-
-
- <_DotnetJsStaticWebAssetCandidate Remove="@(_DotnetJsStaticWebAssetCandidate)" />
- <_DotnetJsCopyCandidates Remove="@(_DotnetJsCopyCandidates)" />
-
-
-
-
-
-
-
-
-
-
-
- <_DotnetJsStaticWebAsset Include="@(_DotnetJsStaticWebAssetCandidate->'%(ContentRoot)_framework\dotnet.js')" />
- <_BlazorStaticWebAsset Include="@(_DotnetJsStaticWebAsset)" />
-
-
-
-
-
-
- <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)
- <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)
- <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js
-
-
-
- <_DotNetJsItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.DestinationSubPath)' == 'dotnet.js' AND '%(ReferenceCopyLocalPaths.AssetType)' == 'native'">
- _framework/$(_BlazorDotnetJsFileName)
-
-
-
-
- <_DotNetJsItem Remove="@(_DotNetJsItem)" />
- <_DotNetJsItem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">
- _framework/$(_BlazorDotnetJsFileName)
- native
- true
-
-
-
-
-
-
-
-
-
-
-
-
- <_DotnetJsStaticWebAsset Include="@(_DotnetJsStaticWebAssetCandidate->'%(ContentRoot)_framework\dotnet.js')" />
- <_BlazorStaticWebAsset Include="@(_DotnetJsStaticWebAsset)" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorEnableTimeZoneSupport>$(BlazorEnableTimeZoneSupport)
- <_BlazorEnableTimeZoneSupport Condition="'$(_BlazorEnableTimeZoneSupport)' == ''">true
- <_BlazorInvariantGlobalization>$(InvariantGlobalization)
- <_BlazorInvariantGlobalization Condition="'$(_BlazorInvariantGlobalization)' == ''">true
- <_BlazorCopyOutputSymbolsToOutputDirectory>$(CopyOutputSymbolsToOutputDirectory)
- <_BlazorCopyOutputSymbolsToOutputDirectory Condition="'$(_BlazorCopyOutputSymbolsToOutputDirectory)'==''">true
- <_BlazorWebAssemblyLoadAllGlobalizationData>$(BlazorWebAssemblyLoadAllGlobalizationData)
- <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(_BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false
-
-
- $(OutputPath)$(PublishDirName)\
-
-
-
-
-
- <_BlazorJSFile Include="$(BlazorWebAssemblyJSPath)" />
- <_BlazorJSFile Include="$(BlazorWebAssemblyJSMapPath)" Condition="Exists('$(BlazorWebAssemblyJSMapPath)')" />
- <_BlazorJsFile>
- _framework/%(Filename)%(Extension)
-
-
-
-
-
- <_BlazorConfigFileCandidates Include="@(StaticWebAsset)" Condition="'%(SourceType)' == 'Discovered'" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorBuildGZipCompressDirectory>$(IntermediateOutputPath)build-gz\
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorBuildGZipCompressedFile>
- %(RelatedAsset)
-
-
- <_BlazorGzipStaticWebAsset Include="@(_BlazorBuildGZipCompressedFile->'%(FullPath)')" />
-
-
-
- <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json
-
-
-
- <_BuildBlazorBootJson
- Include="$(_BlazorBuildBootJsonPath)"
- RelativePath="_framework/blazor.boot.json" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json
- <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false
-
-
-
- <_BlazorJsModuleCandidatesForBuild
- Include="@(StaticWebAsset)"
- Condition="'%(StaticWebAsset.AssetTraitName)' == 'JSModule' and '%(StaticWebAsset.AssetTraitValue)' == 'JSLibraryModule' and '%(AssetKind)' != 'Publish'" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml
-
-
-
- <_BlazorTypeGranularAssembly
- Include="@(ManagedAssemblyToLink)"
- Condition="'%(Extension)' == '.dll' AND $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))">
- false
- all
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorAotEnabled>$(UsingBrowserRuntimeWorkload)
- <_BlazorAotEnabled Condition="'$(_BlazorAotEnabled)' == ''">false
- <_BlazorLinkerEnabled>$(PublishTrimmed)
- <_BlazorLinkerEnabled Condition="'$(_BlazorLinkerEnabled)' == ''">true
-
-
-
-
-
- <_BlazorPublishPrefilteredAssets
- Include="@(StaticWebAsset)"
- Condition="'%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture' or '%(AssetRole)' == 'Alternative'" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorExtensionsCandidate Include="@(BlazorPublishExtension->'%(FullPath)')">
- $(PackageId)
- Computed
- $(PublishDir)wwwroot
- $(StaticWebAssetBasePath)
- %(BlazorPublishExtension.RelativePath)
- Publish
- All
- Primary
- BlazorWebAssemblyResource
- extension:%(BlazorPublishExtension.ExtensionName)
- Never
- PreserveNewest
- %(BlazorPublishExtension.Identity)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_PublishBlazorBootJson
- Include="$(IntermediateOutputPath)blazor.publish.boot.json"
- RelativePath="_framework/blazor.boot.json" />
-
-
-
-
-
-
-
-
-
-
- <_BlazorPublishAsset
- Include="@(StaticWebAsset)"
- Condition="'%(AssetKind)' != 'Build' and '%(StaticWebAsset.AssetTraitValue)' != 'manifest' and ('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture') and '%(StaticWebAsset.AssetTraitValue)' != 'boot'" />
-
- <_BlazorPublishConfigFile
- Include="@(StaticWebAsset)"
- Condition="'%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' and '%(StaticWebAsset.AssetTraitValue)' == 'settings'"/>
-
- <_BlazorJsModuleCandidatesForPublish
- Include="@(StaticWebAsset)"
- Condition="'%(StaticWebAsset.AssetTraitName)' == 'JSModule' and '%(StaticWebAsset.AssetTraitValue)' == 'JSLibraryModule' and '%(AssetKind)' != 'Build'" />
-
-
- <_BlazorPublishAsset Remove="@(_BlazorExtensionsCandidatesForPublish)" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_CompressedFileOutputPath>$(IntermediateOutputPath)compress\
- <_BlazorWebAssemblyBrotliIncremental>true
-
-
-
- <_DotNetHostDirectory>$(NetCoreRoot)
- <_DotNetHostFileName>dotnet
- <_DotNetHostFileName Condition="'$(OS)' == 'Windows_NT'">dotnet.exe
-
-
-
-
-
-
-
- <_GzipFileToCompressForPublish Include="@(StaticWebAsset)"
- Condition="'%(AssetKind)' != 'Build' and ('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture')" >
- %(Identity)
- Alternative
- Content-Encoding
- gzip
-
-
- <_BrotliFileToCompressForPublish Include="@(_GzipFileToCompressForPublish)" Condition="'%(AssetKind)' != 'Build'">
- br
-
-
-
- <_AlreadyGzipCompressedAssets
- Include="@(StaticWebAsset)"
- Condition="'%(AssetKind)' != 'Build' and ('%(StaticWebAsset.AssetTraitName)' == 'Content-Encoding' and '%(StaticWebAsset.AssetTraitValue)' == 'gzip')" />
- <_GzipFileToCompressForPublish Remove="@(_AlreadyGzipCompressedAssets->'%(RelatedAsset)')" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorPublishGZipCompressedFile>
- %(RelatedAsset)
-
- <_BlazorPublishBrotliCompressedFile>
- %(RelatedAsset)
-
-
-
-
-
-
-
-
diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets
index e4bb5ffb278368..4c74fdde056b9a 100644
--- a/src/mono/wasm/build/WasmApp.Native.targets
+++ b/src/mono/wasm/build/WasmApp.Native.targets
@@ -143,6 +143,7 @@
<_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm'))
<_EmccDefaultFlagsRsp>$([MSBuild]::NormalizePath($(_WasmRuntimePackSrcDir), 'emcc-default.rsp'))
+ false
true
true
$(WasmBuildNative)
@@ -156,8 +157,7 @@
<_EmccAssertionLevelDefault>0
<_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault)
- <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(OS)' != 'Windows_NT' and '$(Configuration)' == 'Debug'">-Os
- <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(Configuration)' != 'Debug'">-Oz
+ <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(Configuration)' == 'Debug' and '$(WasmBuildingForNestedPublish)' != 'true'">-O1
<_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz
$(_EmccOptimizationFlagDefault)
@@ -206,6 +206,9 @@
<_EmccLDFlags Include="@(_EmccCommonFlags)" />
<_EmccLDFlags Include="-s TOTAL_MEMORY=$(EmccTotalMemory)" />
+
+ <_EmccLDFlags Include="-s ERROR_ON_UNDEFINED_SYMBOLS=0" Condition="'$(WasmBuildingForNestedPublish)' != 'true'" />
+
<_DriverCDependencies Include="$(_WasmPInvokeHPath);$(_WasmICallTablePath)" />
<_DriverCDependencies Include="$(_DriverGenCPath)" Condition="'$(_DriverGenCNeeded)' == 'true'" />
@@ -297,7 +300,8 @@
Inputs="@(_BitcodeFile);$(_EmccDefaultFlagsRsp);$(_EmccCompileBitcodeRsp)"
Outputs="@(_BitcodeFile->'%(ObjectFile)')"
Condition="'$(_WasmShouldAOT)' == 'true' and @(_BitcodeFile->Count()) > 0"
- DependsOnTargets="_WasmWriteRspForCompilingBitcode">
+ DependsOnTargets="_WasmWriteRspForCompilingBitcode"
+ Returns="@(FileWrites)">
<_BitCodeFile Dependencies="%(_BitCodeFile.Dependencies);$(_EmccDefaultFlagsRsp);$(_EmccCompileBitcodeRsp)" />
@@ -363,7 +367,8 @@
+ DependsOnTargets="_WasmSelectRuntimeComponentsForLinking;_WasmCompileAssemblyBitCodeFilesForAOT;_WasmWriteRspFilesForLinking"
+ Returns="@(FileWrites)" >
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
index 59f8edae779585..230e8ce3664729 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
@@ -325,6 +325,13 @@ private bool ProcessAndValidateArguments()
throw new LogAsErrorException($"'{nameof(OutputType)}=Library' can not be used with '{nameof(UseStaticLinking)}=true'.");
}
+ foreach (var asmItem in Assemblies)
+ {
+ string? fullPath = asmItem.GetMetadata("FullPath");
+ if (!File.Exists(fullPath))
+ throw new LogAsErrorException($"Could not find {fullPath} to AOT");
+ }
+
return !Log.HasLoggedErrors;
}
@@ -339,6 +346,12 @@ public override bool Execute()
Log.LogError(laee.Message);
return false;
}
+ finally
+ {
+ if (_cache != null && _cache.Save(CacheFilePath!))
+ _fileWrites.Add(CacheFilePath!);
+ FileWrites = _fileWrites.ToArray();
+ }
}
private bool ExecuteInternal()
@@ -347,6 +360,7 @@ private bool ExecuteInternal()
return false;
_assembliesToCompile = EnsureAndGetAssembliesInTheSameDir(Assemblies);
+ _assembliesToCompile = FilterAssemblies(_assembliesToCompile);
if (!string.IsNullOrEmpty(AotModulesTablePath) && !GenerateAotModulesTable(_assembliesToCompile, Profilers, AotModulesTablePath))
return false;
@@ -372,42 +386,29 @@ private bool ExecuteInternal()
}
else
{
- int allowedParallelism = Math.Min(_assembliesToCompile.Count, Environment.ProcessorCount);
+ int allowedParallelism = DisableParallelAot ? 1 : Math.Min(_assembliesToCompile.Count, Environment.ProcessorCount);
if (BuildEngine is IBuildEngine9 be9)
allowedParallelism = be9.RequestCores(allowedParallelism);
- if (DisableParallelAot || allowedParallelism == 1)
+ ParallelLoopResult result = Parallel.ForEach(
+ argsList,
+ new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
+ (args, state) => PrecompileLibraryParallel(args, state));
+
+ Log.LogMessage(MessageImportance.High, $"result: {result.IsCompleted}");
+ if (result.IsCompleted)
{
- foreach (var args in argsList)
- {
- if (!PrecompileLibrarySerial(args))
- return !Log.HasLoggedErrors;
- }
+ int numUnchanged = _totalNumAssemblies - _numCompiled;
+ if (numUnchanged > 0 && numUnchanged != _totalNumAssemblies)
+ Log.LogMessage(MessageImportance.High, $"[{numUnchanged}/{_totalNumAssemblies}] skipped unchanged assemblies.");
}
- else
+ else if (!Log.HasLoggedErrors)
{
- ParallelLoopResult result = Parallel.ForEach(
- argsList,
- new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
- (args, state) => PrecompileLibraryParallel(args, state));
-
- if (!result.IsCompleted)
- {
- return false;
- }
+ Log.LogError($"Precompiling failed due to unknown reasons. Check log for more info");
}
-
- int numUnchanged = _totalNumAssemblies - _numCompiled;
- if (numUnchanged > 0 && numUnchanged != _totalNumAssemblies)
- Log.LogMessage(MessageImportance.High, $"[{numUnchanged}/{_totalNumAssemblies}] skipped unchanged assemblies.");
}
CompiledAssemblies = ConvertAssembliesDictToOrderedList(compiledAssemblies, _assembliesToCompile).ToArray();
-
- if (_cache.Save(CacheFilePath!))
- _fileWrites.Add(CacheFilePath!);
- FileWrites = _fileWrites.ToArray();
-
return !Log.HasLoggedErrors;
}
@@ -428,31 +429,44 @@ static bool IsNewerThanOutput(string inFile, string outFile)
(File.GetLastWriteTimeUtc(inFile) > File.GetLastWriteTimeUtc(outFile));
}
- private IList EnsureAndGetAssembliesInTheSameDir(ITaskItem[] originalAssemblies)
+ private IList FilterAssemblies(IEnumerable assemblies)
{
List filteredAssemblies = new();
- string firstAsmDir = Path.GetDirectoryName(originalAssemblies[0].GetMetadata("FullPath")) ?? string.Empty;
- bool allInSameDir = true;
-
- foreach (var origAsm in originalAssemblies)
+ foreach (var asmItem in assemblies)
{
- if (allInSameDir && Path.GetDirectoryName(origAsm.GetMetadata("FullPath")) != firstAsmDir)
- allInSameDir = false;
-
- if (ShouldSkip(origAsm))
+ if (ShouldSkip(asmItem))
{
if (parsedAotMode == MonoAotMode.LLVMOnly)
- throw new LogAsErrorException($"Building in AOTMode=LLVMonly is not compatible with excluding any assemblies for AOT. Excluded assembly: {origAsm.ItemSpec}");
+ throw new LogAsErrorException($"Building in AOTMode=LLVMonly is not compatible with excluding any assemblies for AOT. Excluded assembly: {asmItem.ItemSpec}");
- Log.LogMessage(MessageImportance.Low, $"Skipping {origAsm.ItemSpec} because it has %(AOT_InternalForceToInterpret)=true");
+ Log.LogMessage(MessageImportance.Low, $"Skipping {asmItem.ItemSpec} because it has %(AOT_InternalForceToInterpret)=true");
continue;
}
- filteredAssemblies.Add(origAsm);
+ string assemblyPath = asmItem.GetMetadata("FullPath");
+ using var assemblyFile = File.OpenRead(assemblyPath);
+ using PEReader reader = new(assemblyFile, PEStreamOptions.Default);
+ if (!reader.HasMetadata)
+ {
+ Log.LogWarning($"Skipping unmanaged {assemblyPath} for AOT");
+ continue;
+ }
+
+ filteredAssemblies.Add(asmItem);
}
+ return filteredAssemblies;
+
+ static bool ShouldSkip(ITaskItem asmItem)
+ => bool.TryParse(asmItem.GetMetadata("AOT_InternalForceToInterpret"), out bool skip) && skip;
+ }
+
+ private IList EnsureAndGetAssembliesInTheSameDir(IList assemblies)
+ {
+ string firstAsmDir = Path.GetDirectoryName(assemblies.First().GetMetadata("FullPath")) ?? string.Empty;
+ bool allInSameDir = assemblies.All(asm => Path.GetDirectoryName(asm.GetMetadata("FullPath")) == firstAsmDir);
if (allInSameDir)
- return filteredAssemblies;
+ return assemblies;
// Copy to aot-in
@@ -460,28 +474,23 @@ private IList EnsureAndGetAssembliesInTheSameDir(ITaskItem[] original
Directory.CreateDirectory(aotInPath);
List newAssemblies = new();
- foreach (var origAsm in originalAssemblies)
+ foreach (var asmItem in assemblies)
{
- string asmPath = origAsm.GetMetadata("FullPath");
+ string asmPath = asmItem.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}");
+ _fileWrites.Add(newPath);
- if (!ShouldSkip(origAsm))
- {
- ITaskItem newAsm = new TaskItem(newPath);
- origAsm.CopyMetadataTo(newAsm);
- newAssemblies.Add(newAsm);
- }
+ ITaskItem newAsm = new TaskItem(newPath);
+ asmItem.CopyMetadataTo(newAsm);
+ newAssemblies.Add(newAsm);
}
return newAssemblies;
-
- static bool ShouldSkip(ITaskItem asmItem)
- => bool.TryParse(asmItem.GetMetadata("AOT_InternalForceToInterpret"), out bool skip) && skip;
}
private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, string? monoPaths)
@@ -773,28 +782,6 @@ private bool PrecompileLibrary(PrecompileArguments args)
return true;
}
- private bool PrecompileLibrarySerial(PrecompileArguments args)
- {
- try
- {
- if (PrecompileLibrary(args))
- return true;
- }
- catch (LogAsErrorException laee)
- {
- Log.LogError($"Precompile failed for {args.AOTAssembly}: {laee.Message}");
- }
- catch (Exception ex)
- {
- if (Log.HasLoggedErrors)
- Log.LogMessage(MessageImportance.Low, $"Precompile failed for {args.AOTAssembly}: {ex}");
- else
- Log.LogError($"Precompile failed for {args.AOTAssembly}: {ex}");
- }
-
- return false;
- }
-
private void PrecompileLibraryParallel(PrecompileArguments args, ParallelLoopState state)
{
try
@@ -804,14 +791,14 @@ private void PrecompileLibraryParallel(PrecompileArguments args, ParallelLoopSta
}
catch (LogAsErrorException laee)
{
- Log.LogError($"Precompile failed for {args.AOTAssembly}: {laee.Message}");
+ Log.LogError($"Precompiling failed for {args.AOTAssembly}: {laee.Message}");
}
catch (Exception ex)
{
if (Log.HasLoggedErrors)
Log.LogMessage(MessageImportance.Low, $"Precompile failed for {args.AOTAssembly}: {ex}");
else
- Log.LogError($"Precompile failed for {args.AOTAssembly}: {ex}");
+ Log.LogError($"Precompiling failed for {args.AOTAssembly}: {ex}");
}
state.Break();
@@ -940,10 +927,8 @@ private static IList ConvertAssembliesDictToOrderedList(ConcurrentDic
List outItems = new(originalAssemblies.Count);
foreach (ITaskItem item in originalAssemblies)
{
- if (!dict.TryGetValue(item.GetMetadata("FullPath"), out ITaskItem? dictItem))
- throw new LogAsErrorException($"Bug: Could not find item in the dict with key {item.ItemSpec}");
-
- outItems.Add(dictItem);
+ if (dict.TryGetValue(item.GetMetadata("FullPath"), out ITaskItem? dictItem))
+ outItems.Add(dictItem);
}
return outItems;
}
@@ -993,7 +978,7 @@ public FileCache(string? cacheFilePath, TaskLoggingHelper log)
}
_oldCache ??= new();
- _newCache = new();
+ _newCache = new(_oldCache.FileHashes);
}
public bool ShouldCopy(ProxyFile proxyFile, [NotNullWhen(true)] out string? cause)
@@ -1110,6 +1095,10 @@ public enum MonoAotModulesTableLanguage
internal class CompilerCache
{
+ public CompilerCache() => FileHashes = new();
+ public CompilerCache(IDictionary oldHashes)
+ => FileHashes = new(oldHashes);
+
[JsonPropertyName("file_hashes")]
- public ConcurrentDictionary FileHashes { get; set; } = new();
+ public ConcurrentDictionary FileHashes { get; set; }
}
diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs
index a5d6af6c3b473a..66d4743d81b4d4 100644
--- a/src/tasks/WasmAppBuilder/EmccCompile.cs
+++ b/src/tasks/WasmAppBuilder/EmccCompile.cs
@@ -95,6 +95,7 @@ private bool ExecuteActual()
if (!ShouldCompile(srcFile, objFile, depFiles, out string reason))
{
Log.LogMessage(MessageImportance.Low, $"Skipping {srcFile} because {reason}.");
+ outputItems.Add(CreateOutputItemFor(srcFile, objFile));
}
else
{
@@ -107,7 +108,8 @@ private bool ExecuteActual()
if (_numCompiled == _totalFiles)
{
// nothing to do!
- return true;
+ OutputFiles = outputItems.ToArray();
+ return !Log.HasLoggedErrors;
}
if (_numCompiled > 0)
@@ -123,31 +125,20 @@ private bool ExecuteActual()
_tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(_tempPath);
- int allowedParallelism = Math.Min(SourceFiles.Length, Environment.ProcessorCount);
+ int allowedParallelism = DisableParallelCompile ? 1 : Math.Min(SourceFiles.Length, Environment.ProcessorCount);
if (BuildEngine is IBuildEngine9 be9)
allowedParallelism = be9.RequestCores(allowedParallelism);
- if (DisableParallelCompile || allowedParallelism == 1)
- {
- foreach ((string srcFile, string outFile) in filesToCompile)
- {
- if (!ProcessSourceFile(srcFile, outFile))
- return false;
- }
- }
- else
+ ParallelLoopResult result = Parallel.ForEach(filesToCompile,
+ new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
+ (toCompile, state) =>
{
- ParallelLoopResult result = Parallel.ForEach(filesToCompile,
- new ParallelOptions { MaxDegreeOfParallelism = allowedParallelism },
- (toCompile, state) =>
- {
- if (!ProcessSourceFile(toCompile.Item1, toCompile.Item2))
- state.Stop();
- });
+ if (!ProcessSourceFile(toCompile.Item1, toCompile.Item2))
+ state.Stop();
+ });
- if (!result.IsCompleted && !Log.HasLoggedErrors)
- Log.LogError("Unknown failure occured while compiling. Check logs to get more details.");
- }
+ if (!result.IsCompleted && !Log.HasLoggedErrors)
+ Log.LogError("Unknown failure occured while compiling. Check logs to get more details.");
if (!Log.HasLoggedErrors)
{
@@ -200,9 +191,7 @@ bool ProcessSourceFile(string srcFile, string objFile)
else
Log.LogMessage(MessageImportance.Low, $"Copied {tmpObjFile} to {objFile}");
- ITaskItem newItem = new TaskItem(objFile);
- newItem.SetMetadata("SourceFile", srcFile);
- outputItems.Add(newItem);
+ outputItems.Add(CreateOutputItemFor(srcFile, objFile));
int count = Interlocked.Increment(ref _numCompiled);
Log.LogMessage(MessageImportance.High, $"[{count}/{_totalFiles}] {Path.GetFileName(srcFile)} -> {Path.GetFileName(objFile)} [took {elapsedSecs:F}s]");
@@ -219,6 +208,13 @@ bool ProcessSourceFile(string srcFile, string objFile)
File.Delete(tmpObjFile);
}
}
+
+ ITaskItem CreateOutputItemFor(string srcFile, string objFile)
+ {
+ ITaskItem newItem = new TaskItem(objFile);
+ newItem.SetMetadata("SourceFile", srcFile);
+ return newItem;
+ }
}
private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, out string reason)
diff --git a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs
index cc4d8af1ac7faa..a9f5f95161575d 100644
--- a/src/tasks/WasmAppBuilder/IcallTableGenerator.cs
+++ b/src/tasks/WasmAppBuilder/IcallTableGenerator.cs
@@ -45,7 +45,7 @@ public void GenIcallTable(string runtimeIcallTableFile, string[] assemblies)
{
ReadTable (runtimeIcallTableFile);
var resolver = new PathAssemblyResolver(assemblies);
- var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib");
+ using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib");
foreach (var aname in assemblies)
{
var a = mlc.LoadFromAssemblyPath(aname);
@@ -107,11 +107,8 @@ private void EmitTable (StreamWriter w)
// Read the icall table generated by mono --print-icall-table
private void ReadTable (string filename)
{
- JsonDocument json;
- using (var stream = File.Open (filename, FileMode.Open))
- {
- json = JsonDocument.Parse (stream);
- }
+ using var stream = File.OpenRead (filename);
+ using JsonDocument json = JsonDocument.Parse (stream);
var arr = json.RootElement;
foreach (var v in arr.EnumerateArray ())
diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
index 825de67d6e2704..feb2dda21d8528 100644
--- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
+++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
@@ -3,13 +3,10 @@
using System;
using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
-using System.Text.Json;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
@@ -65,7 +62,7 @@ public void GenPInvokeTable(string[] pinvokeModules, string[] assemblies)
var callbacks = new List();
var resolver = new PathAssemblyResolver(assemblies);
- var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib");
+ using var mlc = new MetadataLoadContext(resolver, "System.Private.CoreLib");
foreach (var aname in assemblies)
{
var a = mlc.LoadFromAssemblyPath(aname);
@@ -121,25 +118,40 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules
w.WriteLine("// GENERATED FILE, DO NOT MODIFY");
w.WriteLine();
- var decls = new HashSet();
- foreach (var pinvoke in pinvokes.OrderBy(l => l.EntryPoint))
+ var pinvokesGroupedByEntryPoint = pinvokes
+ .Where(l => modules.ContainsKey(l.Module))
+ .OrderBy(l => l.EntryPoint)
+ .GroupBy(l => l.EntryPoint);
+
+ var comparer = new PInvokeComparer();
+ foreach (IGrouping group in pinvokesGroupedByEntryPoint)
{
- if (modules.ContainsKey(pinvoke.Module)) {
- try
- {
- var decl = GenPInvokeDecl(pinvoke);
- if (decls.Contains(decl))
- continue;
+ var candidates = group.Distinct(comparer).ToArray();
+ PInvoke first = candidates[0];
+ if (ShouldTreatAsVariadic(candidates))
+ {
+ string imports = string.Join(Environment.NewLine,
+ candidates.Select(
+ p => $" {p.Method} (in [{p.Method.DeclaringType?.Assembly.GetName().Name}] {p.Method.DeclaringType})"));
+ Log.LogWarning($"Found a native function ({first.EntryPoint}) with varargs in {first.Module}." +
+ " Calling such functions is not supported, and will fail at runtime." +
+ $" Managed DllImports: {Environment.NewLine}{imports}");
- w.WriteLine(decl);
- decls.Add(decl);
- }
- catch (NotSupportedException)
- {
- // See the FIXME in GenPInvokeDecl
- Log.LogWarning($"Cannot handle function pointer arguments/return value in pinvoke method '{pinvoke.Method}' in type '{pinvoke.Method.DeclaringType}'.");
- pinvoke.Skip = true;
- }
+ foreach (var c in candidates)
+ c.Skip = true;
+
+ continue;
+ }
+
+ var decls = new HashSet();
+ foreach (var candidate in candidates)
+ {
+ var decl = GenPInvokeDecl(candidate);
+ if (decl == null || decls.Contains(decl))
+ continue;
+
+ w.WriteLine(decl);
+ decls.Add(decl);
}
}
@@ -186,6 +198,22 @@ static string ModuleNameToId(string name)
return fixedName;
}
+
+ static bool ShouldTreatAsVariadic(PInvoke[] candidates)
+ {
+ if (candidates.Length < 2)
+ return false;
+
+ PInvoke first = candidates[0];
+ if (TryIsMethodGetParametersUnsupported(first.Method, out _))
+ return false;
+
+ int firstNumArgs = first.Method.GetParameters().Length;
+ return candidates
+ .Skip(1)
+ .Any(c => !TryIsMethodGetParametersUnsupported(c.Method, out _) &&
+ c.Method.GetParameters().Length != firstNumArgs);
+ }
}
private string MapType (Type t)
@@ -205,7 +233,29 @@ private string MapType (Type t)
return "int";
}
- private string GenPInvokeDecl(PInvoke pinvoke)
+ // FIXME: System.Reflection.MetadataLoadContext can't decode function pointer types
+ // https://github.com/dotnet/runtime/issues/43791
+ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotNullWhen(true)] out string? reason)
+ {
+ try
+ {
+ method.GetParameters();
+ }
+ catch (NotSupportedException nse)
+ {
+ reason = nse.Message;
+ return true;
+ }
+ catch
+ {
+ // not concerned with other exceptions
+ }
+
+ reason = null;
+ return false;
+ }
+
+ private string? GenPInvokeDecl(PInvoke pinvoke)
{
var sb = new StringBuilder();
var method = pinvoke.Method;
@@ -215,6 +265,14 @@ private string GenPInvokeDecl(PInvoke pinvoke)
sb.Append($"int {pinvoke.EntryPoint} (int, int, int, int, int);");
return sb.ToString();
}
+
+ if (TryIsMethodGetParametersUnsupported(pinvoke.Method, out string? reason))
+ {
+ Log.LogWarning($"Skipping the following DllImport because '{reason}'. {Environment.NewLine} {pinvoke.Method}");
+ pinvoke.Skip = true;
+ return null;
+ }
+
sb.Append(MapType(method.ReturnType));
sb.Append($" {pinvoke.EntryPoint} (");
int pindex = 0;
@@ -366,7 +424,7 @@ private static bool IsBlittable (Type type)
private static void Error (string msg) => throw new LogAsErrorException(msg);
}
-internal class PInvoke
+internal class PInvoke : IEquatable
{
public PInvoke(string entryPoint, string module, MethodInfo method)
{
@@ -379,6 +437,30 @@ public PInvoke(string entryPoint, string module, MethodInfo method)
public string Module;
public MethodInfo Method;
public bool Skip;
+
+ public bool Equals(PInvoke? other)
+ => other != null &&
+ string.Equals(EntryPoint, other.EntryPoint, StringComparison.Ordinal) &&
+ string.Equals(Module, other.Module, StringComparison.Ordinal) &&
+ string.Equals(Method.ToString(), other.Method.ToString(), StringComparison.Ordinal);
+
+ public override string ToString() => $"{{ EntryPoint: {EntryPoint}, Module: {Module}, Method: {Method}, Skip: {Skip} }}";
+}
+
+internal class PInvokeComparer : IEqualityComparer
+{
+ public bool Equals(PInvoke? x, PInvoke? y)
+ {
+ if (x == null && y == null)
+ return true;
+ if (x == null || y == null)
+ return false;
+
+ return x.Equals(y);
+ }
+
+ public int GetHashCode(PInvoke pinvoke)
+ => $"{pinvoke.EntryPoint}{pinvoke.Module}{pinvoke.Method}".GetHashCode();
}
internal class PInvokeCallback
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs
index 687429a3fca482..d4fb7cf49875cf 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs
@@ -1,9 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.IO;
using System.Linq;
+using System.Text.RegularExpressions;
using Xunit;
using Xunit.Abstractions;
@@ -171,6 +171,60 @@ public void WithNativeReference_AOTOnCommandLine(string config)
BlazorPublish(id, config, expectedFileType: NativeFilesType.Relinked);
}
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void WithDllImportInMainAssembly(string config)
+ {
+ // Based on https://github.com/dotnet/runtime/issues/59255
+ string id = $"blz_dllimp_{config}";
+ string projectFile = CreateProjectWithNativeReference(id);
+ string nativeSource = @"
+ #include
+
+ extern ""C"" {
+ int cpp_add(int a, int b) {
+ return a + b;
+ }
+ }";
+
+ File.WriteAllText(Path.Combine(_projectDir!, "mylib.cpp"), nativeSource);
+
+ string myDllImportCs = @$"
+ using System.Runtime.InteropServices;
+ namespace {id};
+
+ public static class MyDllImports
+ {{
+ [DllImport(""mylib"")]
+ public static extern int cpp_add(int a, int b);
+ }}";
+
+ File.WriteAllText(Path.Combine(_projectDir!, "Pages", "MyDllImport.cs"), myDllImportCs);
+
+ AddItemsPropertiesToProject(projectFile, extraItems: @"");
+
+ BlazorBuild(id, config, expectedFileType: NativeFilesType.Relinked);
+ CheckNativeFileLinked(forPublish: false);
+
+ BlazorPublish(id, config, expectedFileType: NativeFilesType.Relinked);
+ CheckNativeFileLinked(forPublish: true);
+
+ void CheckNativeFileLinked(bool forPublish)
+ {
+ // very crude way to check that the native file was linked in
+ // needed because we don't run the blazor app yet
+ string objBuildDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm", forPublish ? "for-publish" : "for-build");
+ string pinvokeTableHPath = Path.Combine(objBuildDir, "pinvoke-table.h");
+ Assert.True(File.Exists(pinvokeTableHPath), $"Could not find {pinvokeTableHPath}");
+
+ string pinvokeTableHContents = File.ReadAllText(pinvokeTableHPath);
+ string pattern = $"\"cpp_add\".*{id}";
+ Assert.True(Regex.IsMatch(pinvokeTableHContents, pattern),
+ $"Could not find {pattern} in {pinvokeTableHPath}");
+ }
+ }
+
private string CreateProjectWithNativeReference(string id)
{
CreateBlazorWasmTemplateProject(id);
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
index 6c7e3ed788be56..727e6589c4c6d2 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
@@ -79,6 +79,7 @@ private CommandResult PublishForRequiresWorkloadTest(string config, string extra
[Theory]
[InlineData("Debug")]
[InlineData("Release")]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/59538")]
public void Net50Projects_NativeReference(string config)
=> BuildNet50Project(config, aot: false, expectError: true, @"");
@@ -92,6 +93,7 @@ public void Net50Projects_NativeReference(string config)
[Theory]
[MemberData(nameof(Net50TestData))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/59538")]
public void Net50Projects_AOT(string config, bool aot, bool expectError)
=> BuildNet50Project(config, aot: aot, expectError: expectError);
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
index 3ccf400ee92113..eb306286ba5e0d 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.IO;
using Xunit;
using Xunit.Abstractions;
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
index 9047fd062a82e4..cd0d3ac4396349 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
@@ -77,6 +77,7 @@ protected string Rebuild(bool nativeRelink, bool invariant, BuildArgs buildArgs,
buildArgs = newBuildArgs;
_testOutput.WriteLine($"{Environment.NewLine}Rebuilding with no changes ..{Environment.NewLine}");
+ Console.WriteLine($"{Environment.NewLine}Rebuilding with no changes ..{Environment.NewLine}");
(_, string output) = BuildProject(buildArgs,
id: id,
dotnetWasmFromRuntimePack: false,
@@ -137,6 +138,18 @@ internal void CompareStat(IDictionary oldStat, IDictionary GetFilesTable(bool unchanged, params string[] baseDirs)
+ {
+ var dict = new Dictionary();
+ foreach (var baseDir in baseDirs)
+ {
+ foreach (var file in Directory.EnumerateFiles(baseDir, "*", new EnumerationOptions { RecurseSubdirectories = true }))
+ dict[Path.GetFileName(file)] = (file, unchanged);
+ }
+
+ return dict;
+ }
+
internal IDictionary GetFilesTable(BuildArgs buildArgs, BuildPaths paths, bool unchanged)
{
List files = new()
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
index 96f6d41fa10025..3131dec390ce05 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.IO;
using System.Linq;
using Wasm.Build.Tests;
using Xunit;
@@ -33,5 +34,60 @@ public void NoOpRebuildForNativeBuilds(BuildArgs buildArgs, bool nativeRelink, b
CompareStat(originalStat, newStat, pathsDict.Values);
RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
}
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void BlazorNoopRebuild(string config)
+ {
+ string id = $"blz_rebuild_{config}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: "true");
+
+ string objDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm");
+
+ BlazorBuild(id, config, NativeFilesType.Relinked);
+ File.Move(Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-build.binlog"),
+ Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-build-first.binlog"));
+
+ var pathsDict = GetFilesTable(true, objDir);
+ pathsDict.Remove("runtime-icall-table.h");
+ var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ // build again
+ BlazorBuild(id, config, NativeFilesType.Relinked);
+ var newStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ CompareStat(originalStat, newStat, pathsDict.Values);
+ }
+
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void BlazorOnlyLinkRebuild(string config)
+ {
+ string id = $"blz_relink_{config}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: "true");
+
+ string objDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm");
+
+ BlazorBuild(id, config, NativeFilesType.Relinked);
+ File.Move(Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-build.binlog"),
+ Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-build-first.binlog"));
+
+ var pathsDict = GetFilesTable(true, objDir);
+ pathsDict.Remove("runtime-icall-table.h");
+ pathsDict.UpdateTo(unchanged: false, "dotnet.wasm", "dotnet.js", "emcc-link.rsp");
+
+ var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ // build again
+ BlazorBuild(id, config, NativeFilesType.Relinked, "-p:EmccLinkOptimizationFlag=-O1");
+ var newStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ CompareStat(originalStat, newStat, pathsDict.Values);
+ }
}
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs
new file mode 100644
index 00000000000000..27783ae85c95ad
--- /dev/null
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs
@@ -0,0 +1,139 @@
+// 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;
+
+#nullable enable
+
+namespace Wasm.Build.Tests
+{
+ public class PInvokeTableGeneratorTests : BuildTestBase
+ {
+ public PInvokeTableGeneratorTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [BuildAndRun(host: RunHost.V8)]
+ public void NativeLibraryWithVariadicFunctions(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string code = @"
+ using System;
+ using System.Runtime.InteropServices;
+ public class Test
+ {
+ public static int Main(string[] args)
+ {
+ Console.WriteLine($""Main running"");
+ if (args.Length > 0)
+ {
+ // We don't want to run this, because we can't call variadic functions
+ Console.WriteLine($""sum_three: {sum_three(7, 14, 21)}"");
+ Console.WriteLine($""sum_two: {sum_two(3, 6)}"");
+ Console.WriteLine($""sum_one: {sum_one(5)}"");
+ }
+ return 42;
+ }
+
+ [DllImport(""variadic"", EntryPoint=""sum"")] public static extern int sum_one(int a);
+ [DllImport(""variadic"", EntryPoint=""sum"")] public static extern int sum_two(int a, int b);
+ [DllImport(""variadic"", EntryPoint=""sum"")] public static extern int sum_three(int a, int b, int c);
+ }";
+
+ (buildArgs, string output) = BuildForVariadicFunctionTests(code,
+ buildArgs with { ProjectName = $"variadic_{buildArgs.Config}_{id}" },
+ id);
+ Assert.Matches("warning.*native function.*sum.*varargs", output);
+ Assert.Matches("warning.*sum_(one|two|three)", output);
+
+ output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
+ Assert.Contains("Main running", output);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [BuildAndRun(host: RunHost.V8)]
+ public void DllImportWithFunctionPointersCompilesWithWarning(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string code = @"
+ using System;
+ using System.Runtime.InteropServices;
+ public class Test
+ {
+ public static int Main()
+ {
+ Console.WriteLine($""Main running"");
+ return 42;
+ }
+
+ [DllImport(""variadic"", EntryPoint=""sum"")]
+ public unsafe static extern int using_sum_one(delegate* unmanaged callback);
+
+ [DllImport(""variadic"", EntryPoint=""sum"")]
+ public static extern int sum_one(int a, int b);
+ }";
+
+ (buildArgs, string output) = BuildForVariadicFunctionTests(code,
+ buildArgs with { ProjectName = $"fnptr_{buildArgs.Config}_{id}" },
+ id);
+ Assert.Matches("warning.*Skipping.*because.*function pointer", output);
+ Assert.Matches("warning.*using_sum_one", output);
+
+ output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
+ Assert.Contains("Main running", output);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [BuildAndRun(host: RunHost.V8)]
+ public void DllImportWithFunctionPointers_ForVariadicFunction_CompilesWithWarning(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string code = @"
+ using System;
+ using System.Runtime.InteropServices;
+ public class Test
+ {
+ public static int Main()
+ {
+ Console.WriteLine($""Main running"");
+ return 42;
+ }
+
+ [DllImport(""variadic"", EntryPoint=""sum"")]
+ public unsafe static extern int using_sum_one(delegate* unmanaged callback);
+ }";
+
+ (buildArgs, string output) = BuildForVariadicFunctionTests(code,
+ buildArgs with { ProjectName = $"fnptr_variadic_{buildArgs.Config}_{id}" },
+ id);
+ Assert.Matches("warning.*Skipping.*because.*function pointer", output);
+ Assert.Matches("warning.*using_sum_one", output);
+
+ output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
+ Assert.Contains("Main running", output);
+ }
+
+ private (BuildArgs, string) BuildForVariadicFunctionTests(string programText, BuildArgs buildArgs, string id)
+ {
+ string filename = "variadic.o";
+ buildArgs = ExpandBuildArgs(buildArgs,
+ extraItems: $"",
+ extraProperties: "true<_WasmDevel>true");
+
+ (_, string output) = BuildProject(buildArgs,
+ initProject: () =>
+ {
+ File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText);
+ File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", filename),
+ Path.Combine(_projectDir!, filename));
+ },
+ publish: buildArgs.AOT,
+ id: id,
+ dotnetWasmFromRuntimePack: false);
+
+ return (buildArgs, output);
+ }
+ }
+}
diff --git a/src/tests/BuildWasmApps/testassets/native-libs/variadic.c b/src/tests/BuildWasmApps/testassets/native-libs/variadic.c
new file mode 100644
index 00000000000000..cd4009439d19da
--- /dev/null
+++ b/src/tests/BuildWasmApps/testassets/native-libs/variadic.c
@@ -0,0 +1,14 @@
+#include
+
+int sum(int n, ...)
+{
+ int result = 0;
+ va_list ptr;
+ va_start(ptr, n);
+
+ for (int i = 0; i < n; i++)
+ result += va_arg(ptr, int);
+
+ va_end(ptr);
+ return result;
+}
diff --git a/src/tests/BuildWasmApps/testassets/native-libs/variadic.o b/src/tests/BuildWasmApps/testassets/native-libs/variadic.o
new file mode 100644
index 00000000000000..b4558ce3519793
Binary files /dev/null and b/src/tests/BuildWasmApps/testassets/native-libs/variadic.o differ