Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add prefer native arm64 flag #10134

Merged
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7749725
add basic implementation for preferArm64
YuliiaKovalova May 14, 2024
ee137ff
cleanup
YuliiaKovalova May 15, 2024
a0c1b7e
Merge remote-tracking branch 'origin/main' into dev/ykovalova/add_Pre…
YuliiaKovalova May 15, 2024
7219dab
fix targets file
YuliiaKovalova May 15, 2024
44ce08e
cleanup test file
YuliiaKovalova May 15, 2024
4394d26
remove extra code
YuliiaKovalova May 15, 2024
c74b891
fix review comments + extend tests
YuliiaKovalova May 15, 2024
b9a9d42
fix review comments
YuliiaKovalova May 16, 2024
eb1986c
remove extra ;
YuliiaKovalova May 16, 2024
b05655f
fix the missed condition
YuliiaKovalova May 16, 2024
f52e039
fix review comments
YuliiaKovalova May 16, 2024
459f25d
remove checking build results due to MSB3644 on build machines
YuliiaKovalova May 17, 2024
d81b680
fix review comments
YuliiaKovalova May 23, 2024
170409a
handle the case when prefix doesn't exist in manifest
YuliiaKovalova May 23, 2024
a79ee90
remove extra changes
YuliiaKovalova May 23, 2024
874884d
fix review comments - renaming and handling in the task
YuliiaKovalova May 23, 2024
f645ef8
change severity for SpecifiedSeverityDoesNotExist log
YuliiaKovalova May 23, 2024
98cae23
handle ClickOnceManifest and Prefer32bit cases
YuliiaKovalova May 24, 2024
79dff86
fix quoting
YuliiaKovalova May 24, 2024
bdb3aba
fix review comments - simplify conditions and typos
YuliiaKovalova May 24, 2024
0f35c1a
change condition '$(PreferNativeArm64)' == '' -> '$(PreferNativeArm…
YuliiaKovalova May 24, 2024
8c3e739
handle the case for clickonce manifests
YuliiaKovalova May 27, 2024
edee452
make _SupportedArchitectures a global property
YuliiaKovalova May 27, 2024
528d930
fix spacing
YuliiaKovalova May 27, 2024
b50a57f
Extend logged messages
YuliiaKovalova May 28, 2024
1ec6c37
add path to manifest to AddToWin32Manifest.ManifestCanNotBeOpened
YuliiaKovalova May 28, 2024
be21253
use LogErrorWithCodeFromResources to log file path
YuliiaKovalova May 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,7 @@ elementFormDefault="qualified">
<xs:element name="LangVersion" type="msb:StringPropertyType" substitutionGroup="msb:Property"/>
<xs:element name="VBRuntime" type="msb:StringPropertyType" substitutionGroup="msb:Property"/>
<xs:element name="Prefer32Bit" type="msb:StringPropertyType" substitutionGroup="msb:Property"/>
<xs:element name="PreferNativeArm64" type="msb:StringPropertyType" substitutionGroup="msb:Property"/>
<xs:element name="HighEntropyVA" type="msb:StringPropertyType" substitutionGroup="msb:Property"/>
<xs:element name="LinkIncremental" type="msb:StringPropertyType" substitutionGroup="msb:Property"/>
<xs:element name="ManifestCertificateThumbprint" type="msb:StringPropertyType" substitutionGroup="msb:Property"/>
Expand Down
3 changes: 3 additions & 0 deletions src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@
<None Update="TestResources\lorem.bin">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestResources\Manifests\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestResources\mycert.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
65 changes: 65 additions & 0 deletions src/Tasks.UnitTests/NETMessage_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Build.Evaluation;
using Microsoft.Build.Shared;
using Microsoft.Build.UnitTests;
using Shouldly;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.Build.Tasks.UnitTests
{
public class NETMessage_Tests
{
private readonly ITestOutputHelper _testOutput;

public NETMessage_Tests(ITestOutputHelper testOutput) => _testOutput = testOutput;

[Theory]
[InlineData(true, true, "CommonTarget.Prefer32BitAndPreferNativeArm64Enabled", false)]
[InlineData(false, false, "CommonTarget.PlatformIsAnyCPUAndPreferNativeArm64Enabled", true)]
public void E2EScenarioTests(bool prefer32, bool isPlatformAnyCpu, string expectedResourceName, bool isSuccessfulBuild)
{
using (TestEnvironment env = TestEnvironment.Create())
{
var outputPath = env.CreateFolder().Path;
string projectContent = @$"
<Project DefaultTargets=""Build"">
<Import Project=""$(MSBuildBinPath)\Microsoft.Common.props"" />

<PropertyGroup>
<Platform>{(isPlatformAnyCpu ? "AnyCPU" : "Release")}</Platform>
<OutputType>Library</OutputType>
<PreferNativeArm64>true</PreferNativeArm64>
<Prefer32Bit>{(prefer32 ? "true" : "false")}</Prefer32Bit>
</PropertyGroup>

<Target Name=""Build""/>
<Import Project=""$(MSBuildBinPath)\Microsoft.CSharp.targets"" />

</Project>
";

var projectFile = env.CreateFile(env.CreateFolder(), "test.csproj", projectContent).Path;
Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory(projectFile, touchProject: false);

string expectedBuildMessage = ResourceUtilities.FormatResourceStringStripCodeAndKeyword(expectedResourceName);
MockLogger logger = new MockLogger(_testOutput);

bool result = project.Build(logger);

result.ShouldBe(isSuccessfulBuild);

if (isSuccessfulBuild)
{
logger.Warnings[0].RawMessage.ShouldBe(expectedBuildMessage);
}
else
{
logger.Errors[0].RawMessage.ShouldBe(expectedBuildMessage);
}
}
}
}
}
181 changes: 181 additions & 0 deletions src/Tasks.UnitTests/PopulateSupportedArchitectures_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// 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.Runtime.InteropServices;
using System.Xml;
using Microsoft.Build.Evaluation;
using Microsoft.Build.UnitTests;
using Shouldly;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.Build.Tasks.UnitTests
{
public class PopulateSupportedArchitectures_Tests
{
private static string TestAssetsRootPath { get; } = Path.Combine(
Path.GetDirectoryName(typeof(PopulateSupportedArchitectures_Tests).Assembly.Location) ?? AppContext.BaseDirectory,
"TestResources",
"Manifests");

private readonly ITestOutputHelper _testOutput;

public PopulateSupportedArchitectures_Tests(ITestOutputHelper testOutput) => _testOutput = testOutput;

[Theory]
[InlineData("testManifestWithInvalidSupportedArchs.manifest", false)]
[InlineData("testManifestWithApplicationDefined.manifest", true)]
[InlineData(null, true)]
public void ManifestPopulationCheck(string manifestName, bool expectedResult)
{
PopulateSupportedArchitectures task = new PopulateSupportedArchitectures()
{
BuildEngine = new MockEngine(_testOutput)
};

using (TestEnvironment env = TestEnvironment.Create())
{
var tempOutput = env.CreateFolder().Path;
task.OutputDirectory = tempOutput;
task.SupportedArchitectures = "amd64 arm64";
if (!string.IsNullOrEmpty(manifestName))
{
task.ApplicationManifestPath = Path.Combine(TestAssetsRootPath, manifestName);
}

var result = task.Execute();

result.ShouldBe(expectedResult);

if (result)
{
string generatedManifest = task.ManifestPath;
string expectedManifest = Path.Combine(TestAssetsRootPath, $"{manifestName ?? "default.win32manifest"}_expected");

XmlDocument expectedDoc = new XmlDocument();
XmlDocument actualDoc = new XmlDocument();

expectedDoc.Load(generatedManifest);
actualDoc.Load(expectedManifest);

expectedDoc.OuterXml.ShouldBe(actualDoc.OuterXml);
expectedDoc.InnerXml.ShouldBe(actualDoc.InnerXml);
}
}
}

#if NETFRAMEWORK
[Theory]
[InlineData(null, true)]
[InlineData("buildIn.manifest", true)]
[InlineData("testManifestWithValidSupportedArchs.manifest", true)]
public void E2EScenarioTests(string manifestName, bool expectedResult)
YuliiaKovalova marked this conversation as resolved.
Show resolved Hide resolved
{
using (TestEnvironment env = TestEnvironment.Create())
{
var outputPath = env.CreateFolder().Path;
string projectContent = @$"
<Project DefaultTargets=""Build"">
<Import Project=""$(MSBuildBinPath)\Microsoft.Common.props"" />

<PropertyGroup>
<Platform>AnyCPU</Platform>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputType>Library</OutputType>
<PreferNativeArm64>true</PreferNativeArm64>
<Prefer32Bit>false</Prefer32Bit>
{(!string.IsNullOrEmpty(manifestName) ? $"<ApplicationManifest>{manifestName}</ApplicationManifest>" : "")}
<IntermediateOutputPath>{outputPath}</IntermediateOutputPath>
</PropertyGroup>

<Target Name=""Build""/>
<Import Project=""$(MSBuildBinPath)\Microsoft.CSharp.targets"" />

</Project>
";

var projectFolder = env.CreateFolder();
var projectFile = env.CreateFile(projectFolder, "test.csproj", projectContent).Path;

// copy application manifest
if (!string.IsNullOrEmpty(manifestName))
{
File.Copy(Path.Combine(TestAssetsRootPath, manifestName), Path.Combine(projectFolder.Path, manifestName));
}

Project project = ObjectModelHelpers.LoadProjectFileInTempProjectDirectory(projectFile, touchProject: false);

bool result = project.Build(new MockLogger(_testOutput));
result.ShouldBe(expectedResult);

// #2 - represents the name for native resource (Win 32 resource), #24 - the type (Manifest)
byte[]? actualManifestBytes = AssemblyNativeResourceManager.GetResourceFromExecutable(Path.Combine(outputPath, "test.dll"), "#2", "#24");

// check manifest content
if (actualManifestBytes != null)
{
string expectedManifest = Path.Combine(TestAssetsRootPath, $"{manifestName ?? "default.win32manifest"}_expected");

XmlDocument expectedDoc = new XmlDocument();
XmlDocument actualDoc = new XmlDocument();

expectedDoc.Load(expectedManifest);
using (MemoryStream stream = new MemoryStream(actualManifestBytes))
{
actualDoc.Load(stream);
}

expectedDoc.OuterXml.ShouldBe(actualDoc.OuterXml);
expectedDoc.InnerXml.ShouldBe(actualDoc.InnerXml);
}
}
}

internal sealed class AssemblyNativeResourceManager
{
public enum LoadLibraryFlags : uint { LOAD_LIBRARY_AS_DATAFILE = 2 };

[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibrary(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LockResource(IntPtr hResData);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);

public static byte[]? GetResourceFromExecutable(string assembly, string lpName, string lpType)
{
IntPtr hModule = LoadLibrary(assembly, IntPtr.Zero, LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE);
YuliiaKovalova marked this conversation as resolved.
Show resolved Hide resolved
if (hModule != IntPtr.Zero)
{
IntPtr hResource = FindResource(hModule, "#2", "#24");
YuliiaKovalova marked this conversation as resolved.
Show resolved Hide resolved
if (hResource != IntPtr.Zero)
{
uint resSize = SizeofResource(hModule, hResource);
IntPtr resData = LoadResource(hModule, hResource);
if (resData != IntPtr.Zero)
{
byte[] uiBytes = new byte[resSize];
IntPtr ipMemorySource = LockResource(resData);
Marshal.Copy(ipMemorySource, uiBytes, 0, (int)resSize);

return uiBytes;
}
}
}

return null;
}
}
#endif
}
}
79 changes: 79 additions & 0 deletions src/Tasks.UnitTests/TestResources/Manifests/buildIn.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.

<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />

Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->

<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->

<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->

<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->

<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->

<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->

</application>
</compatibility>

<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.

Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
-->

<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->

</assembly>
Loading
Loading