.CaptureStdErr();
}
- public static Command DotNetRoot(this Command command, string dotNetRoot)
+ public static Command DotNetRoot(this Command command, string dotNetRoot, string architecture = null)
{
+ if (!string.IsNullOrEmpty(architecture))
+ return command.EnvironmentVariable($"DOTNET_ROOT_{architecture.ToUpper()}", dotNetRoot);
+
return command
.EnvironmentVariable("DOTNET_ROOT", dotNetRoot)
.EnvironmentVariable("DOTNET_ROOT(x86)", dotNetRoot);
--- /dev/null
+// 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.Runtime.InteropServices;
+using FluentAssertions;
+using Microsoft.DotNet.Cli.Build.Framework;
+using Microsoft.DotNet.CoreSetup.Test;
+using Xunit;
+
+namespace HostActivation.Tests
+{
+ internal static class InstallLocationCommandResultExtensions
+ {
+ private static bool IsRunningInWoW64(string rid) => OperatingSystem.IsWindows() && Environment.Is64BitOperatingSystem && rid.Equals("win-x86");
+
+ public static AndConstraint<CommandResultAssertions> HaveUsedDotNetRootInstallLocation(this CommandResultAssertions assertion, string installLocation, string rid)
+ {
+ return assertion.HaveUsedDotNetRootInstallLocation(installLocation, rid, null);
+ }
+
+ public static AndConstraint<CommandResultAssertions> HaveUsedDotNetRootInstallLocation(this CommandResultAssertions assertion,
+ string installLocation,
+ string rid,
+ string arch)
+ {
+ // If no arch is passed and we are on Windows, we need the used RID for determining whether or not we are running on WoW64.
+ if (string.IsNullOrEmpty(arch))
+ Assert.NotNull(rid);
+
+ string expectedEnvironmentVariable = !string.IsNullOrEmpty(arch) ? $"DOTNET_ROOT_{arch.ToUpper()}" :
+ IsRunningInWoW64(rid) ? "DOTNET_ROOT(x86)" : "DOTNET_ROOT";
+
+ return assertion.HaveStdErrContaining($"Using environment variable {expectedEnvironmentVariable}=[{installLocation}] as runtime location.");
+ }
+
+ public static AndConstraint<CommandResultAssertions> HaveUsedConfigFileInstallLocation(this CommandResultAssertions assertion, string installLocation)
+ {
+ return assertion.HaveStdErrContaining($"Using install location '{installLocation}'.");
+ }
+
+ public static AndConstraint<CommandResultAssertions> HaveUsedGlobalInstallLocation(this CommandResultAssertions assertion, string installLocation)
+ {
+ return assertion.HaveStdErrContaining($"Using global installation location [{installLocation}]");
+ }
+
+ public static AndConstraint<CommandResultAssertions> HaveFoundDefaultInstallLocationInConfigFile(this CommandResultAssertions assertion, string installLocation)
+ {
+ return assertion.HaveStdErrContaining($"Found install location path '{installLocation}'.");
+ }
+
+ public static AndConstraint<CommandResultAssertions> HaveFoundArchSpecificInstallLocationInConfigFile(this CommandResultAssertions assertion, string installLocation, string arch)
+ {
+ return assertion.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{arch}').");
+ }
+ }
+}
--- /dev/null
+// 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 Microsoft.DotNet.Cli.Build.Framework;
+using Microsoft.DotNet.CoreSetup.Test;
+using Microsoft.DotNet.CoreSetup.Test.HostActivation;
+using Xunit;
+
+namespace HostActivation.Tests
+{
+ public class MultiArchInstallLocation : IClassFixture<MultiArchInstallLocation.SharedTestState>
+ {
+ private SharedTestState sharedTestState;
+
+ public MultiArchInstallLocation(SharedTestState fixture)
+ {
+ sharedTestState = fixture;
+ }
+
+ [Fact]
+ public void EnvironmentVariable_CurrentArchitectureIsUsedIfEnvVarSet()
+ {
+ var fixture = sharedTestState.PortableAppFixture
+ .Copy();
+
+ var appExe = fixture.TestProject.AppExe;
+ var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper();
+ Command.Create(appExe)
+ .EnableTracingAndCaptureOutputs()
+ .DotNetRoot(fixture.BuiltDotnet.BinPath, arch)
+ .Execute()
+ .Should().Pass()
+ .And.HaveUsedDotNetRootInstallLocation(fixture.BuiltDotnet.BinPath, fixture.CurrentRid, arch);
+ }
+
+ [Fact]
+ public void EnvironmentVariable_IfNoArchSpecificEnvVarIsFoundDotnetRootIsUsed()
+ {
+ var fixture = sharedTestState.PortableAppFixture
+ .Copy();
+
+ var appExe = fixture.TestProject.AppExe;
+ var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper();
+ Command.Create(appExe)
+ .EnableTracingAndCaptureOutputs()
+ .DotNetRoot(fixture.BuiltDotnet.BinPath)
+ .Execute()
+ .Should().Pass()
+ .And.HaveUsedDotNetRootInstallLocation(fixture.BuiltDotnet.BinPath, fixture.CurrentRid);
+ }
+
+ [Fact]
+ public void EnvironmentVariable_ArchSpecificDotnetRootIsUsedOverDotnetRoot()
+ {
+ var fixture = sharedTestState.PortableAppFixture
+ .Copy();
+
+ var appExe = fixture.TestProject.AppExe;
+ var arch = fixture.RepoDirProvider.BuildArchitecture.ToUpper();
+ var dotnet = fixture.BuiltDotnet.BinPath;
+ Command.Create(appExe)
+ .EnableTracingAndCaptureOutputs()
+ .DotNetRoot("non_existent_path")
+ .DotNetRoot(dotnet, arch)
+ .Execute()
+ .Should().Pass()
+ .And.HaveUsedDotNetRootInstallLocation(dotnet, fixture.CurrentRid, arch)
+ .And.NotHaveStdErrContaining("Using environment variable DOTNET_ROOT=");
+ }
+
+ [Fact]
+ [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
+ public void InstallLocationFile_ArchSpecificLocationIsPickedFirst()
+ {
+ var fixture = sharedTestState.PortableAppFixture
+ .Copy();
+
+ var appExe = fixture.TestProject.AppExe;
+ var arch1 = "someArch";
+ var path1 = "x/y/z";
+ var arch2 = fixture.RepoDirProvider.BuildArchitecture;
+ var path2 = "a/b/c";
+
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe))
+ {
+ registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] {
+ (string.Empty, path1),
+ (arch1, path1),
+ (arch2, path2)
+ });
+
+ Command.Create(appExe)
+ .EnableTracingAndCaptureOutputs()
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .DotNetRoot(null)
+ .Execute()
+ .Should().HaveFoundDefaultInstallLocationInConfigFile(path1)
+ .And.HaveFoundArchSpecificInstallLocationInConfigFile(path1, arch1)
+ .And.HaveFoundArchSpecificInstallLocationInConfigFile(path2, arch2)
+ .And.HaveUsedGlobalInstallLocation(path2);
+ }
+ }
+
+ [Fact]
+ [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
+ public void InstallLocationFile_OnlyFirstLineMayNotSpecifyArchitecture()
+ {
+ var fixture = sharedTestState.PortableAppFixture
+ .Copy();
+
+ var appExe = fixture.TestProject.AppExe;
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe))
+ {
+ registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] {
+ (string.Empty, "a/b/c"),
+ (string.Empty, "x/y/z"),
+ });
+ Command.Create(appExe)
+ .EnableTracingAndCaptureOutputs()
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .DotNetRoot(null)
+ .Execute()
+ .Should().HaveFoundDefaultInstallLocationInConfigFile("a/b/c")
+ .And.HaveStdErrContaining($"Only the first line in '{registeredInstallLocationOverride.PathValueOverride}' may not have an architecture prefix.")
+ .And.HaveUsedConfigFileInstallLocation("a/b/c");
+ }
+ }
+
+ [Fact]
+ [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
+ public void InstallLocationFile_ReallyLongInstallPathIsParsedCorrectly()
+ {
+ var fixture = sharedTestState.PortableAppFixture
+ .Copy();
+
+ var appExe = fixture.TestProject.AppExe;
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe))
+ {
+ var reallyLongPath =
+ "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreally" +
+ "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreally" +
+ "reallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongpath";
+ registeredInstallLocationOverride.SetInstallLocation((string.Empty, reallyLongPath));
+
+ Command.Create(appExe)
+ .EnableTracingAndCaptureOutputs()
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .DotNetRoot(null)
+ .Execute()
+ .Should().HaveFoundDefaultInstallLocationInConfigFile(reallyLongPath)
+ .And.HaveUsedConfigFileInstallLocation(reallyLongPath);
+ }
+ }
+
+ public class SharedTestState : IDisposable
+ {
+ public string BaseDirectory { get; }
+ public TestProjectFixture PortableAppFixture { get; }
+ public RepoDirectoriesProvider RepoDirectories { get; }
+ public string InstallLocation { get; }
+
+ public SharedTestState()
+ {
+ RepoDirectories = new RepoDirectoriesProvider();
+ var fixture = new TestProjectFixture("PortableApp", RepoDirectories);
+ fixture
+ .EnsureRestored()
+ // App Host generation is turned off by default on macOS
+ .PublishProject(extraArgs: "/p:UseAppHost=true");
+
+ PortableAppFixture = fixture;
+ BaseDirectory = Path.GetDirectoryName(PortableAppFixture.SdkDotnet.GreatestVersionHostFxrFilePath);
+ }
+
+ public void Dispose()
+ {
+ PortableAppFixture.Dispose();
+ }
+ }
+ }
+}
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(DotNet.GreatestVersionHostFxrFilePath))
{
- registeredInstallLocationOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture);
+ registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { (RepoDirectories.BuildArchitecture, _regDir) });
// Add SDK versions
AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.4");
{
if (useRegisteredLocation)
{
- registeredInstallLocationOverride.SetInstallLocation(installLocation, sharedState.RepoDirectories.BuildArchitecture);
+ registeredInstallLocationOverride.SetInstallLocation((sharedState.RepoDirectories.BuildArchitecture, installLocation));
}
result = Command.Create(sharedState.NativeHostPath, $"{GetHostFxrPath} {explicitLoad} {(useAssemblyPath ? sharedState.TestAssemblyPath : string.Empty)}")
[Theory]
[SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
- [InlineData("{0}", true)]
- [InlineData("{0}\n", true)]
- [InlineData("{0}\nSome other text", true)]
- [InlineData("", false)]
- [InlineData("\n{0}", false)]
- [InlineData(" {0}", false)]
- [InlineData("{0} \n", false)]
- [InlineData("{0} ", false)]
- public void GetHostFxrPath_InstallLocationFile(string value, bool shouldPass)
+ [InlineData("{0}", false, true)]
+ [InlineData("{0}\n", false, true)]
+ [InlineData("{0}\nSome other text", false, true)]
+ [InlineData("", false, false)]
+ [InlineData("\n{0}", false, false)]
+ [InlineData(" {0}", false, false)]
+ [InlineData("{0} \n", false, false)]
+ [InlineData("{0} ", false, false)]
+ [InlineData("{0}", true, true)]
+ [InlineData("{0}\n", true, true)]
+ [InlineData("{0}\nSome other text", true, true)]
+ [InlineData("", true, false)]
+ [InlineData("\n{0}", true, false)]
+ [InlineData(" {0}", true, false)]
+ [InlineData("{0} \n", true, false)]
+ [InlineData("{0} ", true, false)]
+ public void GetHostFxrPath_InstallLocationFile(string value, bool shouldUseArchSpecificInstallLocation, bool shouldPass)
{
string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet");
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath))
{
- File.WriteAllText(registeredInstallLocationOverride.PathValueOverride, string.Format(value, installLocation));
+ if (shouldUseArchSpecificInstallLocation)
+ registeredInstallLocationOverride.SetInstallLocation((sharedState.RepoDirectories.BuildArchitecture, string.Format(value, installLocation)));
+ else
+ registeredInstallLocationOverride.SetInstallLocation((string.Empty, string.Format(value, installLocation)));
CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath)
.EnableTracingAndCaptureOutputs()
}
[Fact]
+ [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
+ public void GetHostFxrPath_GlobalInstallation_HasMoreThanOneDefaultInstallationPath()
+ {
+ string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet");
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath))
+ {
+ registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] {
+ (string.Empty, installLocation), (string.Empty, installLocation)
+ });
+
+ CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath)
+ .EnableTracingAndCaptureOutputs()
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass
+ Constants.TestOnlyEnvironmentVariables.DefaultInstallPath,
+ sharedState.InvalidInstallRoot)
+ .DotNetRoot(null)
+ .Execute();
+
+ result.Should().Pass()
+ .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.")
+ .And.HaveStdErrContaining($"Found install location path '{installLocation}'.")
+ .And.HaveStdErrContaining($"Only the first line in '{registeredInstallLocationOverride.PathValueOverride}' may not have an architecture prefix.")
+ .And.HaveStdErrContaining($"Using install location '{installLocation}'.")
+ .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower());
+ }
+ }
+
+ [Fact]
+ [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
+ public void GetHostFxrPath_GlobalInstallation_HasNoDefaultInstallationPath()
+ {
+ string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet");
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath))
+ {
+ registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] {
+ (sharedState.RepoDirectories.BuildArchitecture, installLocation),
+ ("someOtherArch", $"{installLocation}/invalid")
+ });
+
+ CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath)
+ .EnableTracingAndCaptureOutputs()
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass
+ Constants.TestOnlyEnvironmentVariables.DefaultInstallPath,
+ sharedState.InvalidInstallRoot)
+ .DotNetRoot(null)
+ .Execute();
+
+ result.Should().Pass()
+ .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.")
+ .And.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{sharedState.RepoDirectories.BuildArchitecture.ToLower()}').")
+ .And.HaveStdErrContaining($"Using install location '{installLocation}'.")
+ .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower());
+ }
+ }
+
+ [Fact]
+ [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
+ public void GetHostFxrPath_GlobalInstallation_ArchitectureSpecificPathIsPickedOverDefaultPath()
+ {
+ string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet");
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(sharedState.NethostPath))
+ {
+ registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] {
+ (string.Empty, $"{installLocation}/a/b/c"),
+ (sharedState.RepoDirectories.BuildArchitecture, installLocation)
+ });
+
+ CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath)
+ .EnableTracingAndCaptureOutputs()
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass
+ Constants.TestOnlyEnvironmentVariables.DefaultInstallPath,
+ sharedState.InvalidInstallRoot)
+ .DotNetRoot(null)
+ .Execute();
+
+ result.Should().Pass()
+ .And.HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.")
+ .And.HaveStdErrContaining($"Found install location path '{installLocation}/a/b/c'.")
+ .And.HaveStdErrContaining($"Found architecture-specific install location path: '{installLocation}' ('{sharedState.RepoDirectories.BuildArchitecture.ToLower()}').")
+ .And.HaveStdErrContaining($"Using install location '{installLocation}'.")
+ .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower());
+ }
+ }
+
+ [Fact]
public void GetHostFxrPath_InvalidParameters()
{
Command.Create(sharedState.NativeHostPath, $"{GetHostFxrPath} false [error]")
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
- .DotNetRoot(builtDotnet)
+ .DotNetRoot(builtDotnet, sharedTestState.RepoDirectories.BuildArchitecture)
.MultilevelLookup(false)
.Execute()
.Should().Pass()
// Verify running from within the working directory
Command.Create(appExe)
.WorkingDirectory(fixture.TestProject.OutputDirectory)
- .DotNetRoot(builtDotnet)
+ .DotNetRoot(builtDotnet, sharedTestState.RepoDirectories.BuildArchitecture)
.MultilevelLookup(false)
.CaptureStdErr()
.CaptureStdOut()
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe))
{
- string architecture = fixture.CurrentRid.Split('-')[1];
+ string architecture = fixture.RepoDirProvider.BuildArchitecture;
if (useRegisteredLocation)
{
- registeredInstallLocationOverride.SetInstallLocation(builtDotnet, architecture);
+ registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { (architecture, builtDotnet) });
}
// Verify running with the default working directory
if (useAppHost)
{
command = Command.Create(sharedTestState.MockApp.AppExe)
- .DotNetRoot(sharedTestState.BuiltDotNet.BinPath);
+ .DotNetRoot(sharedTestState.BuiltDotNet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture);
}
else
{
if (useAppHost)
{
command = Command.Create(app.AppExe)
- .DotNetRoot(sharedTestState.BuiltDotNet.BinPath);
+ .DotNetRoot(sharedTestState.BuiltDotNet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture);
}
else
{
Command command = Command.Create(appExe)
.EnableTracingAndCaptureOutputs()
- .DotNetRoot(dotnet.BinPath)
+ .DotNetRoot(dotnet.BinPath, sharedTestState.RepoDirectories.BuildArchitecture)
.MultilevelLookup(false)
.Start();
using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.Win32;
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
}
}
- public void SetInstallLocation(string installLocation, string architecture)
+ public void SetInstallLocation(params (string Architecture, string Path)[] locations)
{
+ Debug.Assert(locations.Length >= 1);
if (OperatingSystem.IsWindows())
{
- using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{architecture}"))
+ foreach (var location in locations)
{
- dotnetLocationKey.SetValue("InstallLocation", installLocation);
+ using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{location.Architecture}"))
+ {
+ dotnetLocationKey.SetValue("InstallLocation", location.Path);
+ }
}
}
else
{
- File.WriteAllText(PathValueOverride, installLocation);
+ File.WriteAllText(PathValueOverride, string.Join(Environment.NewLine,
+ locations.Select(l => string.Format("{0}{1}",
+ (!string.IsNullOrWhiteSpace(l.Architecture) ? l.Architecture + "=" : ""), l.Path))));
}
}
// self-contained layout since a flat layout of the shared framework is not supported.
Command.Create(appExe)
.EnvironmentVariable("COREHOST_TRACE", "1")
- .EnvironmentVariable("DOTNET_ROOT", newOutDir)
- .EnvironmentVariable("DOTNET_ROOT(x86)", newOutDir)
+ .DotNetRoot(newOutDir)
.CaptureStdErr()
.CaptureStdOut()
.Execute(fExpectedToFail: true)
public TestProjectFixture EnsureRestored(params string[] fallbackSources)
{
- if ( ! TestProject.IsRestored())
+ if (!TestProject.IsRestored())
{
RestoreProject(fallbackSources);
}
public TestProjectFixture EnsureRestoredForRid(string rid, params string[] fallbackSources)
{
- if ( ! TestProject.IsRestored())
+ if (!TestProject.IsRestored())
{
string extraMSBuildProperties = $"/p:TestTargetRid={rid}";
RestoreProject(fallbackSources, extraMSBuildProperties);
size_t pos = lib_name.find(_X("/"));
entry.library_name = lib_name.substr(0, pos);
entry.library_version = lib_name.substr(pos + 1);
- entry.library_type = pal::to_lower(library.value[_X("type")].GetString());
+ entry.library_type = to_lower(library.value[_X("type")].GetString());
entry.library_hash = hash;
entry.library_path = library_path;
entry.library_hash_path = library_hash_path;
while (arg_i < argc)
{
const pal::char_t* arg = argv[arg_i];
- pal::string_t arg_lower = pal::to_lower(arg);
+ pal::string_t arg_lower = to_lower(arg);
const auto &iter = std::find_if(known_opts.cbegin(), known_opts.cend(),
[&](const known_options &opt) { return arg_lower == get_host_option(opt).option; });
if (iter == known_opts.cend())
return true;
}
- // For framework-dependent apps, use DOTNET_ROOT
+ // For framework-dependent apps, use DOTNET_ROOT_<ARCH>
pal::string_t default_install_location;
- pal::string_t dotnet_root_env_var_name = get_dotnet_root_env_var_name();
- if (get_file_path_from_env(dotnet_root_env_var_name.c_str(), out_dotnet_root))
+ pal::string_t dotnet_root_env_var_name;
+ if (get_dotnet_root_from_env(&dotnet_root_env_var_name, out_dotnet_root))
{
trace::info(_X("Using environment variable %s=[%s] as runtime location."), dotnet_root_env_var_name.c_str(), out_dotnet_root->c_str());
}
#endif // !FEATURE_APPHOST && !FEATURE_LIBHOST
}
-bool fxr_resolver::try_get_path_from_dotnet_root(const pal::string_t &dotnet_root, pal::string_t *out_fxr_path)
+bool fxr_resolver::try_get_path_from_dotnet_root(const pal::string_t& dotnet_root, pal::string_t* out_fxr_path)
{
pal::string_t fxr_dir = dotnet_root;
append_path(&fxr_dir, _X("host"));
return get_latest_fxr(std::move(fxr_dir), out_fxr_path);
}
-bool fxr_resolver::try_get_existing_fxr(pal::dll_t *out_fxr, pal::string_t *out_fxr_path)
+bool fxr_resolver::try_get_existing_fxr(pal::dll_t* out_fxr, pal::string_t* out_fxr_path)
{
if (!pal::get_loaded_library(LIBFXR_NAME, "hostfxr_main", out_fxr, out_fxr_path))
return false;
namespace pal
{
#if defined(_WIN32)
- #ifdef EXPORT_SHARED_API
- #define SHARED_API extern "C" __declspec(dllexport)
- #else
- #define SHARED_API extern "C"
- #endif
+#ifdef EXPORT_SHARED_API
+#define SHARED_API extern "C" __declspec(dllexport)
+#else
+#define SHARED_API extern "C"
+#endif
- #define STDMETHODCALLTYPE __stdcall
+#define STDMETHODCALLTYPE __stdcall
typedef wchar_t char_t;
typedef std::wstring string_t;
inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::_wcsicmp(str1, str2); }
inline int strncmp(const char_t* str1, const char_t* str2, size_t len) { return ::wcsncmp(str1, str2, len); }
inline int strncasecmp(const char_t* str1, const char_t* str2, size_t len) { return ::_wcsnicmp(str1, str2, len); }
- inline int pathcmp(const pal::string_t &path1, const pal::string_t &path2) { return strcasecmp(path1.c_str(), path2.c_str()); }
+ inline int pathcmp(const pal::string_t& path1, const pal::string_t& path2) { return strcasecmp(path1.c_str(), path2.c_str()); }
inline string_t to_string(int value) { return std::to_wstring(value); }
inline size_t strlen(const char_t* str) { return ::wcslen(str); }
#pragma warning(suppress : 4996) // error C4996: '_wfopen': This function or variable may be unsafe.
- inline FILE * file_open(const string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); }
+ inline FILE* file_open(const string_t& path, const char_t* mode) { return ::_wfopen(path.c_str(), mode); }
inline void file_vprintf(FILE* f, const char_t* format, va_list vl) { ::vfwprintf(f, format, vl); ::fputwc(_X('\n'), f); }
inline void err_fputs(const char_t* message) { ::fputws(message, stderr); ::fputwc(_X('\n'), stderr); }
// Suppressing warning since the 'safe' version requires an input buffer that is unnecessary for
// uses of this function.
#pragma warning(suppress : 4996) // error C4996: '_wcserror': This function or variable may be unsafe.
- inline const char_t* strerror(int errnum){ return ::_wcserror(errnum); }
+ inline const char_t* strerror(int errnum) { return ::_wcserror(errnum); }
bool pal_utf8string(const string_t& str, std::vector<char>* out);
bool pal_clrstring(const string_t& str, std::vector<char>* out);
bool clr_palstring(const char* cstr, string_t* out);
inline bool mkdir(const char_t* dir, int mode) { return CreateDirectoryW(dir, NULL) != 0; }
- inline bool rmdir (const char_t* path) { return RemoveDirectoryW(path) != 0; }
+ inline bool rmdir(const char_t* path) { return RemoveDirectoryW(path) != 0; }
inline int rename(const char_t* old_name, const char_t* new_name) { return ::_wrename(old_name, new_name); }
inline int remove(const char_t* path) { return ::_wremove(path); }
inline bool munmap(void* addr, size_t length) { return UnmapViewOfFile(addr) != 0; }
inline int get_pid() { return GetCurrentProcessId(); }
inline void sleep(uint32_t milliseconds) { Sleep(milliseconds); }
#else
- #ifdef EXPORT_SHARED_API
- #define SHARED_API extern "C" __attribute__((__visibility__("default")))
- #else
- #define SHARED_API extern "C"
- #endif
-
- #define __cdecl /* nothing */
- #define __stdcall /* nothing */
- #if !defined(TARGET_FREEBSD)
- #define __fastcall /* nothing */
- #endif
- #define STDMETHODCALLTYPE __stdcall
+#ifdef EXPORT_SHARED_API
+#define SHARED_API extern "C" __attribute__((__visibility__("default")))
+#else
+#define SHARED_API extern "C"
+#endif
+
+#define __cdecl /* nothing */
+#define __stdcall /* nothing */
+#if !defined(TARGET_FREEBSD)
+#define __fastcall /* nothing */
+#endif
+#define STDMETHODCALLTYPE __stdcall
typedef char char_t;
typedef std::string string_t;
inline string_t to_string(int value) { return std::to_string(value); }
inline size_t strlen(const char_t* str) { return ::strlen(str); }
- inline FILE * file_open(const string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); }
+ inline FILE* file_open(const string_t& path, const char_t* mode) { return fopen(path.c_str(), mode); }
inline void file_vprintf(FILE* f, const char_t* format, va_list vl) { ::vfprintf(f, format, vl); ::fputc('\n', f); }
inline void err_fputs(const char_t* message) { ::fputs(message, stderr); ::fputc(_X('\n'), stderr); }
inline void out_vprintf(const char_t* format, va_list vl) { ::vfprintf(stdout, format, vl); ::fputc('\n', stdout); }
inline bool munmap(void* addr, size_t length) { return ::munmap(addr, length) == 0; }
inline int get_pid() { return getpid(); }
inline void sleep(uint32_t milliseconds) { usleep(milliseconds * 1000); }
-
#endif
inline int snwprintf(char_t* buffer, size_t count, const char_t* format, ...)
string_t get_timestamp();
bool getcwd(string_t* recv);
- string_t to_lower(const char_t* in);
-
- inline void file_flush(FILE *f) { std::fflush(f); }
+ inline void file_flush(FILE* f) { std::fflush(f); }
inline void err_flush() { std::fflush(stderr); }
inline void out_flush() { std::fflush(stdout); }
bool get_own_module_path(string_t* recv);
bool get_method_module_path(string_t* recv, void* method);
bool get_module_path(dll_t mod, string_t* recv);
- bool get_current_module(dll_t *mod);
+ bool get_current_module(dll_t* mod);
bool getenv(const char_t* name, string_t* recv);
bool get_default_servicing_directory(string_t* recv);
int xtoi(const char_t* input);
- bool get_loaded_library(const char_t *library_name, const char *symbol_name, /*out*/ dll_t *dll, /*out*/ string_t *path);
+ bool get_loaded_library(const char_t* library_name, const char* symbol_name, /*out*/ dll_t* dll, /*out*/ string_t* path);
bool load_library(const string_t* path, dll_t* dll);
proc_t get_symbol(dll_t library, const char* name);
void unload_library(dll_t library);
#error "Don't know how to obtain max path on this platform"
#endif
-pal::string_t pal::to_lower(const pal::char_t* in)
-{
- pal::string_t ret = in;
- std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
- return ret;
-}
-
pal::string_t pal::get_timestamp()
{
std::time_t t = std::time(nullptr);
trace::warning(_X("open(%s) failed in %s"), path.c_str(), _STRINGIFY(__FUNCTION__));
return false;
}
- (void) close(fd);
+ (void)close(fd);
return true;
}
namespace
{
- bool get_loaded_library_from_proc_maps(const pal::char_t *library_name, pal::dll_t *dll, pal::string_t *path)
+ bool get_loaded_library_from_proc_maps(const pal::char_t* library_name, pal::dll_t* dll, pal::string_t* path)
{
- char *line = nullptr;
+ char* line = nullptr;
size_t lineLen = 0;
ssize_t read;
- FILE *file = pal::file_open(_X("/proc/self/maps"), _X("r"));
+ FILE* file = pal::file_open(_X("/proc/self/maps"), _X("r"));
if (file == nullptr)
return false;
}
bool pal::get_loaded_library(
- const char_t *library_name,
- const char *symbol_name,
- /*out*/ dll_t *dll,
- /*out*/ pal::string_t *path)
+ const char_t* library_name,
+ const char* symbol_name,
+ /*out*/ dll_t* dll,
+ /*out*/ pal::string_t* path)
{
pal::string_t library_name_local;
#if defined(TARGET_OSX)
bool is_read_write_able_directory(pal::string_t& dir)
{
return pal::realpath(&dir) &&
- (access(dir.c_str(), R_OK | W_OK | X_OK) == 0);
+ (access(dir.c_str(), R_OK | W_OK | X_OK) == 0);
}
bool get_extraction_base_parent_directory(pal::string_t& directory)
bool pal::get_dotnet_self_registered_config_location(pal::string_t* recv)
{
- *recv = _X("/etc/dotnet/install_location");
+ recv->assign(_X("/etc/dotnet/install_location"));
// ***Used only for testing***
pal::string_t environment_install_location_override;
if (test_only_getenv(_X("_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH"), &environment_install_location_override))
{
- *recv = environment_install_location_override;
+ recv->assign(environment_install_location_override);
}
return true;
}
+namespace
+{
+ bool get_line_from_file(FILE* pFile, pal::string_t& line)
+ {
+ line = pal::string_t();
+ char buffer[256];
+ while (fgets(buffer, sizeof(buffer), pFile))
+ {
+ line += (pal::char_t*)buffer;
+ size_t len = line.length();
+
+ // fgets includes the newline character in the string - so remove it.
+ if (len > 0 && line[len - 1] == '\n')
+ {
+ line.pop_back();
+ break;
+ }
+ }
+
+ return !line.empty();
+ }
+}
+
bool pal::get_dotnet_self_registered_dir(pal::string_t* recv)
{
recv->clear();
FILE* install_location_file = pal::file_open(install_location_file_path, "r");
if (install_location_file == nullptr)
{
- trace::verbose(_X("The install_location file failed to open."));
+ trace::error(_X("The install_location file ['%s'] failed to open: %s."), install_location_file_path.c_str(), pal::strerror(errno));
return false;
}
- bool result = false;
+ pal::string_t install_location;
+ int current_line = 0;
+ bool is_first_line = true, install_location_found = false;
- char buf[PATH_MAX];
- char* install_location = fgets(buf, sizeof(buf), install_location_file);
- if (install_location != nullptr)
+ while (get_line_from_file(install_location_file, install_location))
{
- size_t len = pal::strlen(install_location);
+ current_line++;
+ size_t arch_sep = install_location.find(_X('='));
+ if (arch_sep == pal::string_t::npos)
+ {
+ if (is_first_line)
+ {
+ recv->assign(install_location);
+ install_location_found = true;
+ trace::verbose(_X("Found install location path '%s'."), install_location.c_str());
+ }
+ else
+ {
+ trace::warning(_X("Found unprefixed install location path '%s' on line %d."), install_location.c_str(), current_line);
+ trace::warning(_X("Only the first line in '%s' may not have an architecture prefix."), install_location_file_path.c_str());
+ }
+
+ is_first_line = false;
+ continue;
+ }
+
+ pal::string_t arch_prefix = install_location.substr(0, arch_sep);
+ pal::string_t path_to_location = install_location.substr(arch_sep + 1);
- // fgets includes the newline character in the string - so remove it.
- if (len > 0 && len < PATH_MAX && install_location[len - 1] == '\n')
+ trace::verbose(_X("Found architecture-specific install location path: '%s' ('%s')."), path_to_location.c_str(), arch_prefix.c_str());
+ if (pal::strcasecmp(arch_prefix.c_str(), get_arch()) == 0)
{
- install_location[len - 1] = '\0';
+ recv->assign(path_to_location);
+ install_location_found = true;
+ trace::verbose(_X("Found architecture-specific install location path matching the current host architecture ('%s'): '%s'."), arch_prefix.c_str(), path_to_location.c_str());
+ break;
}
- trace::verbose(_X("Using install location '%s'."), install_location);
- *recv = install_location;
- result = true;
+ is_first_line = false;
}
- else
+
+ fclose(install_location_file);
+ if (!install_location_found)
{
- trace::verbose(_X("The install_location file first line could not be read."));
+ trace::warning(_X("Did not find any install location in '%s'."), install_location_file_path.c_str());
+ return false;
}
- fclose(install_location_file);
- return result;
+ trace::verbose(_X("Using install location '%s'."), recv->c_str());
+ return true;
}
bool pal::get_default_installation_dir(pal::string_t* recv)
// ***************************
#if defined(TARGET_OSX)
- recv->assign(_X("/usr/local/share/dotnet"));
+ recv->assign(_X("/usr/local/share/dotnet"));
#else
- recv->assign(_X("/usr/share/dotnet"));
+ recv->assign(_X("/usr/share/dotnet"));
#endif
- return true;
+ return true;
}
pal::string_t trim_quotes(pal::string_t stringToCleanup)
{
- pal::char_t quote_array[2] = {'\"', '\''};
- for (size_t index = 0; index < sizeof(quote_array)/sizeof(quote_array[0]); index++)
+ pal::char_t quote_array[2] = { '\"', '\'' };
+ for (size_t index = 0; index < sizeof(quote_array) / sizeof(quote_array[0]); index++)
{
size_t pos = stringToCleanup.find(quote_array[index]);
while (pos != std::string::npos)
if (ret == 0)
{
- char *pos = strchr(str, '.');
+ char* pos = strchr(str, '.');
if (pos)
{
ridOS.append(_X("freebsd."))
- .append(str, pos - str);
+ .append(str, pos - str);
}
}
if (strncmp(utsname_obj.version, "omnios", strlen("omnios")) == 0)
{
ridOS.append(_X("omnios."))
- .append(utsname_obj.version, strlen("omnios-r"), 2); // e.g. omnios.15
+ .append(utsname_obj.version, strlen("omnios-r"), 2); // e.g. omnios.15
}
else if (strncmp(utsname_obj.version, "illumos-", strlen("illumos-")) == 0)
{
else if (strncmp(utsname_obj.version, "joyent_", strlen("joyent_")) == 0)
{
ridOS.append(_X("smartos."))
- .append(utsname_obj.version, strlen("joyent_"), 4); // e.g. smartos.2020
+ .append(utsname_obj.version, strlen("joyent_"), 4); // e.g. smartos.2020
}
return ridOS;
return ridOS;
}
- char *pos = strchr(utsname_obj.version, '.');
+ char* pos = strchr(utsname_obj.version, '.');
if (pos)
{
ridOS.append(_X("solaris."))
- .append(utsname_obj.version, pos - utsname_obj.version); // e.g. solaris.11
+ .append(utsname_obj.version, pos - utsname_obj.version); // e.g. solaris.11
}
return ridOS;
bool pal::get_own_module_path(string_t* recv)
{
Dl_info info;
- if (dladdr((void *)&pal::get_own_module_path, &info) == 0)
+ if (dladdr((void*)&pal::get_own_module_path, &info) == 0)
return false;
recv->assign(info.dli_fname);
return false;
}
-bool pal::get_current_module(dll_t *mod)
+bool pal::get_current_module(dll_t* mod)
{
return false;
}
}
break;
- // Handle symlinks and file systems that do not support d_type
+ // Handle symlinks and file systems that do not support d_type
case DT_LNK:
case DT_UNKNOWN:
- {
- struct stat sb;
+ {
+ struct stat sb;
- if (fstatat(dirfd(dir), entry->d_name, &sb, 0) == -1)
- {
- continue;
- }
+ if (fstatat(dirfd(dir), entry->d_name, &sb, 0) == -1)
+ {
+ continue;
+ }
- if (onlydirectories)
- {
- if (!S_ISDIR(sb.st_mode))
- {
- continue;
- }
- break;
- }
- else if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode))
+ if (onlydirectories)
+ {
+ if (!S_ISDIR(sb.st_mode))
{
continue;
}
+ break;
}
- break;
+ else if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode))
+ {
+ continue;
+ }
+ }
+ break;
default:
continue;
return false;
path.resize(dwModuleFileName);
- *recv = path;
+ recv->assign(path);
return true;
}
return (res != FALSE);
}
-pal::string_t pal::to_lower(const pal::char_t* in)
-{
- pal::string_t ret = in;
- std::transform(ret.begin(), ret.end(), ret.begin(), ::towlower);
- return ret;
-}
-
pal::string_t pal::get_timestamp()
{
std::time_t t = std::time(nullptr);
const pal::char_t* value;
get_dotnet_install_location_registry_path(&key_hive, &sub_key, &value);
- *recv = (key_hive == HKEY_CURRENT_USER ? _X("HKCU\\") : _X("HKLM\\")) + sub_key + _X("\\") + value;
+ recv->assign((key_hive == HKEY_CURRENT_USER ? _X("HKCU\\") : _X("HKLM\\")) + sub_key + _X("\\") + value);
return true;
#endif
}
void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl)
{
- size_t pos = 0;
+ size_t pos = 0;
while ((pos = path->find(match, pos)) != pal::string_t::npos)
{
(*path)[pos] = repl;
pal::string_t get_replaced_char(const pal::string_t& path, pal::char_t match, pal::char_t repl)
{
- size_t pos = path.find(match);
+ size_t pos = path.find(match);
if (pos == pal::string_t::npos)
{
return path;
return true;
}
-bool get_global_shared_store_dirs(std::vector<pal::string_t>* dirs, const pal::string_t& arch, const pal::string_t& tfm)
+bool get_global_shared_store_dirs(std::vector<pal::string_t>* dirs, const pal::string_t& arch, const pal::string_t& tfm)
{
std::vector<pal::string_t> global_dirs;
if (!pal::get_global_dotnet_dirs(&global_dirs))
return true;
}
-pal::string_t get_dotnet_root_env_var_name()
+bool get_dotnet_root_from_env(pal::string_t* dotnet_root_env_var_name, pal::string_t* recv)
{
+ *dotnet_root_env_var_name = _X("DOTNET_ROOT_");
+ dotnet_root_env_var_name->append(to_upper(get_arch()));
+ if (get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv))
+ return true;
+
+#if defined(WIN32)
if (pal::is_running_in_wow64())
{
- return pal::string_t(_X("DOTNET_ROOT(x86)"));
+ *dotnet_root_env_var_name = _X("DOTNET_ROOT(x86)");
+ if (get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv))
+ return true;
}
+#endif
- return pal::string_t(_X("DOTNET_ROOT"));
+ // If no architecture-specific environment variable was set
+ // fallback to the default DOTNET_ROOT.
+ *dotnet_root_env_var_name = _X("DOTNET_ROOT");
+ return get_file_path_from_env(dotnet_root_env_var_name->c_str(), recv);
}
/**
trace::verbose(_X("Runtime config is cfg=%s dev=%s"), cfg->c_str(), dev_cfg->c_str());
}
-pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t &fxr_path)
+pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t& fxr_path)
{
// If coreclr exists next to hostfxr, assume everything is local (e.g. self-contained)
pal::string_t fxr_dir = get_directory(fxr_path);
return get_directory(get_directory(fxr_root));
}
-pal::string_t get_download_url(const pal::char_t *framework_name, const pal::char_t *framework_version)
+pal::string_t get_download_url(const pal::char_t* framework_name, const pal::char_t* framework_version)
{
pal::string_t url = DOTNET_CORE_APPLAUNCH_URL _X("?");
if (framework_name != nullptr && pal::strlen(framework_name) > 0)
return url;
}
+pal::string_t to_lower(const pal::char_t* in) {
+ pal::string_t ret = in;
+ std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
+ return ret;
+}
+
+pal::string_t to_upper(const pal::char_t* in) {
+ pal::string_t ret = in;
+ std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper);
+ return ret;
+}
+
#define TEST_ONLY_MARKER "d38cc827-e34f-4453-9df4-1e796e9f1d07"
// Retrieves environment variable which is only used for testing.
bool get_file_path_from_env(const pal::char_t* env_key, pal::string_t* recv);
size_t index_of_non_numeric(const pal::string_t& str, size_t i);
bool try_stou(const pal::string_t& str, unsigned* num);
-pal::string_t get_dotnet_root_env_var_name();
+bool get_dotnet_root_from_env(pal::string_t* used_dotnet_root_env_var_name, pal::string_t* recv);
pal::string_t get_deps_from_app_binary(const pal::string_t& app_base, const pal::string_t& app);
pal::string_t get_runtime_config_path(const pal::string_t& path, const pal::string_t& name);
pal::string_t get_runtime_config_dev_path(const pal::string_t& path, const pal::string_t& name);
void get_runtime_config_paths(const pal::string_t& path, const pal::string_t& name, pal::string_t* cfg, pal::string_t* dev_cfg);
-pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t &fxr_path);
+pal::string_t get_dotnet_root_from_fxr_path(const pal::string_t& fxr_path);
// Get a download URL for a specific framework and version
// If no framework is specified, a download URL for the runtime is returned
-pal::string_t get_download_url(const pal::char_t *framework_name = nullptr, const pal::char_t *framework_version = nullptr);
+pal::string_t get_download_url(const pal::char_t* framework_name = nullptr, const pal::char_t* framework_version = nullptr);
+
+pal::string_t to_lower(const pal::char_t* in);
+pal::string_t to_upper(const pal::char_t* in);
// Retrieves environment variable which is only used for testing.
// This will return the value of the variable only if the product binary is stamped
class propagate_error_writer_t
{
public:
- typedef trace::error_writer_fn(__cdecl *set_error_writer_fn)(trace::error_writer_fn error_writer);
+ typedef trace::error_writer_fn(__cdecl* set_error_writer_fn)(trace::error_writer_fn error_writer);
private:
set_error_writer_fn m_set_error_writer;
// args: ... [<explicit_load>] [<assembly_path>] [<dotnet_root>] [<hostfxr_to_load>]
bool explicit_load = false;
if (argc >= 3)
- explicit_load = pal::strcmp(pal::to_lower(argv[2]).c_str(), _X("true")) == 0;
+ explicit_load = pal::strcmp(to_lower(argv[2]).c_str(), _X("true")) == 0;
const pal::char_t *assembly_path = nullptr;
if (argc >= 4 && pal::strcmp(argv[3], _X("nullptr")) != 0)
if (static_cast<StatusCode>(res) == StatusCode::Success)
{
std::cout << "get_hostfxr_path succeeded" << std::endl;
- std::cout << "hostfxr_path: " << tostr(pal::to_lower(fxr_path.c_str())).data() << std::endl;
+ std::cout << "hostfxr_path: " << tostr(to_lower(fxr_path.c_str())).data() << std::endl;
return EXIT_SUCCESS;
}
else