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

Add hostfxr_get_dotnet_environment_info API #48097

Merged
merged 12 commits into from
Feb 13, 2021
18 changes: 16 additions & 2 deletions src/installer/corehost/cli/fxr/framework_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info &
return false;
}

return a.version < b.version;
if (a.version < b.version)
{
return true;
}

if (a.version == b.version)
{
return a.hive_depth > b.hive_depth;
}

return false;
}

/*static*/ void framework_info::get_all_framework_infos(
Expand All @@ -30,6 +40,8 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info &
std::vector<pal::string_t> hive_dir;
get_framework_and_sdk_locations(own_dir, &hive_dir);

int32_t hive_depth = 0;

for (pal::string_t dir : hive_dir)
{
auto fx_shared_dir = dir;
Expand Down Expand Up @@ -68,13 +80,15 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info &
{
trace::verbose(_X("Found FX version [%s]"), ver.c_str());

framework_info info(fx_name, fx_dir, parsed);
framework_info info(fx_name, fx_dir, parsed, hive_depth);
framework_infos->push_back(info);
}
}
}
}
}

hive_depth++;
}

std::sort(framework_infos->begin(), framework_infos->end(), compare_by_name_and_version);
Expand Down
6 changes: 4 additions & 2 deletions src/installer/corehost/cli/fxr/framework_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@

