Redesign corehost per spec
This commit is contained in:
parent
12351cd37b
commit
504e109947
16 changed files with 1159 additions and 526 deletions
|
@ -10,14 +10,16 @@ include_directories(inc)
|
|||
set(SOURCES
|
||||
src/args.cpp
|
||||
src/main.cpp
|
||||
src/tpafile.cpp
|
||||
src/deps_resolver.cpp
|
||||
src/trace.cpp
|
||||
src/utils.cpp
|
||||
src/coreclr.cpp
|
||||
src/servicing_index.cpp
|
||||
|
||||
inc/args.h
|
||||
inc/pal.h
|
||||
inc/tpafile.h
|
||||
inc/servicing_index.h
|
||||
inc/deps_resolver.h
|
||||
inc/trace.h
|
||||
inc/coreclr.h
|
||||
inc/utils.h)
|
||||
|
|
|
@ -4,14 +4,20 @@
|
|||
#ifndef ARGS_H
|
||||
#define ARGS_H
|
||||
|
||||
#include "utils.h"
|
||||
#include "pal.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct arguments_t
|
||||
{
|
||||
pal::string_t own_path;
|
||||
pal::string_t app_dir;
|
||||
pal::string_t dotnet_servicing;
|
||||
pal::string_t dotnet_runtime_servicing;
|
||||
pal::string_t dotnet_home;
|
||||
pal::string_t dotnet_packages;
|
||||
pal::string_t dotnet_packages_cache;
|
||||
pal::string_t managed_application;
|
||||
pal::string_t clr_path;
|
||||
|
||||
int app_argc;
|
||||
const pal::char_t** app_argv;
|
||||
|
|
102
src/corehost/inc/deps_resolver.h
Normal file
102
src/corehost/inc/deps_resolver.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef DEPS_RESOLVER_H
|
||||
#define DEPS_RESOLVER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "pal.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "servicing_index.h"
|
||||
|
||||
struct deps_entry_t
|
||||
{
|
||||
pal::string_t library_type;
|
||||
pal::string_t library_name;
|
||||
pal::string_t library_version;
|
||||
pal::string_t library_hash;
|
||||
pal::string_t asset_type;
|
||||
pal::string_t asset_name;
|
||||
pal::string_t relative_path;
|
||||
bool is_serviceable;
|
||||
|
||||
// Given a "base" dir, yield the relative path in the package layout.
|
||||
bool to_full_path(const pal::string_t& root, pal::string_t* str) const;
|
||||
|
||||
// Given a "base" dir, yield the relative path in the package layout only if
|
||||
// the hash matches contents of the hash file.
|
||||
bool to_hash_matched_path(const pal::string_t& root, pal::string_t* str) const;
|
||||
};
|
||||
|
||||
// Probe paths to be resolved for ordering
|
||||
struct probe_paths_t
|
||||
{
|
||||
pal::string_t tpa;
|
||||
pal::string_t native;
|
||||
pal::string_t culture;
|
||||
};
|
||||
|
||||
class deps_resolver_t
|
||||
{
|
||||
public:
|
||||
deps_resolver_t(const arguments_t& args)
|
||||
: m_svc(args.dotnet_servicing)
|
||||
{
|
||||
m_deps_valid = parse_deps_file(args);
|
||||
}
|
||||
|
||||
bool valid() { return m_deps_valid; }
|
||||
|
||||
bool resolve_probe_paths(
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
probe_paths_t* probe_paths);
|
||||
|
||||
private:
|
||||
|
||||
bool load();
|
||||
|
||||
bool parse_deps_file(const arguments_t& args);
|
||||
|
||||
// Resolve order for TPA lookup.
|
||||
void resolve_tpa_list(
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output);
|
||||
|
||||
// Resolve order for culture and native DLL lookup.
|
||||
void resolve_probe_dirs(
|
||||
const pal::string_t& asset_type,
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output);
|
||||
|
||||
// Populate local assemblies from app_dir listing.
|
||||
void get_local_assemblies(const pal::string_t& dir);
|
||||
|
||||
// Servicing index to resolve serviced assembly paths.
|
||||
servicing_index_t m_svc;
|
||||
|
||||
// Map of simple name -> full path of local assemblies populated in priority
|
||||
// order of their extensions.
|
||||
std::unordered_map<pal::string_t, pal::string_t> m_local_assemblies;
|
||||
|
||||
// Entries in the dep file
|
||||
std::vector<deps_entry_t> m_deps_entries;
|
||||
|
||||
// The dep file path
|
||||
pal::string_t m_deps_path;
|
||||
|
||||
// Is the deps file valid
|
||||
bool m_deps_valid;
|
||||
};
|
||||
|
||||
#endif // DEPS_RESOLVER_H
|
|
@ -12,6 +12,9 @@
|
|||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
|
@ -65,7 +68,13 @@ namespace pal
|
|||
typedef wchar_t char_t;
|
||||
typedef std::wstring string_t;
|
||||
typedef std::wstringstream stringstream_t;
|
||||
typedef std::ifstream ifstream_t;
|
||||
// TODO: Agree on the correct encoding of the files: The PoR for now is to
|
||||
// temporarily wchar for Windows and char for Unix. Current implementation
|
||||
// implicitly expects the contents on both Windows and Unix as char and
|
||||
// converts them to wchar in code for Windows. This line should become:
|
||||
// typedef std::basic_ifstream<pal::char_t> ifstream_t.
|
||||
typedef std::basic_ifstream<char> ifstream_t;
|
||||
typedef std::istreambuf_iterator<ifstream_t::char_type> istreambuf_iterator_t;
|
||||
typedef HRESULT hresult_t;
|
||||
typedef HMODULE dll_t;
|
||||
typedef FARPROC proc_t;
|
||||
|
@ -77,11 +86,14 @@ namespace pal
|
|||
|
||||
pal::string_t to_palstring(const std::string& str);
|
||||
std::string to_stdstring(const pal::string_t& str);
|
||||
void to_palstring(const char* str, pal::string_t* out);
|
||||
void to_stdstring(const pal::char_t* str, std::string* out);
|
||||
#else
|
||||
typedef char char_t;
|
||||
typedef std::string string_t;
|
||||
typedef std::stringstream stringstream_t;
|
||||
typedef std::ifstream ifstream_t;
|
||||
typedef std::basic_ifstream<char> ifstream_t;
|
||||
typedef std::istreambuf_iterator<ifstream_t::char_type> istreambuf_iterator_t;
|
||||
typedef int hresult_t;
|
||||
typedef void* dll_t;
|
||||
typedef void* proc_t;
|
||||
|
@ -92,24 +104,26 @@ namespace pal
|
|||
inline void err_vprintf(const char_t* format, va_list vl) { ::vfprintf(stderr, format, vl); ::fputc('\n', stderr); }
|
||||
inline pal::string_t to_palstring(const std::string& str) { return str; }
|
||||
inline std::string to_stdstring(const pal::string_t& str) { return str; }
|
||||
inline void to_palstring(const char* str, pal::string_t* out) { out->assign(str); }
|
||||
inline void to_stdstring(const pal::char_t* str, std::string* out) { out->assign(str); }
|
||||
#endif
|
||||
|
||||
bool realpath(string_t& path);
|
||||
bool realpath(string_t* path);
|
||||
bool file_exists(const string_t& path);
|
||||
std::vector<pal::string_t> readdir(const string_t& path);
|
||||
inline bool directory_exists(const string_t& path) { return file_exists(path); }
|
||||
void readdir(const string_t& path, std::vector<pal::string_t>* list);
|
||||
|
||||
bool get_own_executable_path(string_t& recv);
|
||||
bool getenv(const char_t* name, string_t& recv);
|
||||
bool get_default_packages_directory(string_t& recv);
|
||||
bool get_own_executable_path(string_t* recv);
|
||||
bool getenv(const char_t* name, string_t* recv);
|
||||
bool get_default_packages_directory(string_t* recv);
|
||||
bool is_path_rooted(const string_t& path);
|
||||
|
||||
int xtoi(const char_t* input);
|
||||
|
||||
bool load_library(const char_t* path, dll_t& dll);
|
||||
bool load_library(const char_t* path, dll_t* dll);
|
||||
proc_t get_symbol(dll_t library, const char* name);
|
||||
void unload_library(dll_t library);
|
||||
|
||||
bool find_coreclr(pal::string_t& recv);
|
||||
bool find_coreclr(pal::string_t* recv);
|
||||
}
|
||||
|
||||
#endif // PAL_H
|
||||
|
|
24
src/corehost/inc/servicing_index.h
Normal file
24
src/corehost/inc/servicing_index.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "utils.h"
|
||||
#include "args.h"
|
||||
|
||||
class servicing_index_t
|
||||
{
|
||||
public:
|
||||
servicing_index_t(const pal::string_t& svc_dir);
|
||||
|
||||
bool find_redirection(const pal::string_t& package_name,
|
||||
const pal::string_t& package_version,
|
||||
const pal::string_t& package_relative,
|
||||
pal::string_t* redirection);
|
||||
|
||||
private:
|
||||
void ensure_redirections();
|
||||
|
||||
std::unordered_map<pal::string_t, pal::string_t> m_redirections;
|
||||
pal::string_t m_patch_root;
|
||||
pal::string_t m_index_file;
|
||||
bool m_parsed;
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef TPAFILE_H
|
||||
#define TPAFILE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "pal.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct tpaentry_t
|
||||
{
|
||||
pal::string_t library_type;
|
||||
pal::string_t library_name;
|
||||
pal::string_t library_version;
|
||||
pal::string_t library_hash;
|
||||
pal::string_t asset_type;
|
||||
pal::string_t asset_name;
|
||||
pal::string_t relative_path;
|
||||
};
|
||||
|
||||
class tpafile
|
||||
{
|
||||
public:
|
||||
bool load(pal::string_t path);
|
||||
|
||||
void add_from_local_dir(const pal::string_t& dir);
|
||||
void add_package_dir(pal::string_t dir);
|
||||
void add_native_search_path(pal::string_t dir);
|
||||
|
||||
void write_tpa_list(pal::string_t& output);
|
||||
void write_native_paths(pal::string_t& output);
|
||||
|
||||
private:
|
||||
std::vector<tpaentry_t> m_entries;
|
||||
std::vector<pal::string_t> m_native_search_paths;
|
||||
std::vector<pal::string_t> m_package_search_paths;
|
||||
};
|
||||
|
||||
#endif // TPAFILE_H
|
|
@ -10,7 +10,7 @@ bool ends_with(const pal::string_t& value, const pal::string_t& suffix);
|
|||
pal::string_t get_executable(const pal::string_t& filename);
|
||||
pal::string_t get_directory(const pal::string_t& path);
|
||||
pal::string_t get_filename(const pal::string_t& path);
|
||||
void append_path(pal::string_t& path1, const pal::char_t* path2);
|
||||
void append_path(pal::string_t* path1, const pal::char_t* path2);
|
||||
bool coreclr_exists_in_dir(const pal::string_t& candidate);
|
||||
|
||||
void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl);
|
||||
#endif
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
|
||||
#include "args.h"
|
||||
#include "utils.h"
|
||||
#include "coreclr.h"
|
||||
|
||||
arguments_t::arguments_t() :
|
||||
managed_application(_X("")),
|
||||
clr_path(_X("")),
|
||||
own_path(_X("")),
|
||||
app_dir(_X("")),
|
||||
app_argc(0),
|
||||
app_argv(nullptr)
|
||||
{
|
||||
|
@ -24,10 +26,22 @@ void display_help()
|
|||
|
||||
bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& args)
|
||||
{
|
||||
// Get the full name of the application
|
||||
if (!pal::get_own_executable_path(args.own_path) || !pal::realpath(args.own_path))
|
||||
// Read trace environment variable
|
||||
pal::string_t trace_str;
|
||||
if (pal::getenv(_X("COREHOST_TRACE"), &trace_str))
|
||||
{
|
||||
trace::error(_X("failed to locate current executable"));
|
||||
auto trace_val = pal::xtoi(trace_str.c_str());
|
||||
if (trace_val > 0)
|
||||
{
|
||||
trace::enable();
|
||||
trace::info(_X("Tracing enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the full name of the application
|
||||
if (!pal::get_own_executable_path(&args.own_path) || !pal::realpath(&args.own_path))
|
||||
{
|
||||
trace::error(_X("Failed to locate current executable"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -43,6 +57,12 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
|
|||
return false;
|
||||
}
|
||||
args.managed_application = pal::string_t(argv[1]);
|
||||
if (!pal::realpath(&args.managed_application))
|
||||
{
|
||||
trace::error(_X("Failed to locate managed application: %s"), args.managed_application.c_str());
|
||||
return false;
|
||||
}
|
||||
args.app_dir = get_directory(args.managed_application);
|
||||
args.app_argc = argc - 2;
|
||||
args.app_argv = &argv[2];
|
||||
}
|
||||
|
@ -54,39 +74,20 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
|
|||
managed_app.append(get_executable(own_name));
|
||||
managed_app.append(_X(".dll"));
|
||||
args.managed_application = managed_app;
|
||||
if (!pal::realpath(&args.managed_application))
|
||||
{
|
||||
trace::error(_X("Failed to locate managed application: %s"), args.managed_application.c_str());
|
||||
return false;
|
||||
}
|
||||
args.app_dir = own_dir;
|
||||
args.app_argv = &argv[1];
|
||||
args.app_argc = argc - 1;
|
||||
}
|
||||
|
||||
// Read trace environment variable
|
||||
pal::string_t trace_str;
|
||||
if (pal::getenv(_X("COREHOST_TRACE"), trace_str))
|
||||
{
|
||||
auto trace_val = pal::xtoi(trace_str.c_str());
|
||||
if (trace_val > 0)
|
||||
{
|
||||
trace::enable();
|
||||
trace::info(_X("tracing enabled"));
|
||||
}
|
||||
}
|
||||
|
||||
// Read CLR path from environment variable
|
||||
pal::string_t home_str;
|
||||
pal::getenv(_X("DOTNET_HOME"), home_str);
|
||||
if (!home_str.empty())
|
||||
{
|
||||
append_path(home_str, _X("runtime"));
|
||||
append_path(home_str, _X("coreclr"));
|
||||
args.clr_path.assign(home_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use platform-specific search algorithm
|
||||
if (pal::find_coreclr(home_str))
|
||||
{
|
||||
args.clr_path.assign(home_str);
|
||||
}
|
||||
}
|
||||
|
||||
pal::getenv(_X("DOTNET_PACKAGES"), &args.dotnet_packages);
|
||||
pal::getenv(_X("DOTNET_PACKAGES_CACHE"), &args.dotnet_packages_cache);
|
||||
pal::getenv(_X("DOTNET_SERVICING"), &args.dotnet_servicing);
|
||||
pal::getenv(_X("DOTNET_RUNTIME_SERVICING"), &args.dotnet_runtime_servicing);
|
||||
pal::getenv(_X("DOTNET_HOME"), &args.dotnet_home);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -41,9 +41,9 @@ bool coreclr::bind(const pal::string_t& libcoreclr_path)
|
|||
assert(g_coreclr == nullptr);
|
||||
|
||||
pal::string_t coreclr_dll_path(libcoreclr_path);
|
||||
append_path(coreclr_dll_path, LIBCORECLR_NAME);
|
||||
append_path(&coreclr_dll_path, LIBCORECLR_NAME);
|
||||
|
||||
if (!pal::load_library(coreclr_dll_path.c_str(), g_coreclr))
|
||||
if (!pal::load_library(coreclr_dll_path.c_str(), &g_coreclr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
597
src/corehost/src/deps_resolver.cpp
Normal file
597
src/corehost/src/deps_resolver.cpp
Normal file
|
@ -0,0 +1,597 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <set>
|
||||
#include <functional>
|
||||
#include <cassert>
|
||||
|
||||
#include "trace.h"
|
||||
#include "deps_resolver.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// -----------------------------------------------------------------------------
|
||||
// Read a single field from the deps entry
|
||||
//
|
||||
// Parameters:
|
||||
// line - A deps file entry line
|
||||
// buf - The temporary buffer to use while parsing (with size to contain "line")
|
||||
// ofs - The offset that this method will read from "line" on invocation and
|
||||
// the offset that has been consumed by this method upon successful exit
|
||||
// field - The current field read from the line
|
||||
//
|
||||
// Assumption:
|
||||
// The line should be in a CSV format, with commas separating the fields.
|
||||
// The fields themselves will be quoted. The escape character is '\\'
|
||||
//
|
||||
// Returns:
|
||||
// True if parsed successfully. Else, false
|
||||
//
|
||||
// Note:
|
||||
// Callers cannot call with the same "line" upon an unsuccessful exit.
|
||||
bool read_field(const pal::string_t& line, pal::char_t* buf, unsigned* ofs, pal::string_t* field)
|
||||
{
|
||||
unsigned& offset = *ofs;
|
||||
pal::string_t& value_recv = *field;
|
||||
|
||||
// The first character should be a '"'
|
||||
if (line[offset] != '"')
|
||||
{
|
||||
trace::error(_X("Error reading TPA file"));
|
||||
return false;
|
||||
}
|
||||
offset++;
|
||||
|
||||
auto buf_offset = 0;
|
||||
|
||||
// Iterate through characters in the string
|
||||
for (; offset < line.length(); offset++)
|
||||
{
|
||||
// Is this a '\'?
|
||||
if (line[offset] == '\\')
|
||||
{
|
||||
// Skip this character and read the next character into the buffer
|
||||
offset++;
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
// Is this a '"'?
|
||||
else if (line[offset] == '\"')
|
||||
{
|
||||
// Done! Advance to the pointer after the input
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take the character
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
buf_offset++;
|
||||
}
|
||||
buf[buf_offset] = '\0';
|
||||
value_recv.assign(buf);
|
||||
|
||||
// Consume the ',' if we have one
|
||||
if (line[offset] == ',')
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A uniqifying append helper that doesn't let two entries with the same
|
||||
// "asset_name" be part of the "output" paths.
|
||||
//
|
||||
void add_tpa_asset(
|
||||
const pal::string_t& asset_name,
|
||||
const pal::string_t& asset_path,
|
||||
std::set<pal::string_t>* items,
|
||||
pal::string_t* output)
|
||||
{
|
||||
if (items->count(asset_name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
trace::verbose(_X("Adding tpa entry: %s"), asset_path.c_str());
|
||||
output->append(asset_path);
|
||||
output->push_back(PATH_SEPARATOR);
|
||||
items->insert(asset_name);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Add mscorlib from the CLR directory. Even if CLR is serviced, we should pick
|
||||
// mscorlib from the CLR directory. If mscorlib could not be found in the CLR
|
||||
// location, then leave it to the CLR to pick the right mscorlib.
|
||||
//
|
||||
void add_mscorlib_to_tpa(const pal::string_t& clr_dir, std::set<pal::string_t>* items, pal::string_t* output)
|
||||
{
|
||||
pal::string_t mscorlib_ni_path = clr_dir + DIR_SEPARATOR + _X("mscorlib.ni.dll");
|
||||
if (pal::file_exists(mscorlib_ni_path))
|
||||
{
|
||||
add_tpa_asset(_X("mscorlib"), mscorlib_ni_path, items, output);
|
||||
return;
|
||||
}
|
||||
|
||||
pal::string_t mscorlib_path = clr_dir + DIR_SEPARATOR + _X("mscorlib.dll");
|
||||
if (pal::file_exists(mscorlib_path))
|
||||
{
|
||||
add_tpa_asset(_X("mscorlib"), mscorlib_ni_path, items, output);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A uniqifying append helper that doesn't let two "paths" to be identical in
|
||||
// the "output" string.
|
||||
//
|
||||
void add_unique_path(
|
||||
const pal::string_t& type,
|
||||
const pal::string_t& path,
|
||||
std::set<pal::string_t>* existing,
|
||||
pal::string_t* output)
|
||||
{
|
||||
if (existing->count(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
trace::verbose(_X("Adding to %s path: %s"), type.c_str(), path.c_str());
|
||||
output->append(path);
|
||||
output->push_back(PATH_SEPARATOR);
|
||||
existing->insert(path);
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Given a "base" directory, yield the relative path of this file in the package
|
||||
// layout.
|
||||
//
|
||||
// Parameters:
|
||||
// base - The base directory to look for the relative path of this entry
|
||||
// str - If the method returns true, contains the file path for this deps
|
||||
// entry relative to the "base" directory
|
||||
//
|
||||
// Returns:
|
||||
// If the file exists in the path relative to the "base" directory.
|
||||
//
|
||||
bool deps_entry_t::to_full_path(const pal::string_t& base, pal::string_t* str) const
|
||||
{
|
||||
pal::string_t& candidate = *str;
|
||||
|
||||
candidate.clear();
|
||||
|
||||
// Entry relative path contains '/' separator, sanitize it to use
|
||||
// platform separator. Perf: avoid extra copy if it matters.
|
||||
pal::string_t pal_relative_path = relative_path;
|
||||
if (_X('/') != DIR_SEPARATOR)
|
||||
{
|
||||
replace_char(&pal_relative_path, _X('/'), DIR_SEPARATOR);
|
||||
}
|
||||
|
||||
// Reserve space for the path below
|
||||
candidate.reserve(base.length() +
|
||||
library_name.length() +
|
||||
library_version.length() +
|
||||
pal_relative_path.length() + 3);
|
||||
|
||||
candidate.assign(base);
|
||||
append_path(&candidate, library_name.c_str());
|
||||
append_path(&candidate, library_version.c_str());
|
||||
append_path(&candidate, pal_relative_path.c_str());
|
||||
|
||||
bool exists = pal::file_exists(candidate);
|
||||
if (!exists)
|
||||
{
|
||||
candidate.clear();
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Given a "base" directory, yield the relative path of this file in the package
|
||||
// layout if the entry hash matches the hash file in the "base" directory
|
||||
//
|
||||
// Parameters:
|
||||
// base - The base directory to look for the relative path of this entry and
|
||||
// the hash file.
|
||||
// str - If the method returns true, contains the file path for this deps
|
||||
// entry relative to the "base" directory
|
||||
//
|
||||
// Description:
|
||||
// Looks for a file named "{PackageName}.{PackageVersion}.nupkg.{HashAlgorithm}"
|
||||
// If the deps entry's {HashAlgorithm}-{HashValue} matches the contents then
|
||||
// yields the relative path of this entry in the "base" dir.
|
||||
//
|
||||
// Returns:
|
||||
// If the file exists in the path relative to the "base" directory and there
|
||||
// was hash file match with this deps entry.
|
||||
//
|
||||
// See: to_full_path(base, str)
|
||||
//
|
||||
bool deps_entry_t::to_hash_matched_path(const pal::string_t& base, pal::string_t* str) const
|
||||
{
|
||||
pal::string_t& candidate = *str;
|
||||
|
||||
candidate.clear();
|
||||
|
||||
// Base directory must be present to perform hash lookup.
|
||||
if (base.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// First detect position of hyphen in [Algorithm]-[Hash] in the string.
|
||||
size_t pos = library_hash.find(_X("-"));
|
||||
if (pos == 0 || pos == pal::string_t::npos)
|
||||
{
|
||||
trace::verbose(_X("Invalid hash %s value for deps file entry: %s"), library_hash.c_str(), library_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the nupkg file name. Just reserve approx 8 char_t's for the algorithm name.
|
||||
pal::string_t nupkg_filename;
|
||||
nupkg_filename.reserve(library_name.length() + 1 + library_version.length() + 16);
|
||||
nupkg_filename.append(library_name);
|
||||
nupkg_filename.append(_X("."));
|
||||
nupkg_filename.append(library_version);
|
||||
nupkg_filename.append(_X(".nupkg."));
|
||||
nupkg_filename.append(library_hash.substr(0, pos));
|
||||
|
||||
// Build the hash file path str.
|
||||
pal::string_t hash_file;
|
||||
hash_file.reserve(base.length() + library_name.length() + library_version.length() + nupkg_filename.length() + 3);
|
||||
hash_file.assign(base);
|
||||
append_path(&hash_file, library_name.c_str());
|
||||
append_path(&hash_file, library_version.c_str());
|
||||
append_path(&hash_file, nupkg_filename.c_str());
|
||||
|
||||
// Read the contents of the hash file.
|
||||
pal::ifstream_t fstream(hash_file);
|
||||
if (!fstream.good())
|
||||
{
|
||||
trace::verbose(_X("The hash file is invalid [%s]"), hash_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Obtain the hash from the file.
|
||||
std::string hash;
|
||||
hash.assign(pal::istreambuf_iterator_t(fstream),
|
||||
pal::istreambuf_iterator_t());
|
||||
pal::string_t pal_hash;
|
||||
pal::to_palstring(hash.c_str(), &pal_hash);
|
||||
|
||||
// Check if contents match deps entry.
|
||||
pal::string_t entry_hash = library_hash.substr(pos + 1);
|
||||
if (entry_hash != pal_hash)
|
||||
{
|
||||
trace::verbose(_X("The file hash [%s][%d] did not match entry hash [%s][%d]"),
|
||||
pal_hash.c_str(), pal_hash.length(), entry_hash.c_str(), entry_hash.length());
|
||||
return false;
|
||||
}
|
||||
|
||||
// All good, just append the relative dir to base.
|
||||
return to_full_path(base, &candidate);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load the deps file and parse its "entry" lines which contain the "fields" of
|
||||
// the entry. Populate an array of these entries.
|
||||
//
|
||||
bool deps_resolver_t::load()
|
||||
{
|
||||
// If file doesn't exist, then assume parsed.
|
||||
if (!pal::file_exists(m_deps_path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Somehow the file stream could not be opened. This is an error.
|
||||
pal::ifstream_t file(m_deps_path);
|
||||
if (!file.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the "entry" lines of the deps file.
|
||||
std::string stdline;
|
||||
while (std::getline(file, stdline))
|
||||
{
|
||||
pal::string_t line;
|
||||
pal::to_palstring(stdline.c_str(), &line);
|
||||
|
||||
deps_entry_t entry;
|
||||
pal::string_t is_serviceable;
|
||||
pal::string_t* fields[] = {
|
||||
&entry.library_type,
|
||||
&entry.library_name,
|
||||
&entry.library_version,
|
||||
&entry.library_hash,
|
||||
&entry.asset_type,
|
||||
&entry.asset_name,
|
||||
&entry.relative_path,
|
||||
// TODO: Add when the deps file support is enabled.
|
||||
// &is_serviceable
|
||||
};
|
||||
|
||||
std::vector<pal::char_t> buf(line.length());
|
||||
|
||||
for (unsigned i = 0, offset = 0; i < sizeof(fields) / sizeof(fields[0]); ++i)
|
||||
{
|
||||
if (!(read_field(line, buf.data(), &offset, fields[i])))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Serviceable, if not false, default is true.
|
||||
entry.is_serviceable = pal::strcasecmp(is_serviceable.c_str(), _X("false")) != 0;
|
||||
|
||||
// TODO: Deps file does not follow spec. It uses '\\', should use '/'
|
||||
replace_char(&entry.relative_path, _X('\\'), _X('/'));
|
||||
|
||||
m_deps_entries.push_back(entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve path to the deps file from "args" and parse the deps file.
|
||||
//
|
||||
// Returns:
|
||||
// True if the file parse is successful or if file doesn't exist. False,
|
||||
// when there is an error parsing the file.
|
||||
//
|
||||
bool deps_resolver_t::parse_deps_file(const arguments_t& args)
|
||||
{
|
||||
const auto& app_base = args.app_dir;
|
||||
auto app_name = get_filename(args.managed_application);
|
||||
|
||||
m_deps_path.reserve(app_base.length() + 1 + app_name.length() + 5);
|
||||
m_deps_path.append(app_base);
|
||||
m_deps_path.push_back(DIR_SEPARATOR);
|
||||
m_deps_path.append(app_name, 0, app_name.find_last_of(_X(".")));
|
||||
m_deps_path.append(_X(".deps"));
|
||||
|
||||
return load();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load local assemblies by priority order of their file extensions and
|
||||
// unique-fied by their simple name.
|
||||
//
|
||||
void deps_resolver_t::get_local_assemblies(const pal::string_t& dir)
|
||||
{
|
||||
trace::verbose(_X("Adding files from dir %s"), dir.c_str());
|
||||
|
||||
// Managed extensions in priority order, pick DLL over EXE and NI over IL.
|
||||
const pal::string_t managed_ext[] = { _X(".ni.dll"), _X(".dll"), _X(".ni.exe"), _X(".exe") };
|
||||
|
||||
// List of files in the dir
|
||||
std::vector<pal::string_t> files;
|
||||
pal::readdir(dir, &files);
|
||||
|
||||
for (const auto& file : files)
|
||||
{
|
||||
for (const auto& ext : managed_ext)
|
||||
{
|
||||
// Nothing to do if file length is smaller than expected ext.
|
||||
if (file.length() <= ext.length())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto file_name = file.substr(0, file.length() - ext.length());
|
||||
auto file_ext = file.substr(file_name.length());
|
||||
|
||||
// Ext did not match expected ext, skip this file.
|
||||
if (pal::strcasecmp(file_ext.c_str(), ext.c_str()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Do a case insensitive lookup.
|
||||
// Already added entry for this asset, by priority order skip this ext
|
||||
if (m_local_assemblies.count(file_name))
|
||||
{
|
||||
trace::verbose(_X("Skipping %s because the %s already exists in local assemblies"), file.c_str(), m_local_assemblies.find(file_name)->second.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add entry for this asset
|
||||
pal::string_t file_path = dir + DIR_SEPARATOR + file;
|
||||
trace::verbose(_X("Adding %s to local assembly set from %s"), file_name.c_str(), file_path.c_str());
|
||||
m_local_assemblies.emplace(file_name, file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve the TPA list order.
|
||||
//
|
||||
// Description:
|
||||
// First, add mscorlib to the TPA. Then for each deps entry, check if they
|
||||
// are serviced. If they are not serviced, then look if they are present
|
||||
// app local. Worst case, default to the primary and seconday package
|
||||
// caches. Finally, for cases where deps file may not be present or if deps
|
||||
// did not have an entry for an app local assembly, just use them from the
|
||||
// app dir in the TPA path.
|
||||
//
|
||||
// Parameters:
|
||||
// app_dir - The application local directory
|
||||
// package_dir - The directory path to where packages are restored
|
||||
// package_cache_dir - The directory path to secondary cache for packages
|
||||
// clr_dir - The directory where the host loads the CLR
|
||||
//
|
||||
// Returns:
|
||||
// output - Pointer to a string that will hold the resolved TPA paths
|
||||
//
|
||||
void deps_resolver_t::resolve_tpa_list(
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output)
|
||||
{
|
||||
// Obtain the local assemblies in the app dir.
|
||||
get_local_assemblies(app_dir);
|
||||
|
||||
std::set<pal::string_t> items;
|
||||
|
||||
add_mscorlib_to_tpa(clr_dir, &items, output);
|
||||
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
{
|
||||
// Is this asset a "runtime" type?
|
||||
if (entry.asset_type != _X("runtime") || items.count(entry.asset_name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pal::string_t candidate;
|
||||
|
||||
// Is this a serviceable entry and is there an entry in the servicing index?
|
||||
if (entry.is_serviceable && entry.library_type == _X("Package") &&
|
||||
m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
// Is this entry present locally?
|
||||
else if (m_local_assemblies.count(entry.asset_name))
|
||||
{
|
||||
// TODO: Case insensitive look up?
|
||||
add_tpa_asset(entry.asset_name, m_local_assemblies.find(entry.asset_name)->second, &items, output);
|
||||
}
|
||||
// Is this entry present in the package restore dir?
|
||||
else if (entry.to_full_path(package_dir, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
// Is this entry present in the secondary package cache?
|
||||
else if (entry.to_hash_matched_path(package_cache_dir, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, if the deps file wasn't present or has missing entries, then
|
||||
// add the app local assemblies to the TPA.
|
||||
for (const auto& kv : m_local_assemblies)
|
||||
{
|
||||
add_tpa_asset(kv.first, kv.second, &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve the directories order for culture/native lookup
|
||||
//
|
||||
// Description:
|
||||
// This general purpose function specifies priority order of directory lookup
|
||||
// for both native images and culture specific resource images. Lookup for
|
||||
// culture assemblies is done by looking up two levels above from the file
|
||||
// path. Lookup for native images is done by looking up one level from the
|
||||
// file path.
|
||||
//
|
||||
// Parameters:
|
||||
// asset_type - The type of the asset that needs lookup, currently
|
||||
// supports "culture" and "native"
|
||||
// app_dir - The application local directory
|
||||
// package_dir - The directory path to where packages are restored
|
||||
// package_cache_dir - The directory path to secondary cache for packages
|
||||
// clr_dir - The directory where the host loads the CLR
|
||||
//
|
||||
// Returns:
|
||||
// output - Pointer to a string that will hold the resolved lookup dirs
|
||||
//
|
||||
void deps_resolver_t::resolve_probe_dirs(
|
||||
const pal::string_t& asset_type,
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output)
|
||||
{
|
||||
assert(asset_type == _X("culture") || asset_type == _X("native"));
|
||||
|
||||
// For culture assemblies, we need to provide the base directory of the culture path.
|
||||
// For example: .../Foo/en-US/Bar.dll, then, the resolved path is .../Foo
|
||||
std::function<pal::string_t(const pal::string_t&)> culture = [] (const pal::string_t& str) {
|
||||
return get_directory(get_directory(str));
|
||||
};
|
||||
// For native assemblies, obtain the directory path from the file path
|
||||
std::function<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) {
|
||||
return get_directory(str);
|
||||
};
|
||||
std::function<pal::string_t(const pal::string_t&)>& action = (asset_type == _X("culture")) ? culture : native;
|
||||
|
||||
std::set<pal::string_t> items;
|
||||
|
||||
// Fill the "output" with serviced DLL directories if they are serviceable
|
||||
// and have an entry present.
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
{
|
||||
pal::string_t redirection_path;
|
||||
if (entry.is_serviceable && entry.asset_type == asset_type && entry.library_type == _X("Package") &&
|
||||
m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &redirection_path))
|
||||
{
|
||||
add_unique_path(asset_type, action(redirection_path), &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// App local path
|
||||
add_unique_path(asset_type, app_dir, &items, output);
|
||||
|
||||
// Take care of the packages cached path
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
{
|
||||
if (entry.asset_type != asset_type)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pal::string_t candidate;
|
||||
// Package restore directory
|
||||
if (entry.to_full_path(package_dir, &candidate))
|
||||
{
|
||||
add_unique_path(asset_type, action(candidate), &items, output);
|
||||
}
|
||||
// Secondary cache
|
||||
else if (entry.to_hash_matched_path(package_cache_dir, &candidate))
|
||||
{
|
||||
add_unique_path(asset_type, action(candidate), &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// CLR path
|
||||
add_unique_path(asset_type, clr_dir, &items, output);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Entrypoint to resolve TPA, native and culture path ordering to pass to CoreCLR.
|
||||
//
|
||||
// Parameters:
|
||||
// app_dir - The application local directory
|
||||
// package_dir - The directory path to where packages are restored
|
||||
// package_cache_dir - The directory path to secondary cache for packages
|
||||
// clr_dir - The directory where the host loads the CLR
|
||||
// probe_paths - Pointer to struct containing fields that will contain
|
||||
// resolved path ordering.
|
||||
//
|
||||
//
|
||||
bool deps_resolver_t::resolve_probe_paths(
|
||||
const pal::string_t& app_dir,
|
||||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir,
|
||||
const pal::string_t& clr_dir,
|
||||
probe_paths_t* probe_paths)
|
||||
{
|
||||
resolve_tpa_list(app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->tpa);
|
||||
resolve_probe_dirs(_X("native"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->native);
|
||||
resolve_probe_dirs(_X("culture"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->culture);
|
||||
return true;
|
||||
}
|
|
@ -4,121 +4,191 @@
|
|||
#include "pal.h"
|
||||
#include "args.h"
|
||||
#include "trace.h"
|
||||
#include "tpafile.h"
|
||||
#include "deps_resolver.h"
|
||||
#include "utils.h"
|
||||
#include "coreclr.h"
|
||||
|
||||
void get_tpafile_path(const pal::string_t& app_base, const pal::string_t& app_name, pal::string_t& tpapath)
|
||||
enum StatusCode
|
||||
{
|
||||
tpapath.reserve(app_base.length() + app_name.length() + 5);
|
||||
Failure = 0x87FF0000,
|
||||
InvalidArgFailure = Failure | 0x1,
|
||||
CoreClrResolveFailure = Failure | 0x2,
|
||||
CoreClrBindFailure = Failure | 0x3,
|
||||
CoreClrInitFailure = Failure | 0x4,
|
||||
CoreClrExeFailure = Failure | 0x5,
|
||||
ResolverInitFailure = Failure | 0x6,
|
||||
ResolverResolveFailure = Failure | 0x7,
|
||||
};
|
||||
|
||||
tpapath.append(app_base);
|
||||
tpapath.push_back(DIR_SEPARATOR);
|
||||
// ----------------------------------------------------------------------
|
||||
// resolve_clr_path: Resolve CLR Path in priority order
|
||||
//
|
||||
// Description:
|
||||
// Check if CoreCLR library exists in runtime servicing dir or app
|
||||
// local or DOTNET_HOME directory in that order of priority. If these
|
||||
// fail to locate CoreCLR, then check platform-specific search.
|
||||
//
|
||||
// Returns:
|
||||
// "true" if path to the CoreCLR dir can be resolved in "clr_path"
|
||||
// parameter. Else, returns "false" with "clr_path" unmodified.
|
||||
//
|
||||
bool resolve_clr_path(const arguments_t& args, pal::string_t* clr_path)
|
||||
{
|
||||
const pal::string_t* dirs[] = {
|
||||
&args.dotnet_runtime_servicing, // DOTNET_RUNTIME_SERVICING
|
||||
&args.app_dir, // APP LOCAL
|
||||
&args.dotnet_home // DOTNET_HOME
|
||||
};
|
||||
for (int i = 0; i < sizeof(dirs) / sizeof(dirs[0]); ++i)
|
||||
{
|
||||
if (dirs[i]->empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the extension from the app_name
|
||||
auto ext_location = app_name.find_last_of('.');
|
||||
if (ext_location != std::string::npos)
|
||||
{
|
||||
tpapath.append(app_name.substr(0, ext_location));
|
||||
// App dir should contain coreclr, so skip appending path.
|
||||
pal::string_t cur_dir = *dirs[i];
|
||||
if (dirs[i] != &args.app_dir)
|
||||
{
|
||||
append_path(&cur_dir, _X("runtime"));
|
||||
append_path(&cur_dir, _X("coreclr"));
|
||||
}
|
||||
|
||||
// Found coreclr in priority order.
|
||||
if (coreclr_exists_in_dir(cur_dir))
|
||||
{
|
||||
clr_path->assign(cur_dir);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Use platform-specific search algorithm
|
||||
pal::string_t home_dir = args.dotnet_home;
|
||||
if (pal::find_coreclr(&home_dir))
|
||||
{
|
||||
tpapath.append(app_name);
|
||||
clr_path->assign(home_dir);
|
||||
return true;
|
||||
}
|
||||
tpapath.append(_X(".deps"));
|
||||
return false;
|
||||
}
|
||||
|
||||
int run(arguments_t args, pal::string_t app_base, tpafile tpa)
|
||||
int run(const arguments_t& args, const pal::string_t& clr_path)
|
||||
{
|
||||
tpa.add_from_local_dir(app_base);
|
||||
// Load the deps resolver
|
||||
deps_resolver_t resolver(args);
|
||||
if (!resolver.valid())
|
||||
{
|
||||
trace::error(_X("Invalid .deps file"));
|
||||
return StatusCode::ResolverInitFailure;
|
||||
}
|
||||
|
||||
// Add packages directory
|
||||
pal::string_t packages_dir;
|
||||
if (!pal::get_default_packages_directory(packages_dir))
|
||||
pal::string_t packages_dir = args.dotnet_packages;
|
||||
if (!pal::directory_exists(packages_dir))
|
||||
{
|
||||
trace::info(_X("did not find local packages directory"));
|
||||
|
||||
// We can continue, the app may have it's dependencies locally
|
||||
(void)pal::get_default_packages_directory(&packages_dir);
|
||||
}
|
||||
else
|
||||
trace::info(_X("Package directory: %s"), packages_dir.empty() ? _X("not specified") : packages_dir.c_str());
|
||||
|
||||
probe_paths_t probe_paths;
|
||||
if (!resolver.resolve_probe_paths(args.app_dir, packages_dir, args.dotnet_packages_cache, clr_path, &probe_paths))
|
||||
{
|
||||
trace::info(_X("using packages directory: %s"), packages_dir.c_str());
|
||||
tpa.add_package_dir(packages_dir);
|
||||
return StatusCode::ResolverResolveFailure;
|
||||
}
|
||||
|
||||
// Add native search path
|
||||
trace::info(_X("using native search path: %s"), packages_dir.c_str());
|
||||
tpa.add_native_search_path(args.clr_path);
|
||||
|
||||
// Build TPA list and search paths
|
||||
pal::string_t tpalist;
|
||||
tpa.write_tpa_list(tpalist);
|
||||
|
||||
pal::string_t search_paths;
|
||||
tpa.write_native_paths(search_paths);
|
||||
|
||||
// Build CoreCLR properties
|
||||
const char* property_keys[] = {
|
||||
"TRUSTED_PLATFORM_ASSEMBLIES",
|
||||
"APP_PATHS",
|
||||
"APP_NI_PATHS",
|
||||
"NATIVE_DLL_SEARCH_DIRECTORIES",
|
||||
"AppDomainCompatSwitch"
|
||||
"PLATFORM_RESOURCE_ROOTS",
|
||||
"AppDomainCompatSwitch",
|
||||
// TODO: pipe this from corehost.json
|
||||
"SERVER_GC"
|
||||
};
|
||||
|
||||
auto tpa_cstr = pal::to_stdstring(tpalist);
|
||||
auto app_base_cstr = pal::to_stdstring(app_base);
|
||||
auto search_paths_cstr = pal::to_stdstring(search_paths);
|
||||
auto tpa_paths_cstr = pal::to_stdstring(probe_paths.tpa);
|
||||
auto app_base_cstr = pal::to_stdstring(args.app_dir);
|
||||
auto native_dirs_cstr = pal::to_stdstring(probe_paths.native);
|
||||
auto culture_dirs_cstr = pal::to_stdstring(probe_paths.culture);
|
||||
|
||||
const char* property_values[] = {
|
||||
// TRUSTED_PLATFORM_ASSEMBLIES
|
||||
tpa_cstr.c_str(),
|
||||
tpa_paths_cstr.c_str(),
|
||||
// APP_PATHS
|
||||
app_base_cstr.c_str(),
|
||||
// APP_NI_PATHS
|
||||
app_base_cstr.c_str(),
|
||||
// NATIVE_DLL_SEARCH_DIRECTORIES
|
||||
search_paths_cstr.c_str(),
|
||||
native_dirs_cstr.c_str(),
|
||||
// PLATFORM_RESOURCE_ROOTS
|
||||
culture_dirs_cstr.c_str(),
|
||||
// AppDomainCompatSwitch
|
||||
"UseLatestBehaviorWhenTFMNotSpecified"
|
||||
"UseLatestBehaviorWhenTFMNotSpecified",
|
||||
// SERVER_GC
|
||||
"1"
|
||||
};
|
||||
|
||||
// Dump TPA list
|
||||
trace::verbose(_X("TPA List: %s"), tpalist.c_str());
|
||||
|
||||
// Dump native search paths
|
||||
trace::verbose(_X("Native Paths: %s"), search_paths.c_str());
|
||||
size_t property_size = sizeof(property_keys) / sizeof(property_keys[0]);
|
||||
|
||||
// Bind CoreCLR
|
||||
if (!coreclr::bind(args.clr_path))
|
||||
if (!coreclr::bind(clr_path))
|
||||
{
|
||||
trace::error(_X("failed to bind to coreclr"));
|
||||
return 1;
|
||||
trace::error(_X("Failed to bind to coreclr"));
|
||||
return StatusCode::CoreClrBindFailure;
|
||||
}
|
||||
|
||||
// Verbose logging
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
for (size_t i = 0; i < property_size; ++i)
|
||||
{
|
||||
pal::string_t key, val;
|
||||
pal::to_palstring(property_keys[i], &key);
|
||||
pal::to_palstring(property_values[i], &val);
|
||||
trace::verbose(_X("Property %s = %s"), key.c_str(), val.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string own_path;
|
||||
pal::to_stdstring(args.own_path.c_str(), &own_path);
|
||||
|
||||
// Initialize CoreCLR
|
||||
coreclr::host_handle_t host_handle;
|
||||
coreclr::domain_id_t domain_id;
|
||||
auto hr = coreclr::initialize(
|
||||
pal::to_stdstring(args.own_path).c_str(),
|
||||
own_path.c_str(),
|
||||
"clrhost",
|
||||
property_keys,
|
||||
property_values,
|
||||
sizeof(property_keys) / sizeof(property_keys[0]),
|
||||
property_size,
|
||||
&host_handle,
|
||||
&domain_id);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
trace::error(_X("failed to initialize CoreCLR, HRESULT: 0x%X"), hr);
|
||||
return 1;
|
||||
trace::error(_X("Failed to initialize CoreCLR, HRESULT: 0x%X"), hr);
|
||||
return StatusCode::CoreClrInitFailure;
|
||||
}
|
||||
|
||||
// Convert the args (probably not the most performant way to do this...)
|
||||
auto argv_strs = new std::string[args.app_argc];
|
||||
auto argv = new const char*[args.app_argc];
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
pal::string_t arg_str;
|
||||
for (int i = 0; i < args.app_argc; i++)
|
||||
{
|
||||
arg_str.append(args.app_argv[i]);
|
||||
arg_str.append(_X(","));
|
||||
}
|
||||
trace::info(_X("Launch host: %s app: %s, argc: %d args: %s"), args.own_path.c_str(),
|
||||
args.managed_application.c_str(), args.app_argc, arg_str.c_str());
|
||||
}
|
||||
|
||||
// Initialize with empty strings
|
||||
std::vector<std::string> argv_strs(args.app_argc);
|
||||
std::vector<const char*> argv(args.app_argc);
|
||||
for (int i = 0; i < args.app_argc; i++)
|
||||
{
|
||||
argv_strs[i] = pal::to_stdstring(pal::string_t(args.app_argv[i]));
|
||||
pal::to_stdstring(args.app_argv[i], &argv_strs[i]);
|
||||
argv[i] = argv_strs[i].c_str();
|
||||
}
|
||||
|
||||
|
@ -127,21 +197,21 @@ int run(arguments_t args, pal::string_t app_base, tpafile tpa)
|
|||
hr = coreclr::execute_assembly(
|
||||
host_handle,
|
||||
domain_id,
|
||||
args.app_argc,
|
||||
argv,
|
||||
argv.size(),
|
||||
argv.data(),
|
||||
pal::to_stdstring(args.managed_application).c_str(),
|
||||
&exit_code);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
trace::error(_X("failed to execute managed app, HRESULT: 0x%X"), hr);
|
||||
return 1;
|
||||
trace::error(_X("Failed to execute managed app, HRESULT: 0x%X"), hr);
|
||||
return StatusCode::CoreClrExeFailure;
|
||||
}
|
||||
|
||||
// Shut down the CoreCLR
|
||||
hr = coreclr::shutdown(host_handle, domain_id);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
trace::warning(_X("failed to shut down CoreCLR, HRESULT: 0x%X"), hr);
|
||||
trace::warning(_X("Failed to shut down CoreCLR, HRESULT: 0x%X"), hr);
|
||||
}
|
||||
|
||||
coreclr::unload();
|
||||
|
@ -155,70 +225,19 @@ int __cdecl wmain(const int argc, const pal::char_t* argv[])
|
|||
int main(const int argc, const pal::char_t* argv[])
|
||||
#endif
|
||||
{
|
||||
// Take care of arguments
|
||||
arguments_t args;
|
||||
if (!parse_arguments(argc, argv, args))
|
||||
{
|
||||
return 1;
|
||||
return StatusCode::InvalidArgFailure;
|
||||
}
|
||||
|
||||
// Resolve paths
|
||||
if (!pal::realpath(args.managed_application))
|
||||
// Resolve CLR path
|
||||
pal::string_t clr_path;
|
||||
if (!resolve_clr_path(args, &clr_path))
|
||||
{
|
||||
trace::error(_X("failed to locate managed application: %s"), args.managed_application.c_str());
|
||||
return 1;
|
||||
trace::error(_X("Could not resolve coreclr path"));
|
||||
return StatusCode::CoreClrResolveFailure;
|
||||
}
|
||||
trace::info(_X("preparing to launch managed application: %s"), args.managed_application.c_str());
|
||||
trace::info(_X("host path: %s"), args.own_path.c_str());
|
||||
|
||||
pal::string_t argstr;
|
||||
for (int i = 0; i < args.app_argc; i++)
|
||||
{
|
||||
argstr.append(args.app_argv[i]);
|
||||
argstr.append(_X(","));
|
||||
}
|
||||
trace::info(_X("App argc: %d"), args.app_argc);
|
||||
trace::info(_X("App argv: %s"), argstr.c_str());
|
||||
|
||||
auto app_base = get_directory(args.managed_application);
|
||||
auto app_name = get_filename(args.managed_application);
|
||||
|
||||
// App-local coreclr wins
|
||||
{
|
||||
pal::string_t candidate;
|
||||
candidate.assign(app_base);
|
||||
append_path(candidate, LIBCORECLR_NAME);
|
||||
|
||||
if (pal::file_exists(candidate))
|
||||
{
|
||||
args.clr_path.assign(app_base);
|
||||
}
|
||||
}
|
||||
|
||||
if (args.clr_path.empty())
|
||||
{
|
||||
trace::error(_X("failed to locate CLR files, set the DOTNET_HOME environment variable"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!pal::realpath(args.clr_path))
|
||||
{
|
||||
trace::error(_X("failed to locate CLR files at %s"), args.clr_path.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
trace::info(_X("using CLR files from: %s"), args.clr_path.c_str());
|
||||
trace::info(_X("preparing to launch: %s"), app_name.c_str());
|
||||
trace::info(_X("using app base: %s"), app_base.c_str());
|
||||
|
||||
// Check for and load deps file
|
||||
pal::string_t tpafile_path;
|
||||
get_tpafile_path(app_base, app_name, tpafile_path);
|
||||
trace::info(_X("checking for .deps File at: %s"), tpafile_path.c_str());
|
||||
tpafile tpa;
|
||||
if (!tpa.load(tpafile_path))
|
||||
{
|
||||
trace::error(_X("invalid .deps file"));
|
||||
return 1;
|
||||
}
|
||||
return run(args, app_base, tpa);
|
||||
return run(args, clr_path);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "utils.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <dlfcn.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -19,7 +20,7 @@
|
|||
#define symlinkEntrypointExecutable "/proc/curproc/exe"
|
||||
#endif
|
||||
|
||||
bool pal::find_coreclr(pal::string_t& recv)
|
||||
bool pal::find_coreclr(pal::string_t* recv)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
pal::string_t test;
|
||||
|
@ -28,24 +29,24 @@ bool pal::find_coreclr(pal::string_t& recv)
|
|||
// TODO: These paths should be consistent
|
||||
candidate.assign("/usr/share/dotnet/runtime/coreclr");
|
||||
if (coreclr_exists_in_dir(candidate)) {
|
||||
recv.assign(candidate);
|
||||
recv->assign(candidate);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
candidate.assign("/usr/local/share/dotnet/runtime/coreclr");
|
||||
if (coreclr_exists_in_dir(candidate)) {
|
||||
recv.assign(candidate);
|
||||
recv->assign(candidate);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pal::load_library(const char_t* path, dll_t& dll)
|
||||
bool pal::load_library(const char_t* path, dll_t* dll)
|
||||
{
|
||||
dll = dlopen(path, RTLD_LAZY);
|
||||
if (dll == nullptr)
|
||||
*dll = dlopen(path, RTLD_LAZY);
|
||||
if (*dll == nullptr)
|
||||
{
|
||||
trace::error(_X("failed to load %s, error: %s"), path, dlerror());
|
||||
trace::error(_X("Failed to load %s, error: %s"), path, dlerror());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -56,7 +57,7 @@ pal::proc_t pal::get_symbol(dll_t library, const char* name)
|
|||
auto result = dlsym(library, name);
|
||||
if (result == nullptr)
|
||||
{
|
||||
trace::error(_X("failed to resolve library symbol %s, error: %s"), name, dlerror());
|
||||
trace::error(_X("Failed to resolve library symbol %s, error: %s"), name, dlerror());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -65,7 +66,7 @@ void pal::unload_library(dll_t library)
|
|||
{
|
||||
if (dlclose(library) != 0)
|
||||
{
|
||||
trace::warning(_X("failed to unload library, error: %s"), dlerror());
|
||||
trace::warning(_X("Failed to unload library, error: %s"), dlerror());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,19 +80,20 @@ bool pal::is_path_rooted(const pal::string_t& path)
|
|||
return path.front() == '/';
|
||||
}
|
||||
|
||||
bool pal::get_default_packages_directory(pal::string_t& recv)
|
||||
bool pal::get_default_packages_directory(pal::string_t* recv)
|
||||
{
|
||||
recv->clear();
|
||||
if (!pal::getenv("HOME", recv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
append_path(recv, _X(".dnx"));
|
||||
append_path(recv, _X("packages"));
|
||||
append_path(&*recv, _X(".dnx"));
|
||||
append_path(&*recv, _X("packages"));
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
bool pal::get_own_executable_path(pal::string_t& recv)
|
||||
bool pal::get_own_executable_path(pal::string_t* recv)
|
||||
{
|
||||
uint32_t path_length = 0;
|
||||
if (_NSGetExecutablePath(nullptr, &path_length) == -1)
|
||||
|
@ -99,28 +101,28 @@ bool pal::get_own_executable_path(pal::string_t& recv)
|
|||
char path_buf[path_length];
|
||||
if (_NSGetExecutablePath(path_buf, &path_length) == 0)
|
||||
{
|
||||
recv.assign(path_buf);
|
||||
recv->assign(path_buf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool pal::get_own_executable_path(pal::string_t& recv)
|
||||
bool pal::get_own_executable_path(pal::string_t* recv)
|
||||
{
|
||||
// Just return the symlink to the exe from /proc
|
||||
// We'll call realpath on it later
|
||||
recv.assign(symlinkEntrypointExecutable);
|
||||
recv->assign(symlinkEntrypointExecutable);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pal::getenv(const pal::char_t* name, pal::string_t& recv)
|
||||
bool pal::getenv(const pal::char_t* name, pal::string_t* recv)
|
||||
{
|
||||
auto result = ::getenv(name);
|
||||
if (result != nullptr)
|
||||
{
|
||||
recv.assign(result);
|
||||
recv->assign(result);
|
||||
}
|
||||
|
||||
// We don't return false. Windows does have a concept of an error reading the variable,
|
||||
|
@ -128,10 +130,10 @@ bool pal::getenv(const pal::char_t* name, pal::string_t& recv)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool pal::realpath(pal::string_t& path)
|
||||
bool pal::realpath(pal::string_t* path)
|
||||
{
|
||||
pal::char_t buf[PATH_MAX];
|
||||
auto resolved = ::realpath(path.c_str(), buf);
|
||||
auto resolved = ::realpath(path->c_str(), buf);
|
||||
if (resolved == nullptr)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
|
@ -141,19 +143,25 @@ bool pal::realpath(pal::string_t& path)
|
|||
perror("realpath()");
|
||||
return false;
|
||||
}
|
||||
path.assign(resolved);
|
||||
path->assign(resolved);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pal::file_exists(const pal::string_t& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
struct stat buffer;
|
||||
return (::stat(path.c_str(), &buffer) == 0);
|
||||
}
|
||||
|
||||
std::vector<pal::string_t> pal::readdir(const pal::string_t& path)
|
||||
void pal::readdir(const pal::string_t& path, std::vector<pal::string_t>* list)
|
||||
{
|
||||
std::vector<pal::string_t> files;
|
||||
assert(list != nullptr);
|
||||
|
||||
std::vector<pal::string_t>& files = *list;
|
||||
|
||||
auto dir = opendir(path.c_str());
|
||||
if (dir != nullptr)
|
||||
|
@ -197,6 +205,4 @@ std::vector<pal::string_t> pal::readdir(const pal::string_t& path)
|
|||
files.push_back(pal::string_t(entry->d_name));
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
|
|
@ -5,23 +5,24 @@
|
|||
#include "trace.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> g_converter;
|
||||
|
||||
bool pal::find_coreclr(pal::string_t& recv)
|
||||
bool pal::find_coreclr(pal::string_t* recv)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
pal::string_t test;
|
||||
|
||||
// Try %LocalAppData%\dotnet
|
||||
if (pal::getenv(_X("LocalAppData"), candidate)) {
|
||||
append_path(candidate, _X("dotnet"));
|
||||
append_path(candidate, _X("runtime"));
|
||||
append_path(candidate, _X("coreclr"));
|
||||
if (pal::getenv(_X("LocalAppData"), &candidate)) {
|
||||
append_path(&candidate, _X("dotnet"));
|
||||
append_path(&candidate, _X("runtime"));
|
||||
append_path(&candidate, _X("coreclr"));
|
||||
if (coreclr_exists_in_dir(candidate)) {
|
||||
recv.assign(candidate);
|
||||
recv->assign(candidate);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -30,12 +31,12 @@ bool pal::find_coreclr(pal::string_t& recv)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool pal::load_library(const char_t* path, dll_t& dll)
|
||||
bool pal::load_library(const char_t* path, dll_t* dll)
|
||||
{
|
||||
dll = ::LoadLibraryW(path);
|
||||
if (dll == nullptr)
|
||||
*dll = ::LoadLibraryW(path);
|
||||
if (*dll == nullptr)
|
||||
{
|
||||
trace::error(_X("failed to load coreclr.dll from %s, HRESULT: 0x%X"), path, HRESULT_FROM_WIN32(GetLastError()));
|
||||
trace::error(_X("Failed to load coreclr.dll from %s, HRESULT: 0x%X"), path, HRESULT_FROM_WIN32(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -43,15 +44,15 @@ bool pal::load_library(const char_t* path, dll_t& dll)
|
|||
HMODULE dummy_module;
|
||||
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, path, &dummy_module))
|
||||
{
|
||||
trace::error(_X("failed to pin library: %s"));
|
||||
trace::error(_X("Failed to pin library: %s"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
pal::char_t buf[PATH_MAX];
|
||||
::GetModuleFileNameW(dll, buf, PATH_MAX);
|
||||
trace::info(_X("loaded library from %s"), buf);
|
||||
::GetModuleFileNameW(*dll, buf, PATH_MAX);
|
||||
trace::info(_X("Loaded library from %s"), buf);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -67,14 +68,15 @@ void pal::unload_library(dll_t library)
|
|||
// No-op. On windows, we pin the library, so it can't be unloaded.
|
||||
}
|
||||
|
||||
bool pal::get_default_packages_directory(string_t& recv)
|
||||
bool pal::get_default_packages_directory(string_t* recv)
|
||||
{
|
||||
recv->clear();
|
||||
if (!pal::getenv(_X("USERPROFILE"), recv))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
append_path(recv, _X(".dnx"));
|
||||
append_path(recv, _X("packages"));
|
||||
append_path(&*recv, _X(".dnx"));
|
||||
append_path(&*recv, _X("packages"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -83,7 +85,7 @@ bool pal::is_path_rooted(const string_t& path)
|
|||
return path.length() >= 2 && path[1] == L':';
|
||||
}
|
||||
|
||||
bool pal::getenv(const char_t* name, string_t& recv)
|
||||
bool pal::getenv(const char_t* name, string_t* recv)
|
||||
{
|
||||
auto length = ::GetEnvironmentVariableW(name, nullptr, 0);
|
||||
if (length == 0)
|
||||
|
@ -94,17 +96,17 @@ bool pal::getenv(const char_t* name, string_t& recv)
|
|||
// Leave the receiver empty and return success
|
||||
return true;
|
||||
}
|
||||
trace::error(_X("failed to read enviroment variable '%s', HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError()));
|
||||
trace::error(_X("Failed to read enviroment variable '%s', HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
auto buf = new char_t[length];
|
||||
if (::GetEnvironmentVariableW(name, buf, length) == 0)
|
||||
{
|
||||
trace::error(_X("failed to read enviroment variable '%s', HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError()));
|
||||
trace::error(_X("Failed to read enviroment variable '%s', HRESULT: 0x%X"), name, HRESULT_FROM_WIN32(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
recv.assign(buf);
|
||||
recv->assign(buf);
|
||||
delete[] buf;
|
||||
|
||||
return true;
|
||||
|
@ -115,14 +117,14 @@ int pal::xtoi(const char_t* input)
|
|||
return ::_wtoi(input);
|
||||
}
|
||||
|
||||
bool pal::get_own_executable_path(string_t& recv)
|
||||
bool pal::get_own_executable_path(string_t* recv)
|
||||
{
|
||||
char_t program_path[MAX_PATH];
|
||||
DWORD dwModuleFileName = ::GetModuleFileNameW(NULL, program_path, MAX_PATH);
|
||||
if (dwModuleFileName == 0 || dwModuleFileName >= MAX_PATH) {
|
||||
return false;
|
||||
}
|
||||
recv.assign(program_path);
|
||||
recv->assign(program_path);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -136,21 +138,36 @@ pal::string_t pal::to_palstring(const std::string& str)
|
|||
return g_converter.from_bytes(str);
|
||||
}
|
||||
|
||||
bool pal::realpath(string_t& path)
|
||||
void pal::to_palstring(const char* str, pal::string_t* out)
|
||||
{
|
||||
out->assign(g_converter.from_bytes(str));
|
||||
}
|
||||
|
||||
void pal::to_stdstring(const pal::char_t* str, std::string* out)
|
||||
{
|
||||
out->assign(g_converter.to_bytes(str));
|
||||
}
|
||||
|
||||
bool pal::realpath(string_t* path)
|
||||
{
|
||||
char_t buf[MAX_PATH];
|
||||
auto res = ::GetFullPathNameW(path.c_str(), MAX_PATH, buf, nullptr);
|
||||
auto res = ::GetFullPathNameW(path->c_str(), MAX_PATH, buf, nullptr);
|
||||
if (res == 0 || res > MAX_PATH)
|
||||
{
|
||||
trace::error(_X("error resolving path: %s"), path.c_str());
|
||||
trace::error(_X("Error resolving path: %s"), path->c_str());
|
||||
return false;
|
||||
}
|
||||
path.assign(buf);
|
||||
path->assign(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pal::file_exists(const string_t& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WIN32_FIND_DATAW data;
|
||||
auto find_handle = ::FindFirstFileW(path.c_str(), &data);
|
||||
bool found = find_handle != INVALID_HANDLE_VALUE;
|
||||
|
@ -158,9 +175,11 @@ bool pal::file_exists(const string_t& path)
|
|||
return found;
|
||||
}
|
||||
|
||||
std::vector<pal::string_t> pal::readdir(const string_t& path)
|
||||
void pal::readdir(const string_t& path, std::vector<pal::string_t>* list)
|
||||
{
|
||||
std::vector<string_t> files;
|
||||
assert(list != nullptr);
|
||||
|
||||
std::vector<string_t>& files = *list;
|
||||
|
||||
string_t search_string(path);
|
||||
search_string.push_back(DIR_SEPARATOR);
|
||||
|
@ -174,6 +193,4 @@ std::vector<pal::string_t> pal::readdir(const string_t& path)
|
|||
files.push_back(filepath);
|
||||
} while (::FindNextFileW(handle, &data));
|
||||
::FindClose(handle);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
|
128
src/corehost/src/servicing_index.cpp
Normal file
128
src/corehost/src/servicing_index.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "trace.h"
|
||||
#include "servicing_index.h"
|
||||
|
||||
static const pal::char_t* DOTNET_SERVICING_INDEX_TXT = _X("dotnet_servicing_index.txt");
|
||||
|
||||
servicing_index_t::servicing_index_t(const pal::string_t& svc_dir)
|
||||
{
|
||||
m_patch_root = svc_dir;
|
||||
if (!m_patch_root.empty())
|
||||
{
|
||||
m_index_file.assign(m_patch_root);
|
||||
append_path(&m_index_file, DOTNET_SERVICING_INDEX_TXT);
|
||||
}
|
||||
m_parsed = m_index_file.empty() || !pal::file_exists(m_index_file);
|
||||
}
|
||||
|
||||
bool servicing_index_t::find_redirection(
|
||||
const pal::string_t& package_name,
|
||||
const pal::string_t& package_version,
|
||||
const pal::string_t& package_relative,
|
||||
pal::string_t* redirection)
|
||||
{
|
||||
ensure_redirections();
|
||||
|
||||
redirection->clear();
|
||||
|
||||
if (m_redirections.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pal::stringstream_t stream;
|
||||
stream << package_name << _X("|") << package_version << _X("|") << package_relative;
|
||||
|
||||
auto iter = m_redirections.find(stream.str());
|
||||
if (iter != m_redirections.end())
|
||||
{
|
||||
pal::string_t full_path = m_patch_root;
|
||||
append_path(&full_path, iter->second.c_str());
|
||||
if (pal::file_exists(full_path))
|
||||
{
|
||||
*redirection = full_path;
|
||||
trace::verbose(_X("Servicing %s with %s"), stream.str().c_str(), redirection->c_str());
|
||||
return true;
|
||||
}
|
||||
trace::verbose(_X("Serviced file %s doesn't exist"), full_path.c_str());
|
||||
}
|
||||
|
||||
trace::verbose(_X("Entry %s not serviced or file doesn't exist"), stream.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
void servicing_index_t::ensure_redirections()
|
||||
{
|
||||
if (m_parsed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pal::ifstream_t fstream(m_index_file);
|
||||
if (!fstream.good())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pal::stringstream_t sstream;
|
||||
std::string line;
|
||||
while (std::getline(fstream, line))
|
||||
{
|
||||
pal::string_t str;
|
||||
pal::to_palstring(line.c_str(), &str);
|
||||
|
||||
// Can interpret line as "package"?
|
||||
pal::string_t prefix = _X("package|");
|
||||
if (str.find(prefix) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pal::string_t name, version, relative;
|
||||
pal::string_t* tokens[] = { &name, &version, &relative };
|
||||
pal::string_t delim[] = { pal::string_t(_X("|")), pal::string_t(_X("|")), pal::string_t(_X("=")) };
|
||||
|
||||
bool bad_line = false;
|
||||
|
||||
size_t from = prefix.length();
|
||||
for (size_t cur = 0; cur < (sizeof(delim) / sizeof(delim[0])); ++cur)
|
||||
{
|
||||
size_t pos = str.find(delim[cur], from);
|
||||
if (pos == pal::string_t::npos)
|
||||
{
|
||||
bad_line = true;
|
||||
break;
|
||||
}
|
||||
|
||||
tokens[cur]->assign(str.substr(from, pos - from));
|
||||
from = pos + 1;
|
||||
}
|
||||
|
||||
if (bad_line)
|
||||
{
|
||||
trace::error(_X("Invalid line in servicing index. Skipping..."));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save redirection for this package.
|
||||
sstream.str(_X(""));
|
||||
sstream << name << _X("|") << version << _X("|") << relative;
|
||||
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
trace::verbose(_X("Adding servicing entry %s => %s"), sstream.str().c_str(), str.substr(from).c_str());
|
||||
}
|
||||
|
||||
// Store just the filename.
|
||||
pal::string_t redir = str.substr(from);
|
||||
if (_X('/') != DIR_SEPARATOR)
|
||||
{
|
||||
replace_char(&redir, _X('/'), DIR_SEPARATOR);
|
||||
}
|
||||
m_redirections.emplace(sstream.str(), redir);
|
||||
}
|
||||
|
||||
m_parsed = true;
|
||||
}
|
|
@ -1,251 +0,0 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <set>
|
||||
#include <functional>
|
||||
|
||||
#include "trace.h"
|
||||
#include "tpafile.h"
|
||||
#include "utils.h"
|
||||
|
||||
bool read_field(pal::string_t line, int& offset, pal::string_t& value_recv)
|
||||
{
|
||||
// The first character should be a '"'
|
||||
if (line[offset] != '"')
|
||||
{
|
||||
trace::error(_X("error reading TPA file"));
|
||||
return false;
|
||||
}
|
||||
offset++;
|
||||
|
||||
// Set up destination buffer (it can't be bigger than the original line)
|
||||
pal::char_t buf[PATH_MAX];
|
||||
auto buf_offset = 0;
|
||||
|
||||
// Iterate through characters in the string
|
||||
for (; offset < line.length(); offset++)
|
||||
{
|
||||
// Is this a '\'?
|
||||
if (line[offset] == '\\')
|
||||
{
|
||||
// Skip this character and read the next character into the buffer
|
||||
offset++;
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
// Is this a '"'?
|
||||
else if (line[offset] == '\"')
|
||||
{
|
||||
// Done! Advance to the pointer after the input
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take the character
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
buf_offset++;
|
||||
}
|
||||
buf[buf_offset] = '\0';
|
||||
value_recv.assign(buf);
|
||||
|
||||
// Consume the ',' if we have one
|
||||
if (line[offset] == ',')
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tpafile::load(pal::string_t path)
|
||||
{
|
||||
// Check if the file exists, if not, there is nothing to add
|
||||
if (!pal::file_exists(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Open the file
|
||||
pal::ifstream_t file(path);
|
||||
if (!file.good())
|
||||
{
|
||||
// Failed to open the file!
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read lines from the file
|
||||
while (true)
|
||||
{
|
||||
std::string line;
|
||||
std::getline(file, line);
|
||||
auto line_palstr = pal::to_palstring(line);
|
||||
if (file.eof())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto offset = 0;
|
||||
|
||||
tpaentry_t entry;
|
||||
|
||||
// Read fields
|
||||
if (!(read_field(line_palstr, offset, entry.library_type))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.library_name))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.library_version))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.library_hash))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.asset_type))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.asset_name))) return false;
|
||||
if (!(read_field(line_palstr, offset, entry.relative_path))) return false;
|
||||
|
||||
m_entries.push_back(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void tpafile::add_from_local_dir(const pal::string_t& dir)
|
||||
{
|
||||
trace::verbose(_X("adding files from %s to TPA"), dir.c_str());
|
||||
const pal::char_t * const tpa_extensions[] = {
|
||||
_X(".ni.dll"), // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
|
||||
_X(".dll"),
|
||||
_X(".ni.exe"),
|
||||
_X(".exe"),
|
||||
};
|
||||
|
||||
std::set<pal::string_t> added_assemblies;
|
||||
|
||||
// Get directory entries
|
||||
auto files = pal::readdir(dir);
|
||||
for (auto ext : tpa_extensions)
|
||||
{
|
||||
auto len = pal::strlen(ext);
|
||||
for (auto file : files)
|
||||
{
|
||||
// Can't be a match if it's the same length as the extension :)
|
||||
if (file.length() > len)
|
||||
{
|
||||
// Extract the same amount of text from the end of file name
|
||||
auto file_ext = file.substr(file.length() - len, len);
|
||||
|
||||
// Check if this file name matches
|
||||
if (pal::strcasecmp(ext, file_ext.c_str()) == 0)
|
||||
{
|
||||
// Get the assembly name by stripping the extension
|
||||
// and add it to the set so we can de-dupe
|
||||
auto asm_name = file.substr(0, file.length() - len);
|
||||
|
||||
// TODO(anurse): Also check if already in TPA file
|
||||
if (added_assemblies.find(asm_name) == added_assemblies.end())
|
||||
{
|
||||
added_assemblies.insert(asm_name);
|
||||
|
||||
tpaentry_t entry;
|
||||
entry.asset_type = pal::string_t(_X("runtime"));
|
||||
entry.library_name = pal::string_t(asm_name);
|
||||
entry.library_version = pal::string_t(_X(""));
|
||||
|
||||
pal::string_t relpath(dir);
|
||||
relpath.push_back(DIR_SEPARATOR);
|
||||
relpath.append(file);
|
||||
entry.relative_path = relpath;
|
||||
entry.asset_name = asm_name;
|
||||
|
||||
trace::verbose(_X("adding %s to TPA list from %s"), asm_name.c_str(), relpath.c_str());
|
||||
m_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tpafile::write_tpa_list(pal::string_t& output)
|
||||
{
|
||||
std::set<pal::string_t> items;
|
||||
for (auto entry : m_entries)
|
||||
{
|
||||
if (pal::strcmp(entry.asset_type.c_str(), _X("runtime")) == 0 && items.find(entry.asset_name) == items.end())
|
||||
{
|
||||
// Resolve the full path
|
||||
for (auto search_path : m_package_search_paths)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
candidate.reserve(search_path.length() +
|
||||
entry.library_name.length() +
|
||||
entry.library_version.length() +
|
||||
entry.relative_path.length() + 3);
|
||||
candidate.append(search_path);
|
||||
|
||||
append_path(candidate, entry.library_name.c_str());
|
||||
append_path(candidate, entry.library_version.c_str());
|
||||
append_path(candidate, entry.relative_path.c_str());
|
||||
if (pal::file_exists(candidate))
|
||||
{
|
||||
trace::verbose(_X("adding tpa entry: %s"), candidate.c_str());
|
||||
|
||||
output.append(candidate);
|
||||
output.push_back(PATH_SEPARATOR);
|
||||
items.insert(entry.asset_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tpafile::write_native_paths(pal::string_t& output)
|
||||
{
|
||||
std::set<pal::string_t> items;
|
||||
for (auto search_path : m_native_search_paths)
|
||||
{
|
||||
if (items.find(search_path) == items.end())
|
||||
{
|
||||
trace::verbose(_X("adding native search path: %s"), search_path.c_str());
|
||||
output.append(search_path);
|
||||
output.push_back(PATH_SEPARATOR);
|
||||
items.insert(search_path);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto entry : m_entries)
|
||||
{
|
||||
auto dir = entry.relative_path.substr(0, entry.relative_path.find_last_of(DIR_SEPARATOR));
|
||||
if (pal::strcmp(entry.asset_type.c_str(), _X("native")) == 0 && items.find(dir) == items.end())
|
||||
{
|
||||
// Resolve the full path
|
||||
for (auto search_path : m_package_search_paths)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
candidate.reserve(search_path.length() +
|
||||
entry.library_name.length() +
|
||||
entry.library_version.length() +
|
||||
dir.length() + 3);
|
||||
candidate.append(search_path);
|
||||
|
||||
append_path(candidate, entry.library_name.c_str());
|
||||
append_path(candidate, entry.library_version.c_str());
|
||||
append_path(candidate, get_directory(entry.relative_path).c_str());
|
||||
|
||||
if (pal::file_exists(candidate))
|
||||
{
|
||||
trace::verbose(_X("adding native search path: %s"), candidate.c_str());
|
||||
output.append(candidate);
|
||||
output.push_back(PATH_SEPARATOR);
|
||||
items.insert(dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tpafile::add_package_dir(pal::string_t dir)
|
||||
{
|
||||
m_package_search_paths.push_back(dir);
|
||||
}
|
||||
|
||||
void tpafile::add_native_search_path(pal::string_t dir)
|
||||
{
|
||||
m_native_search_paths.push_back(dir);
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
bool coreclr_exists_in_dir(const pal::string_t& candidate)
|
||||
{
|
||||
pal::string_t test(candidate);
|
||||
append_path(test, LIBCORECLR_NAME);
|
||||
append_path(&test, LIBCORECLR_NAME);
|
||||
trace::verbose(_X("checking for CoreCLR in default location: %s"), test.c_str());
|
||||
return pal::file_exists(test);
|
||||
}
|
||||
|
@ -18,19 +18,19 @@ bool ends_with(const pal::string_t& value, const pal::string_t& suffix)
|
|||
(0 == value.compare(value.length() - suffix.length(), suffix.length(), suffix));
|
||||
}
|
||||
|
||||
void append_path(pal::string_t& path1, const pal::char_t* path2)
|
||||
void append_path(pal::string_t* path1, const pal::char_t* path2)
|
||||
{
|
||||
if (pal::is_path_rooted(path2))
|
||||
{
|
||||
path1.assign(path2);
|
||||
path1->assign(path2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path1.back() != DIR_SEPARATOR)
|
||||
if (path1->empty() || path1->back() != DIR_SEPARATOR)
|
||||
{
|
||||
path1.push_back(DIR_SEPARATOR);
|
||||
path1->push_back(DIR_SEPARATOR);
|
||||
}
|
||||
path1.append(path2);
|
||||
path1->append(path2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,3 +70,12 @@ pal::string_t get_directory(const pal::string_t& path)
|
|||
|
||||
return path.substr(0, path_sep);
|
||||
}
|
||||
|
||||
void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl)
|
||||
{
|
||||
int pos = 0;
|
||||
while ((pos = path->find(match, pos)) != pal::string_t::npos)
|
||||
{
|
||||
(*path)[pos] = repl;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue