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

Honor --arch switch for arm64 on Windows and Mac #3100

Merged
merged 44 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
3ebcdaa
add first version of muxer finder
MarcoRossignoli Oct 11, 2021
792b981
add X64 force scenarios
MarcoRossignoli Oct 11, 2021
e3047e2
refactor DotnetHostHelper
MarcoRossignoli Oct 12, 2021
fb54a0e
add validation matrix
MarcoRossignoli Oct 13, 2021
735569b
move internal helper to internal visibility
MarcoRossignoli Oct 13, 2021
fa12d56
force to x64 only if user doesn't specify nothing, refactor arch/fx m…
MarcoRossignoli Oct 13, 2021
e4e9958
make RunsettingsHelper testable
MarcoRossignoli Oct 13, 2021
47da915
temporary fix issue with ARM64 metadata detection, add architecture i…
MarcoRossignoli Oct 13, 2021
6c09164
address PR feedback
MarcoRossignoli Oct 13, 2021
eebc3c1
fix architecture infer for asm, add Mac-O reader, update dotnet host …
MarcoRossignoli Oct 14, 2021
5cd221b
keep back compat in assembly metadata provider
MarcoRossignoli Oct 14, 2021
9fabfee
refactor dotnet host helper, improve error notification
MarcoRossignoli Oct 14, 2021
3da05df
use invariant culture api
MarcoRossignoli Oct 14, 2021
9090dcf
first part of update for win arm, run always with dotnet muxer and av…
MarcoRossignoli Oct 15, 2021
2dcbd9f
remove testhost.dll override from package for x64, refactor searching…
MarcoRossignoli Oct 18, 2021
df68b83
skip apphost host on arm/win, flow new sdk VSTEST_WINAPPHOST_ to app …
MarcoRossignoli Oct 19, 2021
16f3457
add VSTEST_TMP_SWITCH_DOTNETROOTS_ENVVARS env var for manual testability
MarcoRossignoli Oct 20, 2021
412d0b3
fix PlatformArgumentProcessorTests UTs
MarcoRossignoli Oct 20, 2021
b7275e1
fix unix build
MarcoRossignoli Oct 20, 2021
459f93b
use AnyCPU version of testhost.dll in case of x64
MarcoRossignoli Oct 21, 2021
b3fc02c
remove arch/framework check and let test fail
MarcoRossignoli Oct 21, 2021
0001463
move acceptance RunMultipleTestAssembliesInParallel to win only
MarcoRossignoli Oct 21, 2021
73dc606
fix comment typo
MarcoRossignoli Oct 21, 2021
056e239
Update message for muxer not found
MarcoRossignoli Oct 22, 2021
097198e
Improve error message in case of x64 forcing.
MarcoRossignoli Oct 24, 2021
e13896f
address Vitek feedback, refactor host manager
MarcoRossignoli Oct 25, 2021
704266e
nit cleanup
MarcoRossignoli Oct 25, 2021
0e007d0
address PR feedback
MarcoRossignoli Oct 25, 2021
0ec501e
return current running muxer if SDK and target architecture are the same
MarcoRossignoli Oct 25, 2021
52e120e
Improve logging
MarcoRossignoli Oct 25, 2021
47e268a
add UTs DotnetHostHelper env vars
MarcoRossignoli Oct 25, 2021
9d7976a
Add UTs for windows global registraton
MarcoRossignoli Oct 25, 2021
9d363b8
complete DotnetHostHelper UTs
MarcoRossignoli Oct 26, 2021
72a178a
Improve DotnetHostHelper UTs
MarcoRossignoli Oct 26, 2021
35dbef9
Run GetDotnetPathByArchitecture_DefaultInstallation_Win only on win d…
MarcoRossignoli Oct 26, 2021
8e048e8
Add UTs for apphost DOTNET_ROOTS forwarding
MarcoRossignoli Oct 26, 2021
a48edc2
Cleanup
MarcoRossignoli Oct 26, 2021
cbc247d
Apply suggestions from code review
MarcoRossignoli Oct 27, 2021
73b2a23
address Jakub feedback
MarcoRossignoli Oct 27, 2021
bcf4bbf
fix UTs
MarcoRossignoli Oct 27, 2021
37bfa5e
revert platform BC
MarcoRossignoli Oct 27, 2021
041b1f2
revert public api BC
MarcoRossignoli Oct 27, 2021
4d1c3e0
restore InitializeShouldNotConsiderCaseSensitivityOfTheArgumentPassed UT
MarcoRossignoli Oct 27, 2021
dea3414
fix documentation
MarcoRossignoli Oct 27, 2021
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
138 changes: 135 additions & 3 deletions src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetHostHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@

namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers
{
using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers;
using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Resources;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;

using Microsoft.Win32;
using System;
using System.IO;

Expand All @@ -22,22 +23,30 @@ public class DotnetHostHelper : IDotnetHostHelper

private readonly IFileHelper fileHelper;
private readonly IEnvironment environment;
private readonly IWindowsRegistryHelper windowsRegistryHelper;
private readonly IEnvironmentVariableHelper environmentVariableHelper;

/// <summary>
/// Initializes a new instance of the <see cref="DotnetHostHelper"/> class.
/// </summary>
public DotnetHostHelper() : this(new FileHelper(), new PlatformEnvironment())
public DotnetHostHelper() : this(new FileHelper(), new PlatformEnvironment(), new WindowsRegistryHelper(), new EnvironmentVariableHelper())
MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved
{
}

/// <summary>
/// Initializes a new instance of the <see cref="DotnetHostHelper"/> class.
/// </summary>
/// <param name="fileHelper">File Helper</param>
public DotnetHostHelper(IFileHelper fileHelper, IEnvironment environment)
public DotnetHostHelper(
IFileHelper fileHelper,
IEnvironment environment,
IWindowsRegistryHelper windowsRegistryHelper,
IEnvironmentVariableHelper environmentVariableHelper)
{
this.fileHelper = fileHelper;
this.environment = environment;
this.windowsRegistryHelper = windowsRegistryHelper;
this.environmentVariableHelper = environmentVariableHelper;
}

/// <inheritdoc />
Expand Down Expand Up @@ -88,6 +97,129 @@ private bool TryGetExecutablePath(string executableBaseName, out string executab

return false;
}

public string GetDotnetPathByArchitecture(PlatformArchitecture targetArchitecture)
{
// We used similar approach of runtime resolver.
MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved
// https://github.com/dotnet/runtime/blob/main/src/native/corehost/fxr_resolver.cpp#L55

string path = null;
bool isWinOs = environment.OperatingSystem == PlatformOperatingSystem.Windows;
string muxerName = $"dotnet{(isWinOs ? ".exe" : "")}";
EqtTrace.Verbose($"DotnetHostHelper: current platform muxer '{muxerName}'");

// Try to search using env vars in the order
// DOTNET_ROOT_{arch}
// DOTNET_ROOT(x86) if X86 on Win (here we cannot check if current process is WOW64 because this is SDK process arch and not real host arch so it's irrelevant)
// "DOTNET_ROOT(x86) is used instead when running a 32-bit executable on a 64-bit OS."
// DOTNET_ROOT
string envVarPrefix = "DOTNET_ROOT";
string envKey = $"{envVarPrefix}_{targetArchitecture.ToString().ToUpper()}";
string envVar = this.environmentVariableHelper.GetEnvironmentVariable(envKey);
if (envVar == null && targetArchitecture == PlatformArchitecture.X86 && this.environment.OperatingSystem == PlatformOperatingSystem.Windows)
MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved
{
envKey = $"{envVarPrefix}(x86)";
MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved
envVar = this.environmentVariableHelper.GetEnvironmentVariable(envKey);
if (envVar == null)
{
envKey = envVarPrefix;
envVar = this.environmentVariableHelper.GetEnvironmentVariable(envKey);
}
}

if (envVar != null)
{
envVar = Path.Combine(envVar, muxerName);
if (this.fileHelper.Exists(envVar))
{
path = envVar;
EqtTrace.Verbose($"DotnetHostHelper: muxer resolved using env var key '{envKey}' in '{path}'");
return path;
}
}

// Try to search for global registration
if (isWinOs)
{
// Installed version are always on 32-bit view of registry
// https://github.com/dotnet/designs/blob/main/accepted/2020/install-locations.md#globally-registered-install-location-new
// "Note that this registry key is "redirected" that means that 32-bit processes see different copy of the key then 64bit processes.
// So it's important that both installers and the host access only the 32-bit view of the registry."
using (IRegistryKey hklm = windowsRegistryHelper.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32))
{
if (hklm != null)
{
using (IRegistryKey dotnetInstalledVersion = hklm.OpenSubKey(@"SOFTWARE\dotnet\Setup\InstalledVersions"))
{
if (dotnetInstalledVersion != null)
{
using (IRegistryKey nativeArch = dotnetInstalledVersion.OpenSubKey(targetArchitecture.ToString().ToLower()))
{
string installLocation = nativeArch?.GetValue("InstallLocation")?.ToString();
if (installLocation != null)
{
path = Path.Combine(installLocation.Trim(), muxerName);
EqtTrace.Verbose($"DotnetHostHelper: muxer resolved using win registry in '{path}'");
}
}
}
}
}
}
}
else
{
string baseInstallLocation = "/etc/dotnet/";

// We search for architecture specific installation
string installLocation = $"{baseInstallLocation}install_location_{targetArchitecture.ToString().ToLower()}";

// TODO: understand if we should access to this one or only arch one
if (!this.fileHelper.Exists(installLocation))
{
installLocation = $"{baseInstallLocation}install_location";
}

if (this.fileHelper.Exists(installLocation))
{
using (Stream stream = this.fileHelper.GetStream(installLocation, FileMode.Open, FileAccess.Read))
using (StreamReader streamReader = new StreamReader(stream))
{
string content = streamReader.ReadToEnd().Trim();
EqtTrace.Verbose($"DotnetHostHelper: '{installLocation}' content '{content}'");
path = Path.Combine(content, muxerName);
EqtTrace.Verbose($"DotnetHostHelper: muxer resolved using '{installLocation}' in '{path}'");
}
}
}

// Try on default installation location if exists
if (path is null)
{
path = isWinOs ?
MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved

this.environment.Architecture == PlatformArchitecture.X86 ?
Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles(x86)"), "dotnet", muxerName) :
Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "dotnet", muxerName) :

this.environment.OperatingSystem == PlatformOperatingSystem.OSX ?
// TODO: check if x64 assumptions is correct and if it's ok fallback to default on linux
$"/usr/local/share/dotnet/{muxerName}" + ((targetArchitecture == PlatformArchitecture.X64 && environment.Architecture == PlatformArchitecture.ARM64) ? "/x64" : "") :
$"/usr/share/dotnet/{muxerName}";

EqtTrace.Verbose($"DotnetHostHelper: muxer resolved using default installation path in '{path}'");
}