struct framework_info
{
framework_info(pal::string_t name, pal::string_t path, fx_ver_t version)
framework_info(pal::string_t name, pal::string_t path, fx_ver_t version, int32_t hive_depth)
: name(name)
, path(path)
, version(version) { }
, version(version)
, hive_depth(hive_depth) { }

static void get_all_framework_infos(
const pal::string_t& own_dir,
Expand All @@ -24,6 +25,7 @@ struct framework_info
pal::string_t name;
pal::string_t path;
fx_ver_t version;
int32_t hive_depth;
};

#endif // __FRAMEWORK_INFO_H_
1 change: 1 addition & 0 deletions src/installer/corehost/cli/fxr/fx_ver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ fx_ver_t::fx_ver_t(int major, int minor, int patch, const pal::string_t& pre, co
assert(is_empty() || m_patch >= 0);
assert(m_pre[0] == 0 || validIdentifiers(m_pre));
assert(m_build[0] == 0 || validIdentifiers(m_build));
version_as_str = this->as_str();
mateoatr marked this conversation as resolved.
Show resolved Hide resolved
}

fx_ver_t::fx_ver_t(int major, int minor, int patch, const pal::string_t& pre)
Expand Down
2 changes: 2 additions & 0 deletions src/installer/corehost/cli/fxr/fx_ver.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ struct fx_ver_t

static bool parse(const pal::string_t& ver, fx_ver_t* fx_ver, bool parse_only_production = false);

pal::string_t version_as_str;

private:
int m_major;
int m_minor;
Expand Down
120 changes: 120 additions & 0 deletions src/installer/corehost/cli/fxr/hostfxr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "hostfxr.h"
#include "host_context.h"
#include "bundle/info.h"
#include <framework_info.h>

namespace
{
Expand Down Expand Up @@ -330,6 +331,125 @@ SHARED_API int32_t HOSTFXR_CALLTYPE hostfxr_get_available_sdks(
return StatusCode::Success;
}

typedef void (HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)(
const struct hostfxr_dotnet_environment_info* info,
void* result_context);

//
// Returns available SDKs and frameworks.
//
// Resolves the existing SDKs and frameworks from a dotnet root directory (if
// any), or the global default location. If multi-level lookup is enabled and
// the dotnet root location is different than the global location, the SDKs and
// frameworks will be enumerated from both locations.
//
// The SDKs are sorted in ascending order by version, multi-level lookup
// locations are put before private ones.
//
// The frameworks are sorted in ascending order by name followed by version,
// multi-level lookup locations are put before private ones.
//
// Parameters:
// dotnet_root
// The path to a directory containing a dotnet executable.
//
// reserved
// Reserved for future parameters.
//
// result
// Callback invoke to return the list of SDKs and frameworks.
// Structs and their elements are valid for the duration of the call.
//
// result_context
// Additional context passed to the result callback.
//
// Return value:
// 0 on success, otherwise failure.
//
// String encoding:
// Windows - UTF-16 (pal::char_t is 2 byte wchar_t)
// Unix - UTF-8 (pal::char_t is 1 byte char)
//
SHARED_API int32_t HOSTFXR_CALLTYPE hostfxr_get_dotnet_environment_info(
const pal::char_t* dotnet_root,
void* reserved,
hostfxr_get_dotnet_environment_info_result_fn result,
void* result_context)
{
assert(reserved == nullptr);
mateoatr marked this conversation as resolved.
Show resolved Hide resolved
pal::string_t dotnet_dir;
if (dotnet_root == nullptr)
{
if (pal::get_dotnet_self_registered_dir(&dotnet_dir) || pal::get_default_installation_dir(&dotnet_dir))
{
trace::info(_X("Using global installation location [%s]."), dotnet_dir.c_str());
}
else
{
trace::info(_X("No default dotnet installation could be obtained."));
}
}
else
{
dotnet_dir = dotnet_root;
}

std::vector<sdk_info> sdk_infos;
sdk_info::get_all_sdk_infos(dotnet_dir, &sdk_infos);

std::vector<hostfxr_dotnet_environment_sdk_info> environment_sdk_infos;
if (!sdk_infos.empty())
{
environment_sdk_infos.reserve(sdk_infos.size());
for (const sdk_info& info : sdk_infos)
{
hostfxr_dotnet_environment_sdk_info sdk
{
sizeof(hostfxr_dotnet_environment_sdk_info),
info.version.version_as_str.c_str(),
info.full_path.c_str()
};

environment_sdk_infos.push_back(sdk);
}
}

std::vector<framework_info> framework_infos;
framework_info::get_all_framework_infos(dotnet_dir, _X(""), &framework_infos);

std::vector<hostfxr_dotnet_environment_framework_info> environment_framework_infos;
if (!framework_infos.empty())
{
environment_framework_infos.reserve(framework_infos.size());
for (const framework_info& info : framework_infos)
{
hostfxr_dotnet_environment_framework_info fw
{
sizeof(hostfxr_dotnet_environment_framework_info),
info.name.c_str(),
info.version.version_as_str.c_str(),
info.path.c_str()
};

environment_framework_infos.push_back(fw);
}
}

const hostfxr_dotnet_environment_info environment_info
{
sizeof(hostfxr_dotnet_environment_info),
_STRINGIFY(HOST_FXR_PKG_VER),
_STRINGIFY(REPO_COMMIT_HASH),
environment_sdk_infos.size(),
(environment_sdk_infos.empty()) ? nullptr : &environment_sdk_infos[0],
environment_framework_infos.size(),
(environment_framework_infos.empty()) ? nullptr : &environment_framework_infos[0]
};

result(&environment_info, result_context);
return StatusCode::Success;
}

//
// Returns the native directories of the runtime based upon
// the specified app.
Expand Down
29 changes: 29 additions & 0 deletions src/installer/corehost/cli/hostfxr.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,33 @@ typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_delegate_fn)(
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_close_fn)(const hostfxr_handle host_context_handle);

struct hostfxr_dotnet_environment_sdk_info
{
size_t size;
const char_t* version;
const char_t* path;
};

struct hostfxr_dotnet_environment_framework_info
{
size_t size;
const char_t* name;
const char_t* version;
const char_t* path;
};

struct hostfxr_dotnet_environment_info
{
size_t size;

const char_t* hostfxr_version;
const char_t* hostfxr_commit_hash;

int32_t sdk_count;
const struct hostfxr_dotnet_environment_sdk_info* sdks;
mateoatr marked this conversation as resolved.
Show resolved Hide resolved

int32_t framework_count;
const hostfxr_dotnet_environment_framework_info* frameworks;
};

#endif //__HOSTFXR_H__
106 changes: 106 additions & 0 deletions src/installer/tests/Assets/TestProjects/HostApiInvokerApp/HostFXR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace HostApiInvokerApp
Expand All @@ -23,6 +24,45 @@ internal enum hostfxr_resolve_sdk2_result_key_t : int
global_json_path = 1,
}

[StructLayout(LayoutKind.Sequential)]
internal struct hostfxr_dotnet_environment_sdk_info
{
internal int size;
[MarshalAs(UnmanagedType.LPWStr)]
mateoatr marked this conversation as resolved.
Show resolved Hide resolved
internal string version;
[MarshalAs(UnmanagedType.LPWStr)]
internal string path;
}

[StructLayout(LayoutKind.Sequential)]
internal struct hostfxr_dotnet_environment_framework_info
{
internal int size;
[MarshalAs(UnmanagedType.LPWStr)]
internal string name;
[MarshalAs(UnmanagedType.LPWStr)]
internal string version;
[MarshalAs(UnmanagedType.LPWStr)]
internal string path;
}

