Skip to content

Commit

Permalink
Use nethost to find coreclr without starting up the runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
jkoritzinsky committed Dec 28, 2023
2 parents e391dd4 + 902eb68 commit ec44019
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 87 deletions.
2 changes: 1 addition & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Configure the compiler
include(../configure.cmake)
include(ImportManagedComponents.cmake)
include(FindNetHost.cmake)

if (POLICY CMP0135)
cmake_policy(SET CMP0135 NEW) # Set timestamps in downloaded archives to the time of download.
Expand Down
22 changes: 22 additions & 0 deletions test/FindNetHost.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
execute_process(
COMMAND dotnet msbuild FindNetHostDir.proj -t:OutputNetHostDir -nologo
OUTPUT_VARIABLE NET_HOST_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})

string(STRIP ${NET_HOST_DIR} NET_HOST_DIR)

if (WIN32)
add_library(nethost IMPORTED SHARED)
set_target_properties(nethost PROPERTIES
IMPORTED_LOCATION ${NET_HOST_DIR}/nethost.dll
IMPORTED_IMPLIB ${NET_HOST_DIR}/nethost.lib)
else()
add_library(nethost IMPORTED STATIC)
target_compile_definitions(nethost INTERFACE NETHOST_USE_AS_STATIC)
set_target_properties(nethost PROPERTIES
IMPORTED_LOCATION ${NET_HOST_DIR}/libnethost.a)
endif()

target_include_directories(nethost INTERFACE ${NET_HOST_DIR})
8 changes: 8 additions & 0 deletions test/FindNetHostDir.proj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk" DefaultTargets="OutputNetHostDir">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<Target Name="OutputNetHostDir">
<Message Text="$(NetCoreTargetingPackRoot)/Microsoft.NETCore.App.Host.$(NETCoreSdkRuntimeIdentifier)/$(BundledNETCoreAppPackageVersion)/runtimes/$(NETCoreSdkRuntimeIdentifier)/native" Importance="high" />
</Target>
</Project>
33 changes: 0 additions & 33 deletions test/ImportManagedComponents.cmake

This file was deleted.