if (!this.fileHelper.Exists(path))
{
string errorMessage = string.Format(Resources.NoDotnetMuxerFoundForArchitecture, muxerName, targetArchitecture.ToString());

EqtTrace.Error(errorMessage);
throw new FileNotFoundException(errorMessage);
}

return path;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#if !NETSTANDARD1_0

using System;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;

namespace Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers
{
public class EnvironmentVariableHelper : IEnvironmentVariableHelper
{
public string GetEnvironmentVariable(string variable)
=> Environment.GetEnvironmentVariable(variable);
}
}

#endif
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;

namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces
{
/// <summary>
Expand All @@ -20,5 +23,14 @@ public interface IDotnetHostHelper
/// </summary>
/// <returns>Full path to <c>mono</c> executable</returns>
string GetMonoPath();

/// <summary>
/// Try to locate muxer of specific architecture
/// </summary>
/// <param name="processHandle">Handle of current process</param>
/// <param name="architecture">Specific architecture</param>
/// <param name="path">path to the muxer</param>
/// <returns>true if native muxer is found</returns>
string GetDotnetPathByArchitecture(PlatformArchitecture targetArchitecture);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces
{
public interface IEnvironmentVariableHelper
{
string GetEnvironmentVariable(string variable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.


#if !NETSTANDARD1_0

using System;
using Microsoft.Win32;

namespace Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces
{
public interface IWindowsRegistryHelper
{
IRegistryKey OpenBaseKey(RegistryHive hKey, RegistryView view);
}

public interface IRegistryKey : IDisposable
{
IRegistryKey OpenSubKey(string name);
object GetValue(string name);
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#if !NETSTANDARD1_0

using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;
using Microsoft.Win32;

namespace Microsoft.VisualStudio.TestPlatform.Utilities.Helpers
{
public class WindowsRegistryHelper : IWindowsRegistryHelper
{
public IRegistryKey OpenBaseKey(RegistryHive hKey, RegistryView view)
{
var keyRegistry = RegistryKey.OpenBaseKey(hKey, view);
return keyRegistry is null ? null : new RegistryKeyWrapper(keyRegistry);
}
}

public class RegistryKeyWrapper : IRegistryKey
{
private readonly RegistryKey registryKey;

public RegistryKeyWrapper(RegistryKey registryKey)
{
this.registryKey = registryKey;
}

public object GetValue(string name)
{
return registryKey?.GetValue(name)?.ToString();
}

public IRegistryKey OpenSubKey(string name)
{
var keyRegistry = this.registryKey.OpenSubKey(name);
return keyRegistry is null ? null : new RegistryKeyWrapper(keyRegistry);
}

public void Dispose()
{
this.registryKey?.Dispose();
}

}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

<ItemGroup Condition=" '$(TargetFramework)' != 'net451' AND '$(TargetFramework)' != 'net45' AND '$(TargetFramework)' != 'netstandard1.0' ">
<PackageReference Include="System.Diagnostics.FileVersionInfo" Version="4.3.0" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,14 @@ Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.TestPlatformEventSourc
Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.TestPlatformEventSource.VsTestConsoleStop() -> void
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.DotnetHostHelper() -> void
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.DotnetHostHelper(Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper fileHelper, Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces.IEnvironment environment) -> void
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.DotnetHostHelper(Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper fileHelper, Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces.IEnvironment environment, Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IWindowsRegistryHelper windowsRegistryHelper, Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IEnvironmentVariableHelper environmentVariableHelper) -> void
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.GetDotnetPath() -> string
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.GetMonoPath() -> string
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.GetDotnetPathByArchitecture(Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.PlatformArchitecture targetArchitecture) -> string
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper.GetDotnetPath() -> string
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper.GetMonoPath() -> string
Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper.GetDotnetPathByArchitecture(Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.PlatformArchitecture targetArchitecture) -> string
Microsoft.VisualStudio.TestPlatform.ObjectModel.EqtTrace
Microsoft.VisualStudio.TestPlatform.ObjectModel.ValidateArg
Microsoft.VisualStudio.TestPlatform.ObjectModel.ValidateArgProperty
Expand Down Expand Up @@ -251,4 +253,22 @@ static Microsoft.VisualStudio.TestPlatform.Utilities.OutputExtensions.Warning(th
static Microsoft.VisualStudio.TestPlatform.Utilities.OutputExtensions.Write(this Microsoft.VisualStudio.TestPlatform.Utilities.IOutput output, string message, Microsoft.VisualStudio.TestPlatform.Utilities.OutputLevel level, System.ConsoleColor foregroundColor) -> void
static Microsoft.VisualStudio.TestPlatform.Utilities.TimeSpanParser.Parse(string time) -> System.TimeSpan
static Microsoft.VisualStudio.TestPlatform.Utilities.TimeSpanParser.TryParse(string time, out System.TimeSpan result) -> bool
virtual Microsoft.VisualStudio.TestPlatform.Utilities.JobQueue<T>.WaitForQueueToGetEmpty() -> bool
virtual Microsoft.VisualStudio.TestPlatform.Utilities.JobQueue<T>.WaitForQueueToGetEmpty() -> bool
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IWindowsRegistryHelper
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IWindowsRegistryHelper.OpenBaseKey(Microsoft.Win32.RegistryHive hKey, Microsoft.Win32.RegistryView view) -> Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IRegistryKey
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IRegistryKey
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IRegistryKey.OpenSubKey(string name) -> Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IRegistryKey
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IRegistryKey.GetValue(string name) -> object
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.WindowsRegistryHelper
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.WindowsRegistryHelper.WindowsRegistryHelper() -> void
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.WindowsRegistryHelper.OpenBaseKey(Microsoft.Win32.RegistryHive hKey, Microsoft.Win32.RegistryView view) -> Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IRegistryKey
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.RegistryKeyWrapper
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.RegistryKeyWrapper.RegistryKeyWrapper(Microsoft.Win32.RegistryKey registryKey) -> void
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.RegistryKeyWrapper.OpenSubKey(string name) -> Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IRegistryKey
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.RegistryKeyWrapper.GetValue(string name) -> object
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.RegistryKeyWrapper.Dispose() -> void
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IEnvironmentVariableHelper
Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IEnvironmentVariableHelper.GetEnvironmentVariable(string variable) -> string
Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.EnvironmentVariableHelper
Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.EnvironmentVariableHelper.EnvironmentVariableHelper() -> void
Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.EnvironmentVariableHelper.GetEnvironmentVariable(string variable) -> string
Loading