Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 13 additions & 5 deletions src/installer/tests/AppHost.Bundle.Tests/AppLaunch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@ public AppLaunch(AppLaunch.SharedTestState fixture)

private void RunTheApp(string path, bool selfContained)
{
Command.Create(path)
.CaptureStdErr()
.CaptureStdOut()
var result = Command.Create(path)
.EnableTracingAndCaptureOutputs()
.DotNetRoot(selfContained ? null : TestContext.BuiltDotNet.BinPath)
.Execute()
.Should().Pass()
.Execute();
result.Should().Pass()
.And.HaveStdOutContaining("Hello World!");

if (selfContained)
{
result.Should().NotHaveProperty(Constants.RuntimeProperty.DotnetHostPath);
}
else
{
result.Should().HaveProperty(Constants.RuntimeProperty.DotnetHostPath, TestContext.BuiltDotNet.DotnetExecutablePath);
}
}

private string MakeUniversalBinary(string path, Architecture architecture)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Runtime.InteropServices;
using Xunit;
using HostActivation.Tests;

namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHosting
{
Expand Down Expand Up @@ -37,6 +38,7 @@ public void RunApp()
.Execute()
.Should().Pass()
.And.InitializeContextForApp(app.AppDll)
.And.HaveProperty(Constants.RuntimeProperty.DotnetHostPath, TestContext.BuiltDotNet.DotnetExecutablePath)
.And.ExecuteApplication(sharedState.NativeHostPath, app.AppDll);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Linq;

using HostActivation.Tests;
using Microsoft.DotNet.Cli.Build.Framework;
using Xunit;

Expand Down Expand Up @@ -40,7 +41,8 @@ public void CallDelegateOnApplicationContext(bool validType, bool validMethod)
.Execute();

result.Should()
.InitializeContextForApp(app.AppDll);
.InitializeContextForApp(app.AppDll)
.And.HaveProperty(Constants.RuntimeProperty.DotnetHostPath, TestContext.BuiltDotNet.DotnetExecutablePath);

if (validType && validMethod)
{
Expand Down Expand Up @@ -73,7 +75,8 @@ public void CallDelegateOnComponentContext(bool validType, bool validMethod)
.Execute();

result.Should()
.InitializeContextForConfig(component.RuntimeConfigJson);
.InitializeContextForConfig(component.RuntimeConfigJson)
.And.HaveProperty(Constants.RuntimeProperty.DotnetHostPath, TestContext.BuiltDotNet.DotnetExecutablePath);

// This should fail even with the valid type and valid method,
// because the type is not resolvable from the default AssemblyLoadContext.
Expand All @@ -96,6 +99,7 @@ public void CallDelegateOnSelfContainedApplicationContext()
.Execute();

result.Should().Pass()
.And.NotHaveProperty(Constants.RuntimeProperty.DotnetHostPath)
.And.InitializeContextForApp(app.AppDll)
.And.ExecuteFunctionPointer(sharedState.FunctionPointerEntryPoint1, 1, 1)
.And.ExecuteInDefaultContext(app.AssemblyName);
Expand Down
59 changes: 54 additions & 5 deletions src/installer/tests/HostActivation.Tests/RuntimeProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.IO;
using Microsoft.DotNet.Cli.Build;
using Microsoft.DotNet.Cli.Build.Framework;
using Microsoft.DotNet.CoreSetup.Test;
using Microsoft.DotNet.TestUtils;
using Xunit;
Expand All @@ -28,7 +29,7 @@ public void AppConfigProperty_AppCanGetData()
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining($"Property {SharedTestState.AppTestPropertyName} = {SharedTestState.AppTestPropertyValue}")
.And.HaveProperty(SharedTestState.AppTestPropertyName, SharedTestState.AppTestPropertyValue)
.And.HaveStdOutContaining($"AppContext.GetData({SharedTestState.AppTestPropertyName}) = {SharedTestState.AppTestPropertyValue}");
}

Expand All @@ -39,7 +40,7 @@ public void FrameworkConfigProperty_AppCanGetData()
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining($"Property {SharedTestState.FrameworkTestPropertyName} = {SharedTestState.FrameworkTestPropertyValue}")
.And.HaveProperty(SharedTestState.FrameworkTestPropertyName, SharedTestState.FrameworkTestPropertyValue)
.And.HaveStdOutContaining($"AppContext.GetData({SharedTestState.FrameworkTestPropertyName}) = {SharedTestState.FrameworkTestPropertyValue}");
}

Expand All @@ -55,7 +56,7 @@ public void DuplicateConfigProperty_AppConfigValueUsed()
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining($"Property {SharedTestState.FrameworkTestPropertyName} = {SharedTestState.AppTestPropertyValue}")
.And.HaveProperty(SharedTestState.FrameworkTestPropertyName, SharedTestState.AppTestPropertyValue)
.And.HaveStdOutContaining($"AppContext.GetData({SharedTestState.FrameworkTestPropertyName}) = {SharedTestState.AppTestPropertyValue}");
}