[StructLayout(LayoutKind.Sequential)]
internal struct hostfxr_dotnet_environment_info
{
internal int size;

[MarshalAs(UnmanagedType.LPWStr)]
internal string hostfxr_version;
[MarshalAs(UnmanagedType.LPWStr)]
internal string hostfxr_commit_hash;

internal int sdk_count;
internal IntPtr sdks;

internal int framework_count;
internal IntPtr frameworks;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = Utils.OSCharSet)]
internal delegate void hostfxr_resolve_sdk2_result_fn(
hostfxr_resolve_sdk2_result_key_t key,
Expand Down Expand Up @@ -53,6 +93,18 @@ internal delegate void hostfxr_error_writer_fn(
[DllImport(nameof(hostfxr), CharSet = Utils.OSCharSet, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr hostfxr_set_error_writer(
hostfxr_error_writer_fn error_writer);

[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = Utils.OSCharSet)]
internal delegate void hostfxr_get_dotnet_environment_info_result_fn(
hostfxr_dotnet_environment_info info,
IntPtr result_context);

[DllImport(nameof(hostfxr), CharSet = Utils.OSCharSet, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern int hostfxr_get_dotnet_environment_info(
string? dotnet_root,
IntPtr reserved,
hostfxr_get_dotnet_environment_info_result_fn result,
IntPtr result_context);
}

/// <summary>
Expand Down Expand Up @@ -142,6 +194,57 @@ static void Test_hostfxr_set_error_writer(string[] args)
}
}

/// <summary>
/// Test that invokes native api hostfxr_get_dotnet_environment_info.
/// </summary>
/// <param name="args[0]">hostfxr_get_dotnet_environment_info</param>
/// <param name="args[1]">(Optional) Path to the directory with dotnet.exe</param>
static void Test_hostfxr_get_dotnet_environment_info(string[] args)
{
string? dotnetExeDir = null;
if (args.Length >= 2)
dotnetExeDir = args[1];

string hostfxr_version;
string hostfxr_commit_hash;
List<hostfxr.hostfxr_dotnet_environment_sdk_info> sdks = new List<hostfxr.hostfxr_dotnet_environment_sdk_info>();
List<hostfxr.hostfxr_dotnet_environment_framework_info> frameworks = new List<hostfxr.hostfxr_dotnet_environment_framework_info>();

int rc = hostfxr.hostfxr_get_dotnet_environment_info(
dotnet_root: dotnetExeDir,
reserved: IntPtr.Zero,
result: (info, result_context) => {
hostfxr_version = info.hostfxr_version;
hostfxr_commit_hash = info.hostfxr_commit_hash;
for (int i = 0; i < info.sdk_count; i++)
{
IntPtr pSdkInfo = new IntPtr(info.sdks.ToInt64() + (i * Marshal.SizeOf<hostfxr.hostfxr_dotnet_environment_sdk_info>()));
mateoatr marked this conversation as resolved.
Show resolved Hide resolved
sdks.Add(Marshal.PtrToStructure<hostfxr.hostfxr_dotnet_environment_sdk_info>(pSdkInfo));
}

for (int i = 0; i < info.framework_count; i++)
{
IntPtr pFrameworkInfo = new IntPtr(info.frameworks.ToInt64() + (i * Marshal.SizeOf<hostfxr.hostfxr_dotnet_environment_framework_info>()));
frameworks.Add(Marshal.PtrToStructure<hostfxr.hostfxr_dotnet_environment_framework_info>(pFrameworkInfo));
}
},
result_context: IntPtr.Zero);
mateoatr marked this conversation as resolved.
Show resolved Hide resolved

if (rc != 0)
{
Console.WriteLine($"hostfxr_get_dotnet_environment_info:Fail[{rc}]");
}

Console.WriteLine($"hostfxr_get_dotnet_environment_info sdk versions:[{string.Join(";", sdks.Select(s => s.version).ToList())}]");
Console.WriteLine($"hostfxr_get_dotnet_environment_info sdk paths:[{string.Join(";", sdks.Select(s => s.path).ToList())}]");

Console.WriteLine($"hostfxr_get_dotnet_environment_info framework names:[{string.Join(";", frameworks.Select(f => f.name).ToList())}]");
Console.WriteLine($"hostfxr_get_dotnet_environment_info framework versions:[{string.Join(";", frameworks.Select(f => f.version).ToList())}]");
Console.WriteLine($"hostfxr_get_dotnet_environment_info framework paths:[{string.Join(";", frameworks.Select(f => f.path).ToList())}]");

Console.WriteLine("hostfxr_get_dotnet_environment_info:Success");
}

public static bool RunTest(string apiToTest, string[] args)
{
switch (apiToTest)
Expand All @@ -155,6 +258,9 @@ public static bool RunTest(string apiToTest, string[] args)
case nameof(Test_hostfxr_set_error_writer):
Test_hostfxr_set_error_writer(args);
break;
case nameof(hostfxr.hostfxr_get_dotnet_environment_info):
Test_hostfxr_get_dotnet_environment_info(args);
break;
default:
return false;
}
Expand Down
Loading