if (argc >= 3)
assembly_path = argv[2];
-#if defined(_WIN32)
- pal::string_t testOverride;
- if (pal::getenv(_X("TEST_OVERRIDE_PROGRAMFILES"), &testOverride))
- {
- std::cout << tostr(testOverride).data() << std::endl;
- ::SetEnvironmentVariableW(_X("ProgramFiles"), testOverride.c_str());
- ::SetEnvironmentVariableW(_X("ProgramFiles(x86)"), testOverride.c_str());
- }
-#endif
-
if (argc >= 4)
{
pal::string_t to_load = argv[3];
bool getenv(const char_t* name, string_t* recv);
bool get_default_servicing_directory(string_t* recv);
- //On Linux, there are no global locations
- //On Windows there will be up to 2 global locations
- bool get_global_dotnet_dirs(std::vector<pal::string_t>* recv);
+ // Returns the globally registered install location (if any)
bool get_dotnet_self_registered_dir(pal::string_t* recv);
+
+ // Returns the default install location for a given platform
bool get_default_installation_dir(pal::string_t* recv);
+
+ // Returns the global locations to search for SDK/Frameworks - used when multi-level lookup is enabled
+ bool get_global_dotnet_dirs(std::vector<pal::string_t>* recv);
+
bool get_default_breadcrumb_store(string_t* recv);
bool is_path_rooted(const string_t& path);
bool pal::get_dotnet_self_registered_dir(pal::string_t* recv)
{
- // No support for global directories in Unix.
- return false;
+ recv->clear();
+
+ // ***Used only for testing***
+ pal::string_t environment_override;
+ if (pal::getenv(_X("_DOTNET_TEST_GLOBALLY_REGISTERED_PATH"), &environment_override))
+ {
+ recv->assign(environment_override);
+ return true;
+ }
+ // ***************************
+
+ pal::string_t install_location_file_path = _X("/etc/dotnet/install_location");
+
+ // ***Used only for testing***
+ pal::string_t environment_install_location_override;
+ if (pal::getenv(_X("_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH"), &environment_install_location_override))
+ {
+ install_location_file_path = environment_install_location_override;
+ }
+ // ***************************
+
+ trace::verbose(_X("Looking for install_location file in '%s'."), install_location_file_path.c_str());
+ 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."));
+ return false;
+ }
+
+ bool result = false;
+
+ char buf[PATH_MAX];
+ char* install_location = fgets(buf, sizeof(buf), install_location_file);
+ if (install_location != nullptr)
+ {
+ size_t len = pal::strlen(install_location);
+
+ // fgets includes the newline character in the string - so remove it.
+ if (len > 0 && len < PATH_MAX && install_location[len - 1] == '\n')
+ {
+ install_location[len - 1] = '\0';
+ }
+
+ trace::verbose(_X("Using install location '%s'."), install_location);
+ *recv = install_location;
+ result = true;
+ }
+ else
+ {
+ trace::verbose(_X("The install_location file first line could not be read."));
+ }
+
+ fclose(install_location_file);
+ return result;
}
bool pal::get_default_installation_dir(pal::string_t* recv)
{
+ // ***Used only for testing***
+ pal::string_t environmentOverride;
+ if (pal::getenv(_X("_DOTNET_TEST_DEFAULT_INSTALL_PATH"), &environmentOverride))
+ {
+ recv->assign(environmentOverride);
+ return true;
+ }
+ // ***************************
+
#if defined(__APPLE__)
recv->assign(_X("/usr/local/share/dotnet"));
#else
bool pal::get_default_installation_dir(pal::string_t* recv)
{
+ // ***Used only for testing***
+ pal::string_t environmentOverride;
+ if (pal::getenv(_X("_DOTNET_TEST_DEFAULT_INSTALL_PATH"), &environmentOverride))
+ {
+ recv->assign(environmentOverride);
+ return true;
+ }
+ // ***************************
+
pal::char_t* program_files_dir;
if (pal::is_running_in_wow64())
{
public static class TestOnlyEnvironmentVariables
{
+ public const string DefaultInstallPath = "_DOTNET_TEST_DEFAULT_INSTALL_PATH";
public const string RegistryPath = "_DOTNET_TEST_REGISTRY_PATH";
public const string GloballyRegisteredPath = "_DOTNET_TEST_GLOBALLY_REGISTERED_PATH";
+ public const string InstallLocationFilePath = "_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH";
}
}
}
return;
}
- using (var regKeyOverride = new RegisteredInstallKeyOverride())
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride())
{
- regKeyOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture);
+ registeredInstallLocationOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture);
// Add SDK versions
AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.4");
.WithUserProfile(_userDir)
.Environment(s_DefaultEnvironment)
.EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
- .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath)
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
.CaptureStdOut()
.CaptureStdErr()
.Execute()
[InlineData(true, false, true)]
public void GetHostFxrPath_GlobalInstallation(bool useAssemblyPath, bool useRegisteredLocation, bool isValid)
{
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- // We don't have a good way of hooking into how the product looks for global installations yet.
- return;
- }
-
// Overide the registry key for self-registered global installs.
// If using the registered location, set the install location value to the valid/invalid root.
// If not using the registered location, do not set the value. When the value does not exist,
// the product falls back to the default install location.
CommandResult result;
- string installRoot = Path.Combine(isValid ? sharedState.ValidInstallRoot : sharedState.InvalidInstallRoot);
- using (var regKeyOverride = new RegisteredInstallKeyOverride())
+ string installLocation = Path.Combine(isValid ? sharedState.ValidInstallRoot : sharedState.InvalidInstallRoot, "dotnet");
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride())
{
if (useRegisteredLocation)
{
- regKeyOverride.SetInstallLocation(Path.Combine(installRoot, "dotnet"), sharedState.RepoDirectories.BuildArchitecture);
+ registeredInstallLocationOverride.SetInstallLocation(installLocation, sharedState.RepoDirectories.BuildArchitecture);
}
- string programFilesOverride = useRegisteredLocation ? sharedState.InvalidInstallRoot : installRoot;
result = Command.Create(sharedState.NativeHostPath, $"{GetHostFxrPath} {(useAssemblyPath ? sharedState.TestAssemblyPath : string.Empty)}")
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable("COREHOST_TRACE", "1")
- .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath)
- .EnvironmentVariable("TEST_OVERRIDE_PROGRAMFILES", programFilesOverride)
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .EnvironmentVariable( // Redirect the default install location to a test directory
+ Constants.TestOnlyEnvironmentVariables.DefaultInstallPath,
+ useRegisteredLocation ? sharedState.InvalidInstallRoot : installLocation)
.Execute();
}
.And.HaveStdErrContaining($"Found previously loaded library {HostFxrName}");
}
+ [Theory]
+ [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)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // This test targets the install_location config file which is only used on Linux and macOS.
+ return;
+ }
+
+ string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet");
+
+ using (RegisteredInstallLocationOverride registeredInstallLocationOverride = new RegisteredInstallLocationOverride())
+ {
+ File.WriteAllText(registeredInstallLocationOverride.PathValueOverride, string.Format(value, installLocation));
+
+ CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath)
+ .CaptureStdErr()
+ .CaptureStdOut()
+ .EnvironmentVariable("COREHOST_TRACE", "1")
+ .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)
+ .Execute();
+
+ result.Should().HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.");
+
+ if (shouldPass)
+ {
+ result.Should().Pass()
+ .And.HaveStdErrContaining($"Using install location '{installLocation}'.")
+ .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower());
+ }
+ else
+ {
+ result.Should().Fail()
+ .And.ExitWith(1)
+ .And.HaveStdOutContaining($"{GetHostFxrPath} failed: 0x{CoreHostLibMissingFailure.ToString("x")}")
+ .And.HaveStdErrContaining($"The required library {HostFxrName} could not be found");
+ }
+ }
+ }
+
public class SharedTestState : SharedTestStateBase
{
public string HostFxrPath { get; }
using System.IO;
using System.Security.Cryptography;
using System.Text;
-using System.Runtime.InteropServices;
-using Microsoft.Win32;
using Xunit;
namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
{
public class PortableAppActivation : IClassFixture<PortableAppActivation.SharedTestState>
{
- private SharedTestState sharedTestState;
+ private readonly SharedTestState sharedTestState;
- public PortableAppActivation(PortableAppActivation.SharedTestState fixture)
+ public PortableAppActivation(SharedTestState fixture)
{
sharedTestState = fixture;
}
.Copy();
var dotnet = fixture.BuiltDotnet;
- var appDll = fixture.TestProject.AppDll.Replace(Path.DirectorySeparatorChar,Path.AltDirectorySeparatorChar);
+ var appDll = fixture.TestProject.AppDll.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
dotnet.Exec(appDll)
.CaptureStdErr()
var dotnet = fixture.BuiltDotnet;
var appDll = fixture.TestProject.AppDll;
-
+
dotnet.Exec("exec", "--runtimeconfig", runtimeConfig, appDll)
.CaptureStdErr()
.CaptureStdOut()
- .Execute(fExpectedToFail:true)
+ .Execute(fExpectedToFail: true)
.Should().Fail();
}
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");
-
+
}
[Fact]
dotnet.Exec("exec", "--depsfile", depsJson, appDll)
.CaptureStdErr()
.CaptureStdOut()
- .Execute(fExpectedToFail:true)
+ .Execute(fExpectedToFail: true)
.Should().Fail();
}
.And.HaveStdOutContaining($"Framework Version:{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}");
}
- [Fact]
- public void Framework_Dependent_AppHost_From_Global_Registry_Location_Succeeds()
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void Framework_Dependent_AppHost_From_Global_Location_Succeeds(bool useRegisteredLocation)
{
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- return;
- }
-
var fixture = sharedTestState.PortableAppFixture_Published
.Copy();
// Get the framework location that was built
string builtDotnet = fixture.BuiltDotnet.BinPath;
- using (var regKeyOverride = new RegisteredInstallKeyOverride())
+ using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride())
{
string architecture = fixture.CurrentRid.Split('-')[1];
- regKeyOverride.SetInstallLocation(builtDotnet, architecture);
+ if (useRegisteredLocation)
+ {
+ registeredInstallLocationOverride.SetInstallLocation(builtDotnet, architecture);
+ }
// Verify running with the default working directory
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
- .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath)
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, useRegisteredLocation ? null : builtDotnet)
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
// Verify running from within the working directory
Command.Create(appExe)
- .WorkingDirectory(fixture.TestProject.OutputDirectory)
- .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath)
.CaptureStdErr()
.CaptureStdOut()
+ .WorkingDirectory(fixture.TestProject.OutputDirectory)
+ .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
+ .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, useRegisteredLocation ? null : builtDotnet)
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
Directory.CreateDirectory(storeoutputDirectory);
}
- testProjectFixture.StoreProject(outputDirectory :storeoutputDirectory);
-
+ testProjectFixture.StoreProject(outputDirectory: storeoutputDirectory);
+
return storeoutputDirectory;
}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using Microsoft.Win32;
-using System;
-
-namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
-{
- public class RegisteredInstallKeyOverride : IDisposable
- {
- public string KeyPath { get; }
-
- private readonly RegistryKey parentKey;
- private readonly RegistryKey key;
- private readonly string keyName;
-
- public RegisteredInstallKeyOverride()
- {
- // To test registered installs, we need a registry key which is:
- // - writable without admin access - so that the tests don't require admin to run
- // - redirected in WOW64 - so that there are both 32-bit and 64-bit versions of the key
- // This is because the product stores the info in the 32-bit hive only and even 64-bit
- // product must look into the 32-bit hive.
- // Without the redirection we would not be able to test that the product always looks
- // into 32-bit only.
- // Per this page https://docs.microsoft.com/en-us/windows/desktop/WinProg64/shared-registry-keys
- // a user writable redirected key is for example HKCU\Software\Classes\Interface
- // so we're going to use that one - it's not super clean as the key stores COM interfaces,
- // but we should not corrupt anything by adding a special subkey even if it's left behind.
- //
- // Note: If you want to inspect the values written by the test and/or modify them manually
- // you have to navigate to HKCU\Software\Classes\Wow6432Node\Interface on a 64-bit OS.
- using (RegistryKey hkcu = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32))
- {
- parentKey = hkcu.CreateSubKey(@"Software\Classes\Interface");
- keyName = "_DOTNET_Test" + System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
- key = parentKey.CreateSubKey(keyName);
- KeyPath = key.Name;
- }
- }
-
- public void SetInstallLocation(string installLocation, string architecture)
- {
- using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{architecture}"))
- {
- dotnetLocationKey.SetValue("InstallLocation", installLocation);
- }
- }
-
- public void Dispose()
- {
- parentKey.DeleteSubKeyTree(keyName, throwOnMissingSubKey: false);
- key.Dispose();
- parentKey.Dispose();
- }
- }
-}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.DotNet.Cli.Build.Framework;
+using Microsoft.Win32;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
+{
+ public class RegisteredInstallLocationOverride : IDisposable
+ {
+ public string PathValueOverride { get; }
+
+ // Windows only
+ private readonly RegistryKey parentKey;
+ private readonly RegistryKey key;
+ private readonly string keyName;
+
+ // Linux/macOS only
+
+ public RegisteredInstallLocationOverride()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ // To test registered installs, we need a registry key which is:
+ // - writable without admin access - so that the tests don't require admin to run
+ // - redirected in WOW64 - so that there are both 32-bit and 64-bit versions of the key
+ // This is because the product stores the info in the 32-bit hive only and even 64-bit
+ // product must look into the 32-bit hive.
+ // Without the redirection we would not be able to test that the product always looks
+ // into 32-bit only.
+ // Per this page https://docs.microsoft.com/en-us/windows/desktop/WinProg64/shared-registry-keys
+ // a user writable redirected key is for example HKCU\Software\Classes\Interface
+ // so we're going to use that one - it's not super clean as the key stores COM interfaces,
+ // but we should not corrupt anything by adding a special subkey even if it's left behind.
+ //
+ // Note: If you want to inspect the values written by the test and/or modify them manually
+ // you have to navigate to HKCU\Software\Classes\Wow6432Node\Interface on a 64-bit OS.
+ using (RegistryKey hkcu = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32))
+ {
+ parentKey = hkcu.CreateSubKey(@"Software\Classes\Interface");
+ keyName = "_DOTNET_Test" + Process.GetCurrentProcess().Id.ToString();
+ key = parentKey.CreateSubKey(keyName);
+ PathValueOverride = key.Name;
+ }
+ }
+ else
+ {
+ // On Linux/macOS the install location is registered in a file which is normally
+ // located in /etc/dotnet/install_location
+ // So we need to redirect it to a different place here.
+ string directory = Path.Combine(TestArtifact.TestArtifactsPath, "installLocationOverride");
+ Directory.CreateDirectory(directory);
+ PathValueOverride = Path.Combine(directory, "install_location." + Process.GetCurrentProcess().Id.ToString());
+ File.WriteAllText(PathValueOverride, "");
+ }
+ }
+
+ public void SetInstallLocation(string installLocation, string architecture)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{architecture}"))
+ {
+ dotnetLocationKey.SetValue("InstallLocation", installLocation);
+ }
+ }
+ else
+ {
+ File.WriteAllText(PathValueOverride, installLocation);
+ }
+ }
+
+ public void Dispose()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ parentKey.DeleteSubKeyTree(keyName, throwOnMissingSubKey: false);
+ key.Dispose();
+ parentKey.Dispose();
+ }
+ else
+ {
+ if (File.Exists(PathValueOverride))
+ {
+ File.Delete(PathValueOverride);
+ }
+ }
+ }
+ }
+
+ public static class RegisteredInstallLocationExtensions
+ {
+ public static Command ApplyRegisteredInstallLocationOverride(
+ this Command command,
+ RegisteredInstallLocationOverride registeredInstallLocationOverride)
+ {
+ if (registeredInstallLocationOverride == null)
+ {
+ return command;
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return command.EnvironmentVariable(
+ Constants.TestOnlyEnvironmentVariables.RegistryPath,
+ registeredInstallLocationOverride.PathValueOverride);
+ }
+ else
+ {
+ return command.EnvironmentVariable(
+ Constants.TestOnlyEnvironmentVariables.InstallLocationFilePath,
+ registeredInstallLocationOverride.PathValueOverride);
+ }
+ }
+ }
+}
public Command EnvironmentVariable(string name, string value)
{
- Process.StartInfo.Environment[name] = value;
+ if (value != null)
+ {
+ Process.StartInfo.Environment[name] = value;
+ }
+
return this;
}