2 changes: 1 addition & 1 deletion test/regpal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set(SOURCES

add_library(regpal STATIC ${SOURCES})

target_link_libraries(regpal PUBLIC Regression.Locator dncp::dncp)
target_link_libraries(regpal PUBLIC nethost dncp::dncp)

if (NOT WIN32)
target_link_libraries(regpal PUBLIC dncp::winhdrs)
Expand Down
87 changes: 74 additions & 13 deletions test/regpal/pal.cpp
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
#define DNCP_DEFINE_GUID
#include "pal.hpp"

#ifdef BUILD_WINDOWS
#define DNNE_API_OVERRIDE __declspec(dllimport)
#endif
#include <Regression.LocatorNE.h>
#include <nethost.h>
#include <hostfxr.h>

#ifndef BUILD_WINDOWS
#include <dlfcn.h>
#endif

#include <iostream>
#include <fstream>
#include <filesystem>
#include <string_view>

using std::filesystem::path;

#ifdef BUILD_WINDOWS
#define X(str) std::wstring_view{L##str}
#else
#define X(str) std::string_view{str}
#endif

namespace
{
void* LoadModule(char const* path)
void* LoadModule(path path)
{
#ifdef BUILD_WINDOWS
return LoadLibraryA(path);
return LoadLibraryW(path.c_str());
#else
return dlopen(path, RTLD_LAZY);
return dlopen(path.c_str(), RTLD_LAZY);
#endif
}

Expand All @@ -39,24 +46,24 @@ namespace
MetaDataGetDispenser LoadGetDispenser()
{
// TODO: Can we use nethost to discover hostfxr and use hostfxr APIs to discover the baseline?
dncp::cotaskmem_ptr<char> coreClrPath{ (char*)GetCoreClrPath() };
if (coreClrPath == nullptr)
auto coreClrPath = pal::GetCoreClrPath();
if (coreClrPath.empty())
{
std::cerr << "Failed to get coreclr path" << std::endl;
return nullptr;
}

auto mod = LoadModule(coreClrPath.get());
auto mod = LoadModule(coreClrPath);
if (mod == nullptr)
{
std::cerr << "Failed to load metadata baseline module: " << coreClrPath.get() << std::endl;
std::cerr << "Failed to load metadata baseline module: " << coreClrPath << std::endl;
return nullptr;
}

auto getDispenser = (MetaDataGetDispenser)GetSymbol(mod, "MetaDataGetDispenser");
if (getDispenser == nullptr)
{
std::cerr << "Failed to find MetaDataGetDispenser in module: " << coreClrPath.get() << std::endl;
std::cerr << "Failed to find MetaDataGetDispenser in module: " << coreClrPath << std::endl;
return nullptr;
}

Expand All @@ -76,7 +83,7 @@ HRESULT pal::GetBaselineMetadataDispenser(IMetaDataDispenser** dispenser)
return GetDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (void**)dispenser);
}

bool pal::ReadFile(std::filesystem::path path, malloc_span<uint8_t>& b)
bool pal::ReadFile(path path, malloc_span<uint8_t>& b)
{
// Read in the entire file
std::ifstream fd{ path, std::ios::binary | std::ios::in };
Expand All @@ -92,3 +99,57 @@ bool pal::ReadFile(std::filesystem::path path, malloc_span<uint8_t>& b)
fd.read((char*)(uint8_t*)b, b.size());
return true;
}

path pal::GetCoreClrPath()
{
int result = 0;
size_t bufferSize = 4096;
std::unique_ptr<char_t[]> hostfxr_path{nullptr};
do
{
hostfxr_path.reset(new char_t[bufferSize]);
result = get_hostfxr_path(hostfxr_path.get(), &bufferSize, nullptr);
} while (result != 0);

void* hostfxrModule = LoadModule(hostfxr_path.get());
if (hostfxrModule == nullptr)
{
std::cerr << "Failed to load hostfxr module: " << hostfxr_path.get() << std::endl;
return {};
}

path coreClrPath = {};
auto getDotnetEnvironmentInfo = (hostfxr_get_dotnet_environment_info_fn)GetSymbol(hostfxrModule, "hostfxr_get_dotnet_environment_info");
if (getDotnetEnvironmentInfo(
nullptr,
nullptr,
[](const hostfxr_dotnet_environment_info* info, void* result_context)
{
path& coreClrPath = *(path*)result_context;
for (size_t i = 0; i < info->framework_count; ++i)
{
if (info->frameworks[i].name == X("Microsoft.NETCore.App"))
{
coreClrPath = info->frameworks[i].path;
coreClrPath /= info->frameworks[i].version;
#ifdef BUILD_WINDOWS
coreClrPath /= "coreclr.dll";
#elif BUILD_MACOS
coreClrPath /= "libcoreclr.dylib";
#elif BUILD_UNIX
coreClrPath /= "libcoreclr.so";
#else
#error "Unknown platform, cannot determine name for CoreCLR executable"
#endif
}
}
},
&coreClrPath
) != 0)
{
std::cout << "Failed to get dotnet environment info" << std::endl;
return {};
}

return coreClrPath;
}
1 change: 1 addition & 0 deletions test/regpal/pal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace pal
{
std::filesystem::path GetCoreClrPath();
HRESULT GetBaselineMetadataDispenser(IMetaDataDispenser** dispenser);
bool ReadFile(std::filesystem::path path, malloc_span<uint8_t>& b);
}
Expand Down
3 changes: 1 addition & 2 deletions test/regperf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ add_executable(regperf
target_link_libraries(regperf
dnmd::interfaces
dncp::dncp
regpal
Regression.Locator)
regpal)

if(NOT MSVC)
target_link_libraries(regperf dncp::winhdrs)
Expand Down
11 changes: 3 additions & 8 deletions test/regperf/perf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
#include <iostream>
#include <filesystem>

#ifdef BUILD_WINDOWS
#define DNNE_API_OVERRIDE __declspec(dllimport)
#endif
#include <Regression.LocatorNE.h>

#ifdef _MSC_VER
#define EXPORT extern "C" __declspec(dllexport)
#else
Expand Down Expand Up @@ -187,14 +182,14 @@ int main(int argc, char** argv)
{
RETURN_IF_FAILED(pal::GetBaselineMetadataDispenser(&g_baselineDisp));
RETURN_IF_FAILED(GetDispenser(IID_IMetaDataDispenser, reinterpret_cast<void**>(&g_currentDisp)));
dncp::cotaskmem_ptr<char> coreClrPath{ (char*)GetCoreClrPath() };
if (coreClrPath == nullptr)
auto coreClrPath = pal::GetCoreClrPath();
if (coreClrPath.empty())
{
std::cerr << "Failed to get coreclr path" << std::endl;
return -1;
}

std::filesystem::path dataImagePath = coreClrPath.get();
std::filesystem::path dataImagePath = std::move(coreClrPath);
dataImagePath.replace_filename("System.Private.CoreLib.dll");

std::cerr << "Loading System.Private.CoreLib from: " << dataImagePath << std::endl;
Expand Down
17 changes: 13 additions & 4 deletions test/regtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,21 @@ if (WIN32)
target_link_libraries(regtest PRIVATE WIL)
endif()

target_link_libraries(regtest PRIVATE Regression.Locator)
add_custom_target(Regression.TargetAssembly
dotnet build ${CMAKE_CURRENT_SOURCE_DIR}/../Regression.TargetAssembly/Regression.TargetAssembly.ilproj -c $<CONFIG>
BYPRODUCTS ${CMAKE_BINARY_DIR}/managed/bin/Regression.TargetAssembly/$<LOWER_CASE:$<CONFIG>>/Regression.TargetAssembly.dll
COMMENT "Building Regression.TargetAssembly.dll"
)

add_custom_command(TARGET regtest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:dnmd::interfaces> $<TARGET_FILE_DIR:regtest>)
add_dependencies(regtest Regression.TargetAssembly)

add_custom_command(TARGET regtest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${RegressionLocatorDirectory} $<TARGET_FILE_DIR:regtest>)
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:dnmd::interfaces> $<TARGET_FILE_DIR:regtest>
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/managed/bin/Regression.TargetAssembly/$<LOWER_CASE:$<CONFIG>>/Regression.TargetAssembly.dll $<TARGET_FILE_DIR:regtest>)

if(WIN32)
add_custom_command(TARGET regtest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:nethost> $<TARGET_FILE_DIR:regtest>)
endif()

gtest_discover_tests(regtest DISCOVERY_TIMEOUT 600)
13 changes: 6 additions & 7 deletions test/regtest/discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
#ifdef BUILD_WINDOWS
#define DNNE_API_OVERRIDE __declspec(dllimport)
#endif
#include <Regression.LocatorNE.h>

namespace
{
std::string baselinePath;
std::filesystem::path baselinePath;
std::string regressionAssemblyPath;

template<typename T>
Expand Down Expand Up @@ -210,7 +209,7 @@ std::vector<MetadataFile> CoreLibFiles()
std::cout << "Discovering CoreLib files" << std::endl;
std::vector<MetadataFile> scenarios;

scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(baselinePath).parent_path() / "System.Private.CoreLib.dll").generic_string(), "System_Private_CoreLib");
scenarios.emplace_back(MetadataFile::Kind::OnDisk, (baselinePath.parent_path() / "System.Private.CoreLib.dll").generic_string(), "System_Private_CoreLib");

