aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefanStojanovic <StefanStojanovic@users.noreply.github.com>2022-10-25 22:40:36 +0200
committerGitHub <noreply@github.com>2022-10-25 20:40:36 +0000
commit321c30138c82390ea5ad6b0a612dff294203a877 (patch)
tree2f27ca578d9b162eac11f0c8bee460b17138b531
parent98080672cdbbde00ea40a96c1ce38e8a52f24fee (diff)
downloadwix-321c30138c82390ea5ad6b0a612dff294203a877.tar.gz
wix-321c30138c82390ea5ad6b0a612dff294203a877.tar.bz2
wix-321c30138c82390ea5ad6b0a612dff294203a877.zip
Add NetFx .NET compatibility check for MSI (#262)
Adds new custom element in NetFx extension for running NetCoreCheck.exe tool from within the MSI installer - `<netfx:DotNetCompatibilityCheck />`. The checks are run before evaluating launch conditions, so their results can be used in those conditions. There is no limitation on the number of checks that can be run, so installer may query various runtimes on different platforms and versions and with different roll forward policies. Fixes https://github.com/wixtoolset/issues/issues/6264
-rw-r--r--src/ext/NetFx/ca/netfxca.cpp144
-rw-r--r--src/ext/NetFx/ca/netfxca.def1
-rw-r--r--src/ext/NetFx/ca/netfxca.vcxproj2
-rw-r--r--src/ext/NetFx/ca/precomp.h3
-rw-r--r--src/ext/NetFx/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs12
-rw-r--r--src/ext/NetFx/wixext/NetFxCompiler.cs213
-rw-r--r--src/ext/NetFx/wixext/NetFxExtensionData.cs2
-rw-r--r--src/ext/NetFx/wixext/NetfxTableDefinitions.cs16
-rw-r--r--src/ext/NetFx/wixext/Symbols/NetFxDotNetCompatibilityCheckSymbol.cs79
-rw-r--r--src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs6
-rw-r--r--src/ext/NetFx/wixlib/NetFxExtension_Platform.wxi9
-rw-r--r--src/ext/NetFx/wixlib/netfx.wixproj6
-rw-r--r--src/internal/SetBuildNumber/Directory.Packages.props.pp6
13 files changed, 496 insertions, 3 deletions
diff --git a/src/ext/NetFx/ca/netfxca.cpp b/src/ext/NetFx/ca/netfxca.cpp
index f0704790..cd086c53 100644
--- a/src/ext/NetFx/ca/netfxca.cpp
+++ b/src/ext/NetFx/ca/netfxca.cpp
@@ -36,6 +36,10 @@ LPCWSTR vcsNgenStrongName =
36 L"SELECT `Name`,`Value` FROM `MsiAssemblyName` WHERE `Component_`=?"; 36 L"SELECT `Name`,`Value` FROM `MsiAssemblyName` WHERE `Component_`=?";
37enum eNgenStrongName { ngsnName = 1, ngsnValue }; 37enum eNgenStrongName { ngsnName = 1, ngsnValue };
38 38
39LPCWSTR vscDotNetCompatibilityCheckQuery =
40 L"SELECT `Platform`, `RuntimeType`, `Version`, `RollForward`, `Property` FROM `Wix4NetFxDotNetCheck`";
41enum eDotNetCompatibilityCheckQuery { platform = 1, runtimeType, version, rollForward, property };
42
39// Searches subdirectories of the given path for the highest version of ngen.exe available 43// Searches subdirectories of the given path for the highest version of ngen.exe available
40static HRESULT GetNgenVersion( 44static HRESULT GetNgenVersion(
41 __in LPWSTR pwzParentPath, 45 __in LPWSTR pwzParentPath,
@@ -500,6 +504,15 @@ extern "C" UINT __stdcall SchedNetFx(
500 hr = WcaInitialize(hInstall, "SchedNetFx"); 504 hr = WcaInitialize(hInstall, "SchedNetFx");
501 ExitOnFailure(hr, "failed to initialize"); 505 ExitOnFailure(hr, "failed to initialize");
502 506
507 // If Wix4NetFxNativeImage table doesn't exist skip the rest of this custom action
508 hr = WcaTableExists(L"Wix4NetFxNativeImage");
509 if (S_FALSE == hr)
510 {
511 hr = S_OK;
512 ExitFunction();
513 }
514 ExitOnFailure(hr, "failed to check if table Wix4NetFxNativeImage exists");
515
503 hr = GetNgenPath(&pwz32Ngen, FALSE); 516 hr = GetNgenPath(&pwz32Ngen, FALSE);
504 f32NgenExeExists = SUCCEEDED(hr); 517 f32NgenExeExists = SUCCEEDED(hr);
505 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr) 518 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
@@ -879,3 +892,134 @@ LExit:
879 return WcaFinalize(er); 892 return WcaFinalize(er);
880} 893}
881 894
895/******************************************************************
896 DotNetCompatibilityCheck - entry point for NetFx Custom Action
897
898*******************************************************************/
899extern "C" UINT __stdcall DotNetCompatibilityCheck(
900 __in MSIHANDLE hInstall
901 )
902{
903// AssertSz(FALSE, "debug DotNetCompatibilityCheck");
904
905 HRESULT hr = S_OK;
906 UINT er = ERROR_SUCCESS;
907
908 PMSIHANDLE hView = NULL;
909 PMSIHANDLE hRec = NULL;
910 LPWSTR pwzPlatform = NULL;
911 LPWSTR pwzNetCoreCheckBinaryId = NULL;
912 LPWSTR pwzNetCoreCheckDirectoryName = NULL;
913 LPWSTR pwzNetCoreCheckDirectoryPath = NULL;
914 LPWSTR pwzNetCoreCheckFilePath = NULL;
915 LPWSTR pwzRuntimeType = NULL;
916 LPWSTR pwzVersion = NULL;
917 LPWSTR pwzRollForward = NULL;
918 LPWSTR pwzProperty = NULL;
919 LPWSTR pwzCommandLine = NULL;
920 HANDLE hProcess = NULL;
921 DWORD dwExitCode = 0;
922
923 // Initialize
924 hr = WcaInitialize(hInstall, "DotNetCompatibilityCheck");
925 ExitOnFailure(hr, "failed to initialize");
926
927 // If Wix4NetFxDotNetCheck table doesn't exist skip the rest of this custom action
928 hr = WcaTableExists(L"Wix4NetFxDotNetCheck");
929 if (S_FALSE == hr)
930 {
931 hr = S_OK;
932 ExitFunction();
933 }
934 ExitOnFailure(hr, "failed to check if table Wix4NetFxDotNetCheck exists");
935
936 // Open view on .NET compatibility check table
937 hr = WcaOpenExecuteView(vscDotNetCompatibilityCheckQuery, &hView);
938 ExitOnFailure(hr, "failed to open view on Wix4NetFxDotNetCheck table");
939
940 // Go through all records and run NetCorCheck.exe for each
941 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
942 {
943 // Extract NetCoreCheck.exe for platform to temp directory
944 hr = WcaGetRecordString(hRec, platform, &pwzPlatform);
945 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Platform");
946
947 hr = StrAllocFormatted(&pwzNetCoreCheckBinaryId, L"Wix4NetCheck_%ls", pwzPlatform);
948 ExitOnFailure(hr, "failed to get NetCoreCheck binary id for platform %ls", pwzPlatform);
949
950 hr = GuidCreate(&pwzNetCoreCheckDirectoryName);
951 ExitOnFailure(hr, "failed to set NetCoreCheck directory name");
952
953 hr = PathCreateTempDirectory(NULL, pwzNetCoreCheckDirectoryName, 1, &pwzNetCoreCheckDirectoryPath);
954 ExitOnFailure(hr, "failed to make NetCoreCheck directory path for name %ls", pwzNetCoreCheckDirectoryName);
955
956 hr = StrAllocFormatted(&pwzNetCoreCheckFilePath, L"%lsNetCoreCheck.exe", pwzNetCoreCheckDirectoryPath);
957 ExitOnFailure(hr, "failed to set NetCoreCheck file path for directory %ls", pwzNetCoreCheckDirectoryPath);
958
959 hr = WcaExtractBinaryToFile(pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath);
960 ExitOnFailure(hr, "failed to extract NetCoreCheck from binary '%ls' to file %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath);
961
962 // Read all NetCoreCheck.exe parameters and property
963 hr = WcaGetRecordString(hRec, runtimeType, &pwzRuntimeType);
964 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.RuntimeType");
965
966 hr = WcaGetRecordString(hRec, version, &pwzVersion);
967 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Version");
968
969 hr = WcaGetRecordString(hRec, rollForward, &pwzRollForward);
970 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.RollForward");
971
972 hr = WcaGetRecordString(hRec, property, &pwzProperty);
973 ExitOnFailure(hr, "failed to get Wix4NetFxDotNetCheck.Property");
974
975 // Run NetCoreCheck.exe and store its result in property
976 hr = StrAllocFormatted(&pwzCommandLine, L"-n %ls -v %ls -r %ls", pwzRuntimeType, pwzVersion, pwzRollForward);
977 ExitOnFailure(hr, "failed to set NetCoreCheck command line");
978 WcaLog(LOGMSG_VERBOSE, "Command: %ls %ls", pwzNetCoreCheckFilePath, pwzCommandLine);
979
980 hr = ProcExec(pwzNetCoreCheckFilePath, pwzCommandLine, SW_HIDE, &hProcess);
981 ExitOnFailure(hr, "failed to run NetCoreCheck from binary '%ls' with command line: %ls %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath, pwzCommandLine);
982
983 hr = ProcWaitForCompletion(hProcess, INFINITE, &dwExitCode);
984 ExitOnFailure(hr, "failed to finish NetCoreCheck from binary '%ls' with command line: %ls %ls", pwzNetCoreCheckBinaryId, pwzNetCoreCheckFilePath, pwzCommandLine);
985 WcaLog(LOGMSG_VERBOSE, "Exit code: %lu", dwExitCode);
986 ReleaseHandle(hProcess);
987
988 hr = WcaSetIntProperty(pwzProperty, dwExitCode);
989 ExitOnFailure(hr, "failed to set NetCoreCheck result in %ls", pwzProperty);
990
991 // Delete extracted NetCoreCheck.exe
992 DirEnsureDelete(pwzNetCoreCheckDirectoryPath, TRUE, TRUE);
993 }
994 if (E_NOMOREITEMS == hr)
995 {
996 hr = S_OK;
997 }
998 ExitOnFailure(hr, "failed while looping through all dot net compatibility checks");
999
1000LExit:
1001 // Delete extracted NetCoreCheck.exe
1002 if (NULL != pwzNetCoreCheckDirectoryPath)
1003 {
1004 DirEnsureDelete(pwzNetCoreCheckDirectoryPath, TRUE, TRUE);
1005 }
1006
1007 // Release allocated resources
1008 ReleaseStr(pwzPlatform);
1009 ReleaseStr(pwzNetCoreCheckBinaryId);
1010 ReleaseStr(pwzNetCoreCheckDirectoryName);
1011 ReleaseStr(pwzNetCoreCheckDirectoryPath);
1012 ReleaseStr(pwzNetCoreCheckFilePath);
1013 ReleaseStr(pwzRuntimeType);
1014 ReleaseStr(pwzVersion);
1015 ReleaseStr(pwzRollForward);
1016 ReleaseStr(pwzProperty);
1017 ReleaseStr(pwzCommandLine);
1018 ReleaseHandle(hProcess);
1019
1020 if (FAILED(hr))
1021 {
1022 er = ERROR_INSTALL_FAILURE;
1023 }
1024 return WcaFinalize(er);
1025}
diff --git a/src/ext/NetFx/ca/netfxca.def b/src/ext/NetFx/ca/netfxca.def
index c1d01f5f..3b930756 100644
--- a/src/ext/NetFx/ca/netfxca.def
+++ b/src/ext/NetFx/ca/netfxca.def
@@ -6,3 +6,4 @@ LIBRARY "netfxca"
6EXPORTS 6EXPORTS
7 SchedNetFx 7 SchedNetFx
8 ExecNetFx 8 ExecNetFx
9 DotNetCompatibilityCheck
diff --git a/src/ext/NetFx/ca/netfxca.vcxproj b/src/ext/NetFx/ca/netfxca.vcxproj
index 93276ea1..0158a656 100644
--- a/src/ext/NetFx/ca/netfxca.vcxproj
+++ b/src/ext/NetFx/ca/netfxca.vcxproj
@@ -42,7 +42,7 @@
42 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> 42 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
43 43
44 <PropertyGroup> 44 <PropertyGroup>
45 <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries> 45 <ProjectAdditionalLinkLibraries>msi.lib;rpcrt4.lib</ProjectAdditionalLinkLibraries>
46 </PropertyGroup> 46 </PropertyGroup>
47 47
48 <ItemGroup> 48 <ItemGroup>
diff --git a/src/ext/NetFx/ca/precomp.h b/src/ext/NetFx/ca/precomp.h
index f7b537ed..db618bce 100644
--- a/src/ext/NetFx/ca/precomp.h
+++ b/src/ext/NetFx/ca/precomp.h
@@ -10,6 +10,9 @@
10#include "fileutil.h" 10#include "fileutil.h"
11#include "strutil.h" 11#include "strutil.h"
12#include "pathutil.h" 12#include "pathutil.h"
13#include "procutil.h"
14#include "dirutil.h"
15#include "guidutil.h"
13 16
14#include "caDecor.h" 17#include "caDecor.h"
15#include "cost.h" 18#include "cost.h"
diff --git a/src/ext/NetFx/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs b/src/ext/NetFx/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs
index 6a75f30f..a03d47d6 100644
--- a/src/ext/NetFx/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs
+++ b/src/ext/NetFx/test/WixToolsetTest.Netfx/NetfxExtensionFixture.cs
@@ -85,7 +85,11 @@ namespace WixToolsetTest.Netfx
85 var results = build.BuildAndQuery(Build, "Binary", "CustomAction", "Wix4NetFxNativeImage"); 85 var results = build.BuildAndQuery(Build, "Binary", "CustomAction", "Wix4NetFxNativeImage");
86 WixAssert.CompareLineByLine(new[] 86 WixAssert.CompareLineByLine(new[]
87 { 87 {
88 "Binary:Wix4NetCheck_arm64\t[Binary data]",
89 "Binary:Wix4NetCheck_x64\t[Binary data]",
90 "Binary:Wix4NetCheck_x86\t[Binary data]",
88 "Binary:Wix4NetFxCA_X86\t[Binary data]", 91 "Binary:Wix4NetFxCA_X86\t[Binary data]",
92 "CustomAction:Wix4NetFxDotNetCompatibilityCheck_X86\t1\tWix4NetFxCA_X86\tDotNetCompatibilityCheck\t",
89 "CustomAction:Wix4NetFxExecuteNativeImageCommitInstall_X86\t3649\tWix4NetFxCA_X86\tExecNetFx\t", 93 "CustomAction:Wix4NetFxExecuteNativeImageCommitInstall_X86\t3649\tWix4NetFxCA_X86\tExecNetFx\t",
90 "CustomAction:Wix4NetFxExecuteNativeImageCommitUninstall_X86\t3649\tWix4NetFxCA_X86\tExecNetFx\t", 94 "CustomAction:Wix4NetFxExecuteNativeImageCommitUninstall_X86\t3649\tWix4NetFxCA_X86\tExecNetFx\t",
91 "CustomAction:Wix4NetFxExecuteNativeImageInstall_X86\t3137\tWix4NetFxCA_X86\tExecNetFx\t", 95 "CustomAction:Wix4NetFxExecuteNativeImageInstall_X86\t3137\tWix4NetFxCA_X86\tExecNetFx\t",
@@ -104,7 +108,11 @@ namespace WixToolsetTest.Netfx
104 var results = build.BuildAndQuery(BuildX64, "Binary", "CustomAction", "Wix4NetFxNativeImage"); 108 var results = build.BuildAndQuery(BuildX64, "Binary", "CustomAction", "Wix4NetFxNativeImage");
105 WixAssert.CompareLineByLine(new[] 109 WixAssert.CompareLineByLine(new[]
106 { 110 {
111 "Binary:Wix4NetCheck_arm64\t[Binary data]",
112 "Binary:Wix4NetCheck_x64\t[Binary data]",
113 "Binary:Wix4NetCheck_x86\t[Binary data]",
107 "Binary:Wix4NetFxCA_X64\t[Binary data]", 114 "Binary:Wix4NetFxCA_X64\t[Binary data]",
115 "CustomAction:Wix4NetFxDotNetCompatibilityCheck_X64\t1\tWix4NetFxCA_X64\tDotNetCompatibilityCheck\t",
108 "CustomAction:Wix4NetFxExecuteNativeImageCommitInstall_X64\t3649\tWix4NetFxCA_X64\tExecNetFx\t", 116 "CustomAction:Wix4NetFxExecuteNativeImageCommitInstall_X64\t3649\tWix4NetFxCA_X64\tExecNetFx\t",
109 "CustomAction:Wix4NetFxExecuteNativeImageCommitUninstall_X64\t3649\tWix4NetFxCA_X64\tExecNetFx\t", 117 "CustomAction:Wix4NetFxExecuteNativeImageCommitUninstall_X64\t3649\tWix4NetFxCA_X64\tExecNetFx\t",
110 "CustomAction:Wix4NetFxExecuteNativeImageInstall_X64\t3137\tWix4NetFxCA_X64\tExecNetFx\t", 118 "CustomAction:Wix4NetFxExecuteNativeImageInstall_X64\t3137\tWix4NetFxCA_X64\tExecNetFx\t",
@@ -123,7 +131,11 @@ namespace WixToolsetTest.Netfx
123 var results = build.BuildAndQuery(BuildARM64, "Binary", "CustomAction", "Wix4NetFxNativeImage"); 131 var results = build.BuildAndQuery(BuildARM64, "Binary", "CustomAction", "Wix4NetFxNativeImage");
124 WixAssert.CompareLineByLine(new[] 132 WixAssert.CompareLineByLine(new[]
125 { 133 {
134 "Binary:Wix4NetCheck_arm64\t[Binary data]",
135 "Binary:Wix4NetCheck_x64\t[Binary data]",
136 "Binary:Wix4NetCheck_x86\t[Binary data]",
126 "Binary:Wix4NetFxCA_A64\t[Binary data]", 137 "Binary:Wix4NetFxCA_A64\t[Binary data]",
138 "CustomAction:Wix4NetFxDotNetCompatibilityCheck_A64\t1\tWix4NetFxCA_A64\tDotNetCompatibilityCheck\t",
127 "CustomAction:Wix4NetFxExecuteNativeImageCommitInstall_A64\t3649\tWix4NetFxCA_A64\tExecNetFx\t", 139 "CustomAction:Wix4NetFxExecuteNativeImageCommitInstall_A64\t3649\tWix4NetFxCA_A64\tExecNetFx\t",
128 "CustomAction:Wix4NetFxExecuteNativeImageCommitUninstall_A64\t3649\tWix4NetFxCA_A64\tExecNetFx\t", 140 "CustomAction:Wix4NetFxExecuteNativeImageCommitUninstall_A64\t3649\tWix4NetFxCA_A64\tExecNetFx\t",
129 "CustomAction:Wix4NetFxExecuteNativeImageInstall_A64\t3137\tWix4NetFxCA_A64\tExecNetFx\t", 141 "CustomAction:Wix4NetFxExecuteNativeImageInstall_A64\t3137\tWix4NetFxCA_A64\tExecNetFx\t",
diff --git a/src/ext/NetFx/wixext/NetFxCompiler.cs b/src/ext/NetFx/wixext/NetFxCompiler.cs
index 739618e9..f3c91918 100644
--- a/src/ext/NetFx/wixext/NetFxCompiler.cs
+++ b/src/ext/NetFx/wixext/NetFxCompiler.cs
@@ -40,7 +40,6 @@ namespace WixToolset.Netfx
40 break; 40 break;
41 } 41 }
42 break; 42 break;
43 case "Bundle":
44 case "Fragment": 43 case "Fragment":
45 switch (element.Name.LocalName) 44 switch (element.Name.LocalName)
46 { 45 {
@@ -50,8 +49,45 @@ namespace WixToolset.Netfx
50 case "DotNetCoreSearchRef": 49 case "DotNetCoreSearchRef":
51 this.ParseDotNetCoreSearchRefElement(intermediate, section, element); 50 this.ParseDotNetCoreSearchRefElement(intermediate, section, element);
52 break; 51 break;
52 case "DotNetCompatibilityCheck":
53 this.ParseDotNetCompatibilityCheckElement(intermediate, section, element);
54 break;
55 case "DotNetCompatibilityCheckRef":
56 this.ParseDotNetCompatibilityCheckRefElement(intermediate, section, element);
57 break;
58 default:
59 this.ParseHelper.UnexpectedElement(parentElement, element);
60 break;
61 }
62 break;
63 case "Bundle":
64 switch (element.Name.LocalName)
65 {
66 case "DotNetCoreSearch":
67 this.ParseDotNetCoreSearchElement(intermediate, section, element);
68 break;
69 case "DotNetCoreSearchRef":
70 this.ParseDotNetCoreSearchRefElement(intermediate, section, element);
71 break;
72 default:
73 this.ParseHelper.UnexpectedElement(parentElement, element);
74 break;
75 }
76 break;
77 case "Package":
78 case "Module":
79 switch (element.Name.LocalName)
80 {
81 case "DotNetCompatibilityCheck":
82 this.ParseDotNetCompatibilityCheckElement(intermediate, section, element);
83 break;
84 case "DotNetCompatibilityCheckRef":
85 this.ParseDotNetCompatibilityCheckRefElement(intermediate, section, element);
86 break;
87 default:
88 this.ParseHelper.UnexpectedElement(parentElement, element);
89 break;
53 } 90 }
54
55 break; 91 break;
56 default: 92 default:
57 this.ParseHelper.UnexpectedElement(parentElement, element); 93 this.ParseHelper.UnexpectedElement(parentElement, element);
@@ -325,5 +361,178 @@ namespace WixToolset.Netfx
325 }); 361 });
326 } 362 }
327 } 363 }
364
365 /// <summary>
366 /// Parses a DotNetCompatibilityCheck element.
367 /// </summary>
368 /// <param name="element">The element to parse.</param>
369 private void ParseDotNetCompatibilityCheckElement(Intermediate intermediate, IntermediateSection section, XElement element)
370 {
371 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
372 Identifier id = null;
373 string property = null;
374 string runtimeType = null;
375 string platform = null;
376 string version = null;
377 string rollForward = "Minor";
378
379 foreach (var attrib in element.Attributes())
380 {
381 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
382 {
383 switch (attrib.Name.LocalName)
384 {
385 case "Id":
386 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
387 break;
388 case "Property":
389 property = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
390 break;
391 case "RuntimeType":
392 runtimeType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
393 switch (runtimeType.ToLower())
394 {
395 case "aspnet":
396 runtimeType = "Microsoft.AspNetCore.App";
397 break;
398 case "desktop":
399 runtimeType = "Microsoft.WindowsDesktop.App";
400 break;
401 case "core":
402 runtimeType = "Microsoft.NETCore.App";
403 break;
404 default:
405 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, runtimeType, "aspnet", "desktop", "core"));
406 break;
407 }
408 break;
409 case "Platform":
410 platform = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
411 switch (platform.ToLower())
412 {
413 case "x86":
414 case "x64":
415 case "arm64":
416 platform = platform.ToLower();
417 break;
418 default:
419 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, platform, "x86", "x64", "arm64"));
420 break;
421 }
422 break;
423 case "Version":
424 version = this.ParseHelper.GetAttributeVersionValue(sourceLineNumbers, attrib);
425 break;
426 case "RollForward":
427 rollForward = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
428 switch (rollForward.ToLower())
429 {
430 case "latestmajor":
431 rollForward = "LatestMajor";
432 break;
433 case "major":
434 rollForward = "Major";
435 break;
436 case "latestminor":
437 rollForward = "LatestMinor";
438 break;
439 case "minor":
440 rollForward = "Minor";
441 break;
442 case "latestpatch":
443 rollForward = "LatestPatch";
444 break;
445 case "disable":
446 rollForward = "Disable";
447 break;
448 default:
449 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, rollForward, "latestmajor", "major", "latestminor", "minor", "latestpatch", "disable"));
450 break;
451 }
452 break;
453 default:
454 this.ParseHelper.UnexpectedAttribute(element, attrib);
455 break;
456 }
457 }
458 else
459 {
460 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
461 }
462 }
463
464 if (null == id)
465 {
466 id = this.ParseHelper.CreateIdentifier("ndncc", property, runtimeType, platform, version);
467 }
468
469 if (String.IsNullOrEmpty(property))
470 {
471 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Property"));
472 }
473
474 if (String.IsNullOrEmpty(runtimeType))
475 {
476 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "RuntimeType"));
477 }
478
479 if (String.IsNullOrEmpty(platform))
480 {
481 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Platform"));
482 }
483
484 if (String.IsNullOrEmpty(version))
485 {
486 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Version"));
487 }
488
489 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
490
491 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4NetFxDotNetCompatibilityCheck", this.Context.Platform, CustomActionPlatforms.ARM64 | CustomActionPlatforms.X64 | CustomActionPlatforms.X86);
492
493 if (!this.Messaging.EncounteredError)
494 {
495 section.AddSymbol(new NetFxDotNetCompatibilityCheckSymbol(sourceLineNumbers, id)
496 {
497 RuntimeType = runtimeType,
498 Platform = platform,
499 Version = version,
500 RollForward = rollForward,
501 Property = property,
502 });
503 }
504 }
505
506 /// <summary>
507 /// Parses a DotNetCompatibilityCheckRef element.
508 /// </summary>
509 /// <param name="element">The element to parse.</param>
510 private void ParseDotNetCompatibilityCheckRefElement(Intermediate intermediate, IntermediateSection section, XElement element)
511 {
512 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
513
514 foreach (var attrib in element.Attributes())
515 {
516 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
517 {
518 switch (attrib.Name.LocalName)
519 {
520 case "Id":
521 var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
522 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, NetfxSymbolDefinitions.NetFxDotNetCompatibilityCheck, refId);
523 break;
524 default:
525 this.ParseHelper.UnexpectedAttribute(element, attrib);
526 break;
527 }
528 }
529 else
530 {
531 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
532 }
533 }
534
535 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
536 }
328 } 537 }
329} 538}
diff --git a/src/ext/NetFx/wixext/NetFxExtensionData.cs b/src/ext/NetFx/wixext/NetFxExtensionData.cs
index 61d618cf..8dd9e003 100644
--- a/src/ext/NetFx/wixext/NetFxExtensionData.cs
+++ b/src/ext/NetFx/wixext/NetFxExtensionData.cs
@@ -10,6 +10,8 @@ namespace WixToolset.Netfx
10 /// </summary> 10 /// </summary>
11 public sealed class NetfxExtensionData : BaseExtensionData 11 public sealed class NetfxExtensionData : BaseExtensionData
12 { 12 {
13 public override string DefaultCulture => "en-US";
14
13 public override bool TryGetSymbolDefinitionByName(string name, out IntermediateSymbolDefinition symbolDefinition) 15 public override bool TryGetSymbolDefinitionByName(string name, out IntermediateSymbolDefinition symbolDefinition)
14 { 16 {
15 symbolDefinition = NetfxSymbolDefinitions.ByName(name); 17 symbolDefinition = NetfxSymbolDefinitions.ByName(name);
diff --git a/src/ext/NetFx/wixext/NetfxTableDefinitions.cs b/src/ext/NetFx/wixext/NetfxTableDefinitions.cs
index 57e35323..6be1abe7 100644
--- a/src/ext/NetFx/wixext/NetfxTableDefinitions.cs
+++ b/src/ext/NetFx/wixext/NetfxTableDefinitions.cs
@@ -22,9 +22,25 @@ namespace WixToolset.Netfx
22 symbolIdIsPrimaryKey: true 22 symbolIdIsPrimaryKey: true
23 ); 23 );
24 24
25 public static readonly TableDefinition NetFxDotNetCompatibilityCheck = new TableDefinition(
26 "Wix4NetFxDotNetCheck",
27 NetfxSymbolDefinitions.NetFxDotNetCompatibilityCheck,
28 new[]
29 {
30 new ColumnDefinition("NetFxDotNetCompatibilityCheck", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "The primary key, a non-localized token.", modularizeType: ColumnModularizeType.Column),
31 new ColumnDefinition("RuntimeType", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Text, description: "The type of .NET runtime being checked for. Possible values: aspnet, desktop and core", modularizeType: ColumnModularizeType.Column),
32 new ColumnDefinition("Platform", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Text, description: "Sets the platform for the .NET runtime being checked for. Possible values: x86, x64 and arm64", modularizeType: ColumnModularizeType.Column),
33 new ColumnDefinition("Version", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Version, description: "The version of the .NET runtime being checked for (e.g. 3.1.10, 5.0.1).", modularizeType: ColumnModularizeType.Column),
34 new ColumnDefinition("RollForward", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Text, description: "Sets the roll-forward policy that the application is using. Possible values: latestmajor, major, latestminor, minor, latestpatch and disable", modularizeType: ColumnModularizeType.Column),
35 new ColumnDefinition("Property", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, description: "Name of the property in which to place the result of the check.", modularizeType: ColumnModularizeType.Column),
36 },
37 symbolIdIsPrimaryKey: true
38 );
39
25 public static readonly TableDefinition[] All = new[] 40 public static readonly TableDefinition[] All = new[]
26 { 41 {
27 NetFxNativeImage, 42 NetFxNativeImage,
43 NetFxDotNetCompatibilityCheck
28 }; 44 };
29 } 45 }
30} 46}
diff --git a/src/ext/NetFx/wixext/Symbols/NetFxDotNetCompatibilityCheckSymbol.cs b/src/ext/NetFx/wixext/Symbols/NetFxDotNetCompatibilityCheckSymbol.cs
new file mode 100644
index 00000000..a46cf17f
--- /dev/null
+++ b/src/ext/NetFx/wixext/Symbols/NetFxDotNetCompatibilityCheckSymbol.cs
@@ -0,0 +1,79 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Netfx
4{
5 using WixToolset.Data;
6 using WixToolset.Netfx.Symbols;
7
8 public static partial class NetfxSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition NetFxDotNetCompatibilityCheck = new IntermediateSymbolDefinition(
11 NetfxSymbolDefinitionType.NetFxDotNetCompatibilityCheck.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(NetFxDotNetCompatibilityCheckSymbollFields.RuntimeType), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(NetFxDotNetCompatibilityCheckSymbollFields.Platform), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(NetFxDotNetCompatibilityCheckSymbollFields.Version), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(NetFxDotNetCompatibilityCheckSymbollFields.RollForward), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(NetFxDotNetCompatibilityCheckSymbollFields.Property), IntermediateFieldType.String),
19 },
20 typeof(NetFxDotNetCompatibilityCheckSymbol));
21 }
22}
23
24namespace WixToolset.Netfx.Symbols
25{
26 using WixToolset.Data;
27
28 public enum NetFxDotNetCompatibilityCheckSymbollFields
29 {
30 RuntimeType,
31 Platform,
32 Version,
33 RollForward,
34 Property,
35 }
36
37 public class NetFxDotNetCompatibilityCheckSymbol : IntermediateSymbol
38 {
39 public NetFxDotNetCompatibilityCheckSymbol() : base(NetfxSymbolDefinitions.NetFxDotNetCompatibilityCheck, null, null)
40 {
41 }
42
43 public NetFxDotNetCompatibilityCheckSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(NetfxSymbolDefinitions.NetFxDotNetCompatibilityCheck, sourceLineNumber, id)
44 {
45 }
46
47 public IntermediateField this[NetFxDotNetCompatibilityCheckSymbollFields index] => this.Fields[(int)index];
48
49 public string RuntimeType
50 {
51 get => this.Fields[(int)NetFxDotNetCompatibilityCheckSymbollFields.RuntimeType].AsString();
52 set => this.Set((int)NetFxDotNetCompatibilityCheckSymbollFields.RuntimeType, value);
53 }
54
55 public string Platform
56 {
57 get => this.Fields[(int)NetFxDotNetCompatibilityCheckSymbollFields.Platform].AsString();
58 set => this.Set((int)NetFxDotNetCompatibilityCheckSymbollFields.Platform, value);
59 }
60
61 public string Version
62 {
63 get => this.Fields[(int)NetFxDotNetCompatibilityCheckSymbollFields.Version].AsString();
64 set => this.Set((int)NetFxDotNetCompatibilityCheckSymbollFields.Version, value);
65 }
66
67 public string RollForward
68 {
69 get => this.Fields[(int)NetFxDotNetCompatibilityCheckSymbollFields.RollForward].AsString();
70 set => this.Set((int)NetFxDotNetCompatibilityCheckSymbollFields.RollForward, value);
71 }
72
73 public string Property
74 {
75 get => this.Fields[(int)NetFxDotNetCompatibilityCheckSymbollFields.Property].AsString();
76 set => this.Set((int)NetFxDotNetCompatibilityCheckSymbollFields.Property, value);
77 }
78 }
79}
diff --git a/src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs b/src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs
index 862eba16..ad729dd4 100644
--- a/src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs
+++ b/src/ext/NetFx/wixext/Symbols/NetfxSymbolDefinitions.cs
@@ -10,10 +10,13 @@ namespace WixToolset.Netfx
10 { 10 {
11 NetFxNativeImage, 11 NetFxNativeImage,
12 NetFxNetCoreSearch, 12 NetFxNetCoreSearch,
13 NetFxDotNetCompatibilityCheck
13 } 14 }
14 15
15 public static partial class NetfxSymbolDefinitions 16 public static partial class NetfxSymbolDefinitions
16 { 17 {
18 public static readonly Version Version = new Version("4.0.0");
19
17 public static IntermediateSymbolDefinition ByName(string name) 20 public static IntermediateSymbolDefinition ByName(string name)
18 { 21 {
19 if (!Enum.TryParse(name, out NetfxSymbolDefinitionType type)) 22 if (!Enum.TryParse(name, out NetfxSymbolDefinitionType type))
@@ -33,6 +36,9 @@ namespace WixToolset.Netfx
33 36
34 case NetfxSymbolDefinitionType.NetFxNetCoreSearch: 37 case NetfxSymbolDefinitionType.NetFxNetCoreSearch:
35 return NetfxSymbolDefinitions.NetFxNetCoreSearch; 38 return NetfxSymbolDefinitions.NetFxNetCoreSearch;
39
40 case NetfxSymbolDefinitionType.NetFxDotNetCompatibilityCheck:
41 return NetfxSymbolDefinitions.NetFxDotNetCompatibilityCheck;
36 42
37 default: 43 default:
38 throw new ArgumentOutOfRangeException(nameof(type)); 44 throw new ArgumentOutOfRangeException(nameof(type));
diff --git a/src/ext/NetFx/wixlib/NetFxExtension_Platform.wxi b/src/ext/NetFx/wixlib/NetFxExtension_Platform.wxi
index f607c72d..c9c8af02 100644
--- a/src/ext/NetFx/wixlib/NetFxExtension_Platform.wxi
+++ b/src/ext/NetFx/wixlib/NetFxExtension_Platform.wxi
@@ -10,6 +10,7 @@
10 <CustomAction Id="$(var.Prefix)NetFxExecuteNativeImageCommitInstall$(var.Suffix)" DllEntry="ExecNetFx" Execute="commit" Impersonate="no" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)NetFxCA$(var.Suffix)" /> 10 <CustomAction Id="$(var.Prefix)NetFxExecuteNativeImageCommitInstall$(var.Suffix)" DllEntry="ExecNetFx" Execute="commit" Impersonate="no" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)NetFxCA$(var.Suffix)" />
11 <CustomAction Id="$(var.Prefix)NetFxExecuteNativeImageUninstall$(var.Suffix)" DllEntry="ExecNetFx" Execute="deferred" Impersonate="no" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)NetFxCA$(var.Suffix)" /> 11 <CustomAction Id="$(var.Prefix)NetFxExecuteNativeImageUninstall$(var.Suffix)" DllEntry="ExecNetFx" Execute="deferred" Impersonate="no" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)NetFxCA$(var.Suffix)" />
12 <CustomAction Id="$(var.Prefix)NetFxExecuteNativeImageCommitUninstall$(var.Suffix)" DllEntry="ExecNetFx" Execute="commit" Impersonate="no" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)NetFxCA$(var.Suffix)" /> 12 <CustomAction Id="$(var.Prefix)NetFxExecuteNativeImageCommitUninstall$(var.Suffix)" DllEntry="ExecNetFx" Execute="commit" Impersonate="no" Return="ignore" SuppressModularization="yes" BinaryRef="$(var.Prefix)NetFxCA$(var.Suffix)" />
13 <CustomAction Id="$(var.Prefix)NetFxDotNetCompatibilityCheck$(var.Suffix)" DllEntry="DotNetCompatibilityCheck" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)NetFxCA$(var.Suffix)" />
13 14
14 <InstallExecuteSequence> 15 <InstallExecuteSequence>
15 <Custom Action="$(var.Prefix)NetFxScheduleNativeImage$(var.Suffix)" Before="InstallFiles" Overridable="yes" /> 16 <Custom Action="$(var.Prefix)NetFxScheduleNativeImage$(var.Suffix)" Before="InstallFiles" Overridable="yes" />
@@ -17,11 +18,19 @@
17 <Custom Action="$(var.Prefix)NetFxExecuteNativeImageUninstall$(var.Suffix)" After="$(var.Prefix)NetFxExecuteNativeImageCommitUninstall$(var.Suffix)" Overridable="yes" Condition="RollbackDisabled = 1" /> 18 <Custom Action="$(var.Prefix)NetFxExecuteNativeImageUninstall$(var.Suffix)" After="$(var.Prefix)NetFxExecuteNativeImageCommitUninstall$(var.Suffix)" Overridable="yes" Condition="RollbackDisabled = 1" />
18 <Custom Action="$(var.Prefix)NetFxExecuteNativeImageCommitInstall$(var.Suffix)" After="$(var.Prefix)NetFxExecuteNativeImageUninstall$(var.Suffix)" Overridable="yes" Condition="RollbackDisabled &lt;&gt; 1" /> 19 <Custom Action="$(var.Prefix)NetFxExecuteNativeImageCommitInstall$(var.Suffix)" After="$(var.Prefix)NetFxExecuteNativeImageUninstall$(var.Suffix)" Overridable="yes" Condition="RollbackDisabled &lt;&gt; 1" />
19 <Custom Action="$(var.Prefix)NetFxExecuteNativeImageInstall$(var.Suffix)" After="$(var.Prefix)NetFxExecuteNativeImageCommitInstall$(var.Suffix)" Overridable="yes" Condition="RollbackDisabled = 1" /> 20 <Custom Action="$(var.Prefix)NetFxExecuteNativeImageInstall$(var.Suffix)" After="$(var.Prefix)NetFxExecuteNativeImageCommitInstall$(var.Suffix)" Overridable="yes" Condition="RollbackDisabled = 1" />
21 <Custom Action="$(var.Prefix)NetFxDotNetCompatibilityCheck$(var.Suffix)" Before="LaunchConditions" Overridable="yes" />
20 </InstallExecuteSequence> 22 </InstallExecuteSequence>
23
24 <InstallUISequence>
25 <Custom Action="$(var.Prefix)NetFxDotNetCompatibilityCheck$(var.Suffix)" Before="LaunchConditions" Overridable="yes" />
26 </InstallUISequence>
21 </Fragment> 27 </Fragment>
22 28
23 <!-- NetFx Custom Action DLL Definitions --> 29 <!-- NetFx Custom Action DLL Definitions -->
24 <Fragment> 30 <Fragment>
25 <Binary Id="$(var.Prefix)NetFxCA$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))netfxca.dll" /> 31 <Binary Id="$(var.Prefix)NetFxCA$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))netfxca.dll" />
32 <Binary Id="$(var.Prefix)NetCheck_x86" SourceFile="!(bindpath.x86)NetCoreCheck.exe" />
33 <Binary Id="$(var.Prefix)NetCheck_x64" SourceFile="!(bindpath.x64)NetCoreCheck.exe" />
34 <Binary Id="$(var.Prefix)NetCheck_arm64" SourceFile="!(bindpath.arm64)NetCoreCheck.exe" />
26 </Fragment> 35 </Fragment>
27</Include> 36</Include>
diff --git a/src/ext/NetFx/wixlib/netfx.wixproj b/src/ext/NetFx/wixlib/netfx.wixproj
index bc6a1172..734e64da 100644
--- a/src/ext/NetFx/wixlib/netfx.wixproj
+++ b/src/ext/NetFx/wixlib/netfx.wixproj
@@ -11,6 +11,9 @@
11 <BindInputPaths Include="$(OutputPath)x86" BindName='x86' /> 11 <BindInputPaths Include="$(OutputPath)x86" BindName='x86' />
12 <BindInputPaths Include="$(OutputPath)x64" BindName='x64' /> 12 <BindInputPaths Include="$(OutputPath)x64" BindName='x64' />
13 <BindInputPaths Include="$(OutputPath)arm64" BindName='arm64' /> 13 <BindInputPaths Include="$(OutputPath)arm64" BindName='arm64' />
14 <BindInputPaths Include="$(PkgMicrosoft_NET_Tools_NETCoreCheck_x86)\win-x86" BindName='x86' />
15 <BindInputPaths Include="$(PkgMicrosoft_NET_Tools_NETCoreCheck_x64)\win-x64" BindName='x64' />
16 <BindInputPaths Include="$(PkgMicrosoft_NET_Tools_NETCoreCheck_arm64)\win-arm64" BindName='arm64' />
14 </ItemGroup> 17 </ItemGroup>
15 18
16 <ItemGroup> 19 <ItemGroup>
@@ -33,5 +36,8 @@
33 <ItemGroup> 36 <ItemGroup>
34 <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" /> 37 <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
35 <PackageReference Include="GitInfo" PrivateAssets="All" /> 38 <PackageReference Include="GitInfo" PrivateAssets="All" />
39 <PackageReference Include="Microsoft.NET.Tools.NETCoreCheck.x86" GeneratePathProperty="true" />
40 <PackageReference Include="Microsoft.NET.Tools.NETCoreCheck.x64" GeneratePathProperty="true" />
41 <PackageReference Include="Microsoft.NET.Tools.NETCoreCheck.arm64" GeneratePathProperty="true" />
36 </ItemGroup> 42 </ItemGroup>
37</Project> 43</Project>
diff --git a/src/internal/SetBuildNumber/Directory.Packages.props.pp b/src/internal/SetBuildNumber/Directory.Packages.props.pp
index 4869df82..f59c9742 100644
--- a/src/internal/SetBuildNumber/Directory.Packages.props.pp
+++ b/src/internal/SetBuildNumber/Directory.Packages.props.pp
@@ -93,4 +93,10 @@
93 <PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" /> 93 <PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
94 <PackageVersion Include="xunit.assert" Version="2.4.2" /> 94 <PackageVersion Include="xunit.assert" Version="2.4.2" />
95 </ItemGroup> 95 </ItemGroup>
96
97 <ItemGroup>
98 <PackageVersion Include="Microsoft.NET.Tools.NETCoreCheck.x86" Version="6.0.0" />
99 <PackageVersion Include="Microsoft.NET.Tools.NETCoreCheck.x64" Version="6.0.0" />
100 <PackageVersion Include="Microsoft.NET.Tools.NETCoreCheck.arm64" Version="6.0.0" />
101 </ItemGroup>
96</Project> 102</Project>