Expand All @@ -67,7 +68,7 @@ public void HostFxrPathProperty_SetWhenRunningSDKCommand()
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining($"Property {SharedTestState.HostFxrPathPropertyName} = {dotnet.GreatestVersionHostFxrFilePath}");
.And.HaveProperty(SharedTestState.HostFxrPathPropertyName, dotnet.GreatestVersionHostFxrFilePath);
}

[Fact]
Expand All @@ -77,9 +78,57 @@ public void HostFxrPathProperty_NotVisibleFromApp()
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.NotHaveProperty(SharedTestState.HostFxrPathPropertyName)
.And.HaveStdOutContaining($"Property '{SharedTestState.HostFxrPathPropertyName}' was not found.");
}

[Fact]
public void DotNetHostPathProperty_FrameworkDependentApp()
{
// DOTNET_HOST_PATH property should point to the dotnet executable in the dotnet root directory
sharedState.DotNet.Exec(sharedState.App.AppDll, PrintProperties, Constants.RuntimeProperty.DotnetHostPath)
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveProperty(Constants.RuntimeProperty.DotnetHostPath, sharedState.DotNet.DotnetExecutablePath)
.And.HaveStdOutContaining($"AppContext.GetData({Constants.RuntimeProperty.DotnetHostPath}) = {sharedState.DotNet.DotnetExecutablePath}");

Command.Create(sharedState.App.AppExe, PrintProperties, Constants.RuntimeProperty.DotnetHostPath)
.EnableTracingAndCaptureOutputs()
.DotNetRoot(sharedState.DotNet.BinPath)
.Execute()
.Should().Pass()
.And.HaveProperty(Constants.RuntimeProperty.DotnetHostPath, sharedState.DotNet.DotnetExecutablePath)
.And.HaveStdOutContaining($"AppContext.GetData({Constants.RuntimeProperty.DotnetHostPath}) = {sharedState.DotNet.DotnetExecutablePath}");
}

[Fact]
public void DotNetHostPathProperty_SDKCommand()
{
// DOTNET_HOST_PATH property should point to the dotnet executable used to run the command
var dotnet = sharedState.MockSDK;
dotnet.Exec("--info")
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveProperty(Constants.RuntimeProperty.DotnetHostPath, dotnet.DotnetExecutablePath);
}

[Fact]
public void DotNetHostPathProperty_SDKCommand_LocalInstall()
{
using TestArtifact workingDirectory = TestArtifact.Create("dotnetHostPath");
GlobalJson.Write(workingDirectory.Location, new GlobalJson.Sdk() { Paths = [ sharedState.MockSDK.BinPath ] });

// DOTNET_HOST_PATH should point to the dotnet executable in the local SDK install used to run the command
sharedState.DotNet.Exec("--info")
.EnableTracingAndCaptureOutputs()
.WorkingDirectory(workingDirectory.Location)
.Execute()
.Should().Pass()
.And.HaveProperty(Constants.RuntimeProperty.DotnetHostPath, sharedState.MockSDK.DotnetExecutablePath);
}

[Fact]
public void DuplicateCommonProperty_Fails()
{
Expand Down Expand Up @@ -110,7 +159,7 @@ public void SpecifiedInConfigAndDevConfig_ConfigWins()
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining($"Property {SharedTestState.AppTestPropertyName} = {SharedTestState.AppTestPropertyValue}")
.And.HaveProperty(SharedTestState.AppTestPropertyName, SharedTestState.AppTestPropertyValue)
.And.HaveStdOutContaining($"AppContext.GetData({SharedTestState.AppTestPropertyName}) = {SharedTestState.AppTestPropertyValue}");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public void Default()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion)
.And.NotHaveProperty(Constants.RuntimeProperty.DotnetHostPath);

if (OperatingSystem.IsWindows())
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using FluentAssertions;