#ifdef BUILD_WINDOWS
scenarios.emplace_back(MetadataFile::Kind::OnDisk, (std::filesystem::path(FindFrameworkInstall("v4.0.30319")) / "mscorlib.dll").generic_string(), "4_0_mscorlib");
Expand Down Expand Up @@ -251,7 +250,7 @@ span<uint8_t> GetMetadataForFile(MetadataFile file)
malloc_span<uint8_t> b;
if (file.kind == MetadataFile::Kind::OnDisk)
{
auto path = std::filesystem::path(baselinePath).parent_path() / file.pathOrKey;
auto path = baselinePath.parent_path() / file.pathOrKey;
b = ReadMetadataFromFile(path);
}
else
Expand Down Expand Up @@ -292,12 +291,12 @@ std::string PrintName(testing::TestParamInfo<MetadataFile> info)

std::string GetBaselineDirectory()
{
return std::filesystem::path(baselinePath).parent_path().string();
return baselinePath.parent_path().string();
}

void SetBaselineModulePath(std::string path)
void SetBaselineModulePath(std::filesystem::path path)
{
baselinePath = path;
baselinePath = std::move(path);
}

void SetRegressionAssemblyPath(std::string path)
Expand Down
3 changes: 2 additions & 1 deletion test/regtest/fixtures.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <internal/dnmd_platform.hpp>

#include <vector>
#include <filesystem>
#include <internal/span.hpp>

struct MetadataFile final
Expand Down Expand Up @@ -44,7 +45,7 @@ std::string FindFrameworkInstall(std::string version);

std::string GetBaselineDirectory();

void SetBaselineModulePath(std::string path);
void SetBaselineModulePath(std::filesystem::path path);

void SetRegressionAssemblyPath(std::string path);

Expand Down
Loading

0 comments on commit ec44019

Please sign in to comment.