namespace Microsoft.DotNet.CoreSetup.Test
{
public static partial class CommandResultExtensions
{
public static AndConstraint<CommandResultAssertions> HaveProperty(this CommandResultAssertions assertion, string name, string value)
{
// Expected trace message from hostpolicy
return assertion.HaveStdErrContaining($"Property {name} = {value}");
}

public static AndConstraint<CommandResultAssertions> NotHaveProperty(this CommandResultAssertions assertion, string name)
{
return assertion.NotHaveStdErrContaining($"Property {name} =");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Microsoft.DotNet.CoreSetup.Test
{
public static class CommandResultExtensions
public static partial class CommandResultExtensions
{
public static CommandResultAssertions Should(this CommandResult commandResult)
{
Expand Down
5 changes: 5 additions & 0 deletions src/installer/tests/TestUtils/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,10 @@ public static class ErrorCode
public const int COMPlusException = unchecked((int)0xe0434352);
public const int SIGABRT = 134;
}

public static class RuntimeProperty
{
public const string DotnetHostPath = "DOTNET_HOST_PATH";
}
}
}
7 changes: 3 additions & 4 deletions src/native/corehost/hostmisc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,15 @@
#if defined(TARGET_WINDOWS)
#define LIB_PREFIX ""
#define LIB_FILE_EXT ".dll"
#define EXE_FILE_EXT ".exe"
#elif defined(TARGET_OSX)
#define LIB_PREFIX "lib"
#define LIB_FILE_EXT ".dylib"
#define EXE_FILE_EXT ""
#else
#define LIB_PREFIX "lib"
#define LIB_FILE_EXT ".so"
#define EXE_FILE_EXT ""
#endif

#define _STRINGIFY(s) _X(s)
Expand Down Expand Up @@ -139,8 +142,6 @@ namespace pal
CRITICAL_SECTION _impl;
};

inline const pal::char_t* exe_suffix() { return _X(".exe"); }

inline int cstrcasecmp(const char* str1, const char* str2) { return ::_stricmp(str1, str2); }
inline int strcmp(const char_t* str1, const char_t* str2) { return ::wcscmp(str1, str2); }
inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::_wcsicmp(str1, str2); }
Expand Down Expand Up @@ -213,8 +214,6 @@ namespace pal
typedef void* proc_t;
typedef std::mutex mutex_t;

inline const pal::char_t* exe_suffix() { return nullptr; }

inline int cstrcasecmp(const char* str1, const char* str2) { return ::strcasecmp(str1, str2); }
inline int strcmp(const char_t* str1, const char_t* str2) { return ::strcmp(str1, str2); }
inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::strcasecmp(str1, str2); }
Expand Down
8 changes: 2 additions & 6 deletions src/native/corehost/hostmisc/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,11 @@ void append_path(pal::string_t* path1, const pal::char_t* path2)

pal::string_t strip_executable_ext(const pal::string_t& filename)
{
const pal::char_t* exe_suffix = pal::exe_suffix();
if (exe_suffix == nullptr)
return filename;

size_t suffix_len = pal::strlen(exe_suffix);
size_t suffix_len = STRING_LENGTH(EXE_FILE_EXT);
if (suffix_len == 0)
return filename;

if (utils::ends_with(filename, exe_suffix, suffix_len, false))
if (utils::ends_with(filename, _STRINGIFY(EXE_FILE_EXT), false))
{
// We need to strip off the old extension
pal::string_t result(filename);
Expand Down
1 change: 1 addition & 0 deletions src/native/corehost/hostpolicy/coreclr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ namespace
_X("STARTUP_HOOKS"),
_X("APP_PATHS"),
_X("RUNTIME_IDENTIFIER"),
_X("DOTNET_HOST_PATH"),
};

static_assert((sizeof(PropertyNameMapping) / sizeof(*PropertyNameMapping)) == static_cast<size_t>(common_property::Last), "Invalid property count");
Expand Down
1 change: 1 addition & 0 deletions src/native/corehost/hostpolicy/coreclr.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ enum class common_property
StartUpHooks,
AppPaths,
RuntimeIdentifier,
DotNetHostPath,
// Sentinel value - new values should be defined above
Last
};
Expand Down
16 changes: 16 additions & 0 deletions src/native/corehost/hostpolicy/hostpolicy_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,22 @@ int hostpolicy_context_t::initialize(const hostpolicy_init_t &hostpolicy_init, c
coreclr_properties.add(common_property::StartUpHooks, startup_hooks.c_str());
}

// Path to dotnet corresponding to the runtime used to run the application (if it exists)
// Only for framework-dependent applications, as there is no corresponding dotnet for self-contained.
#if !defined(NATIVE_LIBS_EMBEDDED)
if (hostpolicy_init.is_framework_dependent)
{
pal::string_t dotnet_host_path = hostpolicy_init.host_info.dotnet_root;
append_path(&dotnet_host_path, _X("dotnet"));
dotnet_host_path.append(_STRINGIFY(EXE_FILE_EXT));
if (!coreclr_properties.add(common_property::DotNetHostPath, dotnet_host_path.c_str()))
{
log_duplicate_property_error(coreclr_property_bag_t::common_property_to_string(common_property::DotNetHostPath));
return StatusCode::LibHostDuplicateProperty;
}
}
#endif

{
host_contract = { sizeof(host_runtime_contract), this };
if (bundle::info_t::is_single_file_bundle())
Expand Down
Loading