Shared FX and Portable Support for Unified Host

- Add Casablanca CPP Rest SDK to corehost
    - Correct understanding of portable runtimeTargets section
    - Fix minor issues and automation for RID
    - CLI Build Integration
    - Add API to consume deps files
    - Unix doesn't like major as a variable name
    - Define NOMINMAX for Windows.h
    - Support APP_CONTEXT_DEPS_FILES
    - mscorlib.ni can come from native
    - Append Dotnet.dll to sdk path
    - Muxer vs standalone distinction based on own name.dll
This commit is contained in:
Senthil 2016-02-22 01:41:25 -08:00
parent 6bd9d80fed
commit f615f74a39
49 changed files with 14979 additions and 592 deletions

View file

@ -70,15 +70,20 @@ namespace Microsoft.DotNet.Cli.Build
var configuration = c.BuildContext.Get<string>("Configuration");
// Run the build
string version = DotNetCli.Stage0.Exec("", "--version").CaptureStdOut().Execute().StdOut;
string rid = Array.Find<string>(version.Split(Environment.NewLine.ToCharArray()), (e) => e.Contains("Runtime Id:")).Replace("Runtime Id:", "").Trim();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// Why does Windows directly call cmake but Linux/Mac calls "build.sh" in the corehost dir?
// See the comment in "src/corehost/build.sh" for details. It doesn't work for some reason.
var visualStudio = IsWinx86 ? "Visual Studio 14 2015" : "Visual Studio 14 2015 Win64";
var archMacro = IsWinx86 ? "-DCLI_CMAKE_PLATFORM_ARCH_I386=1" : "-DCLI_CMAKE_PLATFORM_ARCH_AMD64=1";
var ridMacro = $"-DCLI_CMAKE_RUNTIME_ID:STRING={rid}";
ExecIn(cmakeOut, "cmake",
Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost"),
archMacro,
ridMacro,
"-G",
visualStudio);
@ -101,14 +106,21 @@ namespace Microsoft.DotNet.Cli.Build
File.Copy(Path.Combine(cmakeOut, "cli", configuration, "corehost.pdb"), Path.Combine(Dirs.Corehost, "corehost.pdb"), overwrite: true);
File.Copy(Path.Combine(cmakeOut, "cli", "dll", configuration, "hostpolicy.dll"), Path.Combine(Dirs.Corehost, "hostpolicy.dll"), overwrite: true);
File.Copy(Path.Combine(cmakeOut, "cli", "dll", configuration, "hostpolicy.pdb"), Path.Combine(Dirs.Corehost, "hostpolicy.pdb"), overwrite: true);
File.Copy(Path.Combine(cmakeOut, "cli", "fxr", configuration, "hostfxr.dll"), Path.Combine(Dirs.Corehost, "hostfxr.dll"), overwrite: true);
File.Copy(Path.Combine(cmakeOut, "cli", "fxr", configuration, "hostfxr.pdb"), Path.Combine(Dirs.Corehost, "hostfxr.pdb"), overwrite: true);
}
else
{
ExecIn(cmakeOut, Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost", "build.sh"));
ExecIn(cmakeOut, Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost", "build.sh"),
"--arch",
"amd64",
"--rid",
rid);
// Copy the output out
File.Copy(Path.Combine(cmakeOut, "cli", "corehost"), Path.Combine(Dirs.Corehost, "corehost"), overwrite: true);
File.Copy(Path.Combine(cmakeOut, "cli", "dll", $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), overwrite: true);
File.Copy(Path.Combine(cmakeOut, "cli", "fxr", $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), overwrite: true);
}
return c.Success();
@ -193,6 +205,7 @@ namespace Microsoft.DotNet.Cli.Build
// Copy corehost
File.Copy(Path.Combine(Dirs.Corehost, $"corehost{Constants.ExeSuffix}"), Path.Combine(binDir, $"corehost{Constants.ExeSuffix}"), overwrite: true);
File.Copy(Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), Path.Combine(binDir, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), overwrite: true);
File.Copy(Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), Path.Combine(binDir, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), overwrite: true);
// Corehostify binaries
foreach (var binaryToCorehostify in BinariesForCoreHost)

View file

@ -98,7 +98,8 @@ namespace Microsoft.DotNet.Cli.Utils
if (depsFilePath != null)
{
arguments.Add($"--depsfile:{depsFilePath}");
arguments.Add("--depsfile");
arguments.Add(depsFilePath);
}
arguments.AddRange(commandArguments);

View file

@ -75,7 +75,7 @@ namespace Microsoft.DotNet.Cli.Utils
return null;
}
var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.Deps;
var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.DepsJson;
var dependencyLibraries = GetAllDependencyLibraries(projectContext);

View file

@ -37,7 +37,8 @@ namespace Microsoft.DotNet.Cli.Utils
public static readonly string HostExecutableName = "corehost" + ExeSuffix;
public static readonly string[] HostBinaryNames = new string[] {
HostExecutableName,
(CurrentPlatform == Platform.Windows ? "hostpolicy" : "libhostpolicy") + DynamicLibSuffix
(CurrentPlatform == Platform.Windows ? "hostpolicy" : "libhostpolicy") + DynamicLibSuffix,
(CurrentPlatform == Platform.Windows ? "hostfxr" : "libhostfxr") + DynamicLibSuffix
};
}

View file

@ -14,6 +14,46 @@ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
__build_arch=
__runtime_id=
while [ "$1" != "" ]; do
lowerI="$(echo $1 | awk '{print tolower($0)}')"
case $lowerI in
-h|--help)
usage
exit 1
;;
--arch)
shift
__build_arch=$1
;;
--rid)
shift
__runtime_id=$1
;;
*)
echo "Unknown argument to build.sh $1"; exit 1
esac
shift
done
__cmake_defines=
case $__build_arch in
amd64)
__define=-DCLI_CMAKE_PLATFORM_ARCH_AMD64=1
;;
x86)
__define=-DCLI_CMAKE_PLATFORM_ARCH_I386=1
;;
*)
echo "Unknown architecture $__build_arch"; exit 1
;;
esac
__cmake_defines="${__cmake_defines} ${__define}"
echo "Building Corehost from $DIR to $(pwd)"
cmake "$DIR" -G "Unix Makefiles"
cmake "$DIR" -G "Unix Makefiles" $__cmake_defines -DCLI_CMAKE_RUNTIME_ID:STRING=$__runtime_id
make

View file

@ -14,13 +14,26 @@ include(setup.cmake)
set (CMAKE_CXX_STANDARD 11)
include_directories(..)
include_directories(../common)
include_directories(.)
include_directories(./fxr)
include_directories(./json/casablanca/include)
# CMake does not recommend using globbing since it messes with the freshness checks
set(SOURCES
../corehost.cpp
libhost.cpp
#deps_format.cpp
#./json/casablanca/src/json/json.cpp
#./json/casablanca/src/json/json_parsing.cpp
#./json/casablanca/src/json/json_serialization.cpp
#./json/casablanca/src/utilities/asyncrt_utils.cpp
./fxr/fx_ver.cpp
../corehost.cpp
../policy_load.cpp
../common/trace.cpp
../common/utils.cpp)
@ -32,6 +45,9 @@ else()
endif()
add_executable(corehost ${SOURCES})
install(TARGETS corehost DESTINATION bin)
add_definitions(-D_NO_ASYNCRTIMP)
add_definitions(-D_NO_PPLXIMP)
# Older CMake doesn't support CMAKE_CXX_STANDARD and GCC/Clang need a switch to enable C++ 11
if(${CMAKE_CXX_COMPILER_ID} MATCHES "(Clang|GNU)")
@ -44,3 +60,4 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
endif()
add_subdirectory(dll)
add_subdirectory(fxr)

View file

@ -4,6 +4,7 @@
#include "args.h"
#include "utils.h"
#include "coreclr.h"
#include "libhost.h"
arguments_t::arguments_t() :
managed_application(_X("")),
@ -11,11 +12,8 @@ arguments_t::arguments_t() :
app_dir(_X("")),
app_argc(0),
app_argv(nullptr),
nuget_packages(_X("")),
dotnet_packages_cache(_X("")),
dotnet_servicing(_X("")),
dotnet_runtime_servicing(_X("")),
dotnet_home(_X("")),
deps_path(_X(""))
{
}
@ -26,12 +24,13 @@ void display_help()
_X("Usage: " HOST_EXE_NAME " [ASSEMBLY] [ARGUMENTS]\n")
_X("Execute the specified managed assembly with the passed in arguments\n\n")
_X("The Host's behavior can be altered using the following environment variables:\n")
_X(" DOTNET_HOME Set the dotnet home directory. The CLR is expected to be in the runtime subdirectory of this directory. Overrides all other values for CLR search paths\n")
_X(" COREHOST_TRACE Set to affect trace levels (0 = Errors only (default), 1 = Warnings, 2 = Info, 3 = Verbose)\n");
}
bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& args)
bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_dir, host_mode_t mode,
const int argc, const pal::char_t* argv[], arguments_t* arg_out)
{
arguments_t& args = *arg_out;
// Get the full name of the application
if (!pal::get_own_executable_path(&args.own_path) || !pal::realpath(&args.own_path))
{
@ -41,8 +40,8 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
auto own_name = get_filename(args.own_path);
auto own_dir = get_directory(args.own_path);
if (own_name.compare(HOST_EXE_NAME) == 0)
if (mode != host_mode_t::standalone)
{
// corerun mode. First argument is managed app
if (argc < 2)
@ -78,23 +77,26 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
args.app_argc = argc - 1;
}
if (args.app_argc > 0)
std::unordered_map<pal::string_t, pal::string_t> opts;
std::vector<pal::string_t> known_opts = { _X("--depsfile"), _X("--additionalprobingpath") };
int num_args = 0;
if (!parse_known_args(args.app_argc, args.app_argv, known_opts, &opts, &num_args))
{
auto depsfile_candidate = pal::string_t(args.app_argv[0]);
if (starts_with(depsfile_candidate, s_deps_arg_prefix, false))
{
args.deps_path = depsfile_candidate.substr(s_deps_arg_prefix.length());
if (!pal::realpath(&args.deps_path))
{
trace::error(_X("Failed to locate deps file: %s"), args.deps_path.c_str());
return false;
}
args.app_dir = get_directory(args.deps_path);
args.app_argc = args.app_argc - 1;
args.app_argv = &args.app_argv[1];
}
return false;
}
args.app_argc -= num_args;
args.app_argv += num_args;
pal::string_t deps_file = opts.count(_X("--depsfile")) ? opts[_X("--depsfile")] : deps_path;
pal::string_t probe_path = opts.count(_X("--additionalprobingpath")) ? opts[_X("--additionalprobingpath")] : probe_dir;
if (!deps_file.empty())
{
args.deps_path = deps_file;
args.app_dir = get_directory(args.deps_path);
}
args.probe_dir = probe_path;
if (args.deps_path.empty())
{
@ -105,13 +107,10 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
args.deps_path.append(app_base);
args.deps_path.push_back(DIR_SEPARATOR);
args.deps_path.append(app_name, 0, app_name.find_last_of(_X(".")));
args.deps_path.append(_X(".deps"));
args.deps_path.append(_X(".deps.json"));
}
pal::getenv(_X("NUGET_PACKAGES"), &args.nuget_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;
}

View file

@ -7,8 +7,7 @@
#include "utils.h"
#include "pal.h"
#include "trace.h"
static const pal::string_t s_deps_arg_prefix = _X("--depsfile:");
#include "libhost.h"
struct arguments_t
{
@ -18,7 +17,7 @@ struct arguments_t
pal::string_t dotnet_servicing;
pal::string_t dotnet_runtime_servicing;
pal::string_t dotnet_home;
pal::string_t nuget_packages;
pal::string_t probe_dir;
pal::string_t dotnet_packages_cache;
pal::string_t managed_application;
@ -28,6 +27,6 @@ struct arguments_t
arguments_t();
};
bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& args);
bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_dir, host_mode_t mode, const int argc, const pal::char_t* argv[], arguments_t* args);
#endif // ARGS_H

View file

@ -0,0 +1,144 @@
// 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 "pal.h"
#include "utils.h"
#include "deps_entry.h"
#include "trace.h"
// -----------------------------------------------------------------------------
// 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();
// Base directory must be present to obtain full path
if (base.empty())
{
return false;
}
// 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);
}

View file

@ -0,0 +1,38 @@
// 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_ENTRY_H_
#define __DEPS_ENTRY_H_
#include <iostream>
#include <vector>
#include "pal.h"
struct deps_entry_t
{
enum asset_types
{
runtime = 0,
resources,
native,
count
};
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;
};
#endif // __DEPS_ENTRY_H_

View file

@ -0,0 +1,305 @@
// 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 "deps_format.h"
#include "utils.h"
#include "trace.h"
#include <unordered_set>
#include <tuple>
#include <array>
#include <iterator>
#include <cassert>
#include <functional>
const std::array<const pal::char_t*, deps_entry_t::asset_types::count> deps_json_t::s_known_asset_types = {
_X("runtime"), _X("resources"), _X("native") };
const deps_entry_t& deps_json_t::try_ni(const deps_entry_t& entry) const
{
if (m_ni_entries.count(entry.asset_name))
{
int index = m_ni_entries.at(entry.asset_name);
return m_deps_entries[deps_entry_t::asset_types::runtime][index];
}
return entry;
}
void deps_json_t::reconcile_libraries_with_targets(
const json_value& json,
const std::function<bool(const pal::string_t&)>& library_exists_fn,
const std::function<const std::vector<pal::string_t>&(const pal::string_t&, int)>& get_rel_paths_by_asset_type_fn)
{
const auto& libraries = json.at(_X("libraries")).as_object();
for (const auto& library : libraries)
{
trace::info(_X("Reconciling library %s"), library.first.c_str());
if (pal::to_lower(library.second.at(_X("type")).as_string()) != _X("package"))
{
trace::info(_X("Library %s is not a package"), library.first.c_str());
continue;
}
if (!library_exists_fn(library.first))
{
trace::info(_X("Library %s does not exist"), library.first.c_str());
continue;
}
const auto& properties = library.second.as_object();
const pal::string_t& hash = properties.at(_X("sha512")).as_string();
bool serviceable = properties.at(_X("serviceable")).as_bool();
for (int i = 0; i < s_known_asset_types.size(); ++i)
{
for (const auto& rel_path : get_rel_paths_by_asset_type_fn(library.first, i))
{
bool ni_dll = false;
auto asset_name = get_filename_without_ext(rel_path);
if (ends_with(asset_name, _X(".ni"), false))
{
ni_dll = true;
asset_name = strip_file_ext(asset_name);
}
deps_entry_t entry;
size_t pos = library.first.find(_X("/"));
entry.library_name = library.first.substr(0, pos);
entry.library_version = library.first.substr(pos + 1);
entry.library_type = _X("package");
entry.library_hash = hash;
entry.asset_name = asset_name;
entry.asset_type = s_known_asset_types[i];
entry.relative_path = rel_path;
entry.is_serviceable = serviceable;
// TODO: Deps file does not follow spec. It uses '\\', should use '/'
replace_char(&entry.relative_path, _X('\\'), _X('/'));
m_deps_entries[i].push_back(entry);
if (ni_dll)
{
m_ni_entries[entry.asset_name] = m_deps_entries
[deps_entry_t::asset_types::runtime].size() - 1;
}
trace::info(_X("Added %s %s deps entry [%d] [%s, %s, %s]"), s_known_asset_types[i], entry.asset_name.c_str(), m_deps_entries[i].size() - 1, entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str());
if (i == deps_entry_t::asset_types::native &&
entry.asset_name == LIBCORECLR_FILENAME)
{
m_coreclr_index = m_deps_entries[i].size() - 1;
trace::verbose(_X("Found coreclr from deps %d [%s, %s, %s]"),
m_coreclr_index,
entry.library_name.c_str(),
entry.library_version.c_str(),
entry.relative_path.c_str());
}
}
}
}
}
pal::string_t get_own_rid()
{
#define _STRINGIFY(s) _X(s)
#if defined(TARGET_RUNTIME_ID)
return _STRINGIFY(TARGET_RUNTIME_ID);
#else
#error "Cannot build the host without knowing host's root RID"
#endif
}
bool deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph)
{
pal::string_t host_rid = get_own_rid();
for (auto& package : *portable_assets)
{
pal::string_t matched_rid = package.second.count(host_rid) ? host_rid : _X("");
if (matched_rid.empty())
{
if (rid_fallback_graph.count(host_rid) == 0)
{
trace::error(_X("Did not find fallback rids for package %s for the host rid %s"), package.first.c_str(), host_rid.c_str());
return false;
}
const auto& fallback_rids = rid_fallback_graph.find(host_rid)->second;
auto iter = std::find_if(fallback_rids.begin(), fallback_rids.end(), [&package](const pal::string_t& rid) {
return package.second.count(rid);
});
if (iter == fallback_rids.end() || (*iter).empty())
{
trace::error(_X("Did not find a matching fallback rid for package %s for the host rid %s"), package.first.c_str(), host_rid.c_str());
return false;
}
matched_rid = *iter;
}
assert(!matched_rid.empty());
for (auto iter = package.second.begin(); iter != package.second.end(); /* */)
{
iter = (iter->first != matched_rid)
? package.second.erase(iter)
: iter++;
}
}
return true;
}
bool deps_json_t::process_runtime_targets(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph, rid_specific_assets_t* p_assets)
{
rid_specific_assets_t& assets = *p_assets;
for (const auto& package : json.at(_X("targets")).at(target_name).as_object())
{
const auto& targets = package.second.as_object();
auto iter = targets.find(_X("runtimeTargets"));
if (iter == targets.end())
{
continue;
}
const auto& files = iter->second.as_object();
for (const auto& file : files)
{
const auto& type = file.second.at(_X("assetType")).as_string();
for (int i = 0; i < s_known_asset_types.size(); ++i)
{
if (pal::strcasecmp(type.c_str(), s_known_asset_types[i]) == 0)
{
const auto& rid = file.second.at(_X("rid")).as_string();
assets[package.first][rid][i].push_back(file.first);
}
}
}
}
if (!perform_rid_fallback(&assets, rid_fallback_graph))
{
return false;
}
return true;
}
bool deps_json_t::process_targets(const json_value& json, const pal::string_t& target_name, deps_assets_t* p_assets)
{
deps_assets_t& assets = *p_assets;
for (const auto& package : json.at(_X("targets")).at(target_name).as_object())
{
// if (package.second.at(_X("type")).as_string() != _X("package")) continue;
const auto& asset_types = package.second.as_object();
for (int i = 0; i < s_known_asset_types.size(); ++i)
{
auto iter = asset_types.find(s_known_asset_types[i]);
if (iter != asset_types.end())
{
for (const auto& file : iter->second.as_object())
{
trace::info(_X("Adding %s asset %s from %s"), s_known_asset_types[i], file.first.c_str(), package.first.c_str());
assets[package.first][i].push_back(file.first);
}
}
}
}
return true;
}
bool deps_json_t::load_portable(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph)
{
rid_specific_assets_t rid_assets;
if (!process_runtime_targets(json, target_name, rid_fallback_graph, &rid_assets))
{
return false;
}
deps_assets_t non_rid_assets;
if (!process_targets(json, target_name, &non_rid_assets))
{
return false;
}
auto package_exists = [&rid_assets, &non_rid_assets](const pal::string_t& package) -> bool {
return rid_assets.count(package) || non_rid_assets.count(package);
};
auto get_relpaths = [&rid_assets, &non_rid_assets](const pal::string_t& package, int type_index) -> const std::vector<pal::string_t>& {
return (rid_assets.count(package))
? rid_assets[package].begin()->second[type_index]
: non_rid_assets[package][type_index];
};
reconcile_libraries_with_targets(json, package_exists, get_relpaths);
return true;
}
bool deps_json_t::load_standalone(const json_value& json, const pal::string_t& target_name)
{
deps_assets_t assets;
if (!process_targets(json, target_name, &assets))
{
return false;
}
auto package_exists = [&assets](const pal::string_t& package) -> bool {
return assets.count(package);
};
auto get_relpaths = [&assets](const pal::string_t& package, int type_index) -> const std::vector<pal::string_t>& {
return assets[package][type_index];
};
reconcile_libraries_with_targets(json, package_exists, get_relpaths);
const auto& json_object = json.as_object();
const auto iter = json_object.find(_X("runtimes"));
if (iter != json_object.end())
{
for (const auto& rid : iter->second.as_object())
{
auto& vec = m_rid_fallback_graph[rid.first];
for (const auto& fallback : rid.second.as_array())
{
vec.push_back(fallback.as_string());
}
}
}
return true;
}
// -----------------------------------------------------------------------------
// Load the deps file and parse its "entry" lines which contain the "fields" of
// the entry. Populate an array of these entries.
//
bool deps_json_t::load(bool portable, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph)
{
// If file doesn't exist, then assume parsed.
if (!pal::file_exists(deps_path))
{
return true;
}
// Somehow the file stream could not be opened. This is an error.
pal::ifstream_t file(deps_path);
if (!file.good())
{
return false;
}
try
{
const auto json = json_value::parse(file);
const auto& runtime_target = json.at(_X("runtimeTarget"));
const pal::string_t& name = runtime_target.as_string();
return (portable) ? load_portable(json, name, rid_fallback_graph) : load_standalone(json, name);
}
catch (...)
{
return false;
}
}

View file

@ -0,0 +1,113 @@
// 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_FORMAT_H_
#define __DEPS_FORMAT_H_
#include <iostream>
#include <vector>
#include <functional>
#include "pal.h"
#include "deps_entry.h"
#include "cpprest/json.h"
class deps_json_t
{
typedef web::json::value json_value;
typedef std::array<std::vector<pal::string_t>, deps_entry_t::asset_types::count> vectors_t;
typedef std::unordered_map<pal::string_t, vectors_t> str_to_vectors_map_t;
typedef std::unordered_map<pal::string_t, std::vector<pal::string_t>> str_to_vector_map_t;
typedef str_to_vector_map_t rid_fallback_graph_t;
typedef str_to_vectors_map_t deps_assets_t;
typedef std::unordered_map<pal::string_t, str_to_vectors_map_t> rid_specific_assets_t;
public:
deps_json_t()
: m_valid(false)
, m_coreclr_index(-1)
{
}
deps_json_t(bool portable, const pal::string_t& deps_path)
: deps_json_t(portable, deps_path, m_rid_fallback_graph /* dummy */)
{
}
deps_json_t(bool portable, const pal::string_t& deps_path, const rid_fallback_graph_t& graph)
: deps_json_t()
{
m_valid = load(portable, deps_path, graph);
}
const std::vector<deps_entry_t>& get_entries(deps_entry_t::asset_types type)
{
assert(type < deps_entry_t::asset_types::count);
return m_deps_entries[type];
}
bool has_coreclr_entry()
{
return m_coreclr_index >= 0;
}
const deps_entry_t& get_coreclr_entry()
{
assert(has_coreclr_entry());
return m_deps_entries[deps_entry_t::asset_types::native][m_coreclr_index];
}
bool is_valid()
{
return m_valid;
}
const rid_fallback_graph_t& get_rid_fallback_graph()
{
return m_rid_fallback_graph;
}
const deps_entry_t& try_ni(const deps_entry_t& entry) const;
private:
bool load_standalone(const json_value& json, const pal::string_t& target_name);
bool load_portable(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph);
bool load(bool portable, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph);
bool process_runtime_targets(const json_value& json, const pal::string_t& target_name, const rid_fallback_graph_t& rid_fallback_graph, rid_specific_assets_t* p_assets);
bool process_targets(const json_value& json, const pal::string_t& target_name, deps_assets_t* p_assets);
void reconcile_libraries_with_targets(
const json_value& json,
const std::function<bool(const pal::string_t&)>& library_exists_fn,
const std::function<const std::vector<pal::string_t>&(const pal::string_t&, int)>& get_rel_paths_by_asset_type_fn);
bool perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph);
static const std::array<const pal::char_t*, deps_entry_t::asset_types::count> s_known_asset_types;
std::vector<deps_entry_t> m_deps_entries[deps_entry_t::asset_types::count];
std::unordered_map<pal::string_t, int> m_ni_entries;
rid_fallback_graph_t m_rid_fallback_graph;
int m_coreclr_index;
bool m_valid;
};
class deps_text_t
{
public:
deps_text_t(const pal::string_t& deps_path)
: m_valid(load(deps_path))
{
}
bool load(const pal::string_t& deps_path);
bool is_valid() { return m_valid; }
const std::vector<deps_entry_t>& get_entries() { return m_deps_entries; }
private:
std::vector<deps_entry_t> m_deps_entries;
bool m_valid;
};
#endif // __DEPS_FORMAT_H_

View file

@ -6,80 +6,12 @@
#include <cassert>
#include "trace.h"
#include "deps_format.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.
@ -106,28 +38,6 @@ void add_tpa_asset(
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_path, items, output);
return;
}
}
// -----------------------------------------------------------------------------
// A uniqifying append helper that doesn't let two "paths" to be identical in
// the "output" string.
@ -157,234 +67,16 @@ void add_unique_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);
trace::verbose(_X("Added deps entry [%d] [%s, %s, %s]"), m_deps_entries.size() - 1, entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str());
static_assert(std::is_same<std::vector<deps_entry_t>, decltype(m_deps_entries)>::value, "decltype(m_deps_entries) not a vector, took index based on size.");
if (entry.asset_type == _X("native") &&
entry.asset_name == LIBCORECLR_FILENAME)
{
m_coreclr_index = m_deps_entries.size() - 1;
trace::verbose(_X("Found coreclr from deps entry [%d] [%s, %s, %s]"),
m_coreclr_index,
entry.library_name.c_str(),
entry.library_version.c_str(),
entry.relative_path.c_str());
}
}
return true;
}
// -----------------------------------------------------------------------------
// 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)
{
m_deps_path = args.deps_path;
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)
void deps_resolver_t::get_dir_assemblies(
const pal::string_t& dir,
const pal::string_t& dir_name,
std::unordered_map<pal::string_t, pal::string_t>* dir_assemblies)
{
trace::verbose(_X("Adding files from dir %s"), dir.c_str());
trace::verbose(_X("Adding files from %s dir %s"), dir_name.c_str(), 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") };
@ -412,18 +104,17 @@ void deps_resolver_t::get_local_assemblies(const pal::string_t& dir)
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))
if (dir_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());
trace::verbose(_X("Skipping %s because the %s already exists in %s assemblies"), file.c_str(), dir_assemblies->find(file_name)->second.c_str(), dir_name.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);
trace::verbose(_X("Adding %s to %s assembly set from %s"), file_name.c_str(), dir_name.c_str(), file_path.c_str());
dir_assemblies->emplace(file_name, file_path);
}
}
}
@ -440,43 +131,93 @@ pal::string_t deps_resolver_t::resolve_coreclr_dir(
const pal::string_t& package_dir,
const pal::string_t& package_cache_dir)
{
// Runtime servicing
trace::verbose(_X("Probing for CoreCLR in servicing dir=[%s]"), m_runtime_svc.c_str());
if (!m_runtime_svc.empty())
auto process_coreclr = [&]
(bool is_portable, const pal::string_t& deps_dir, deps_json_t* deps) -> pal::string_t
{
pal::string_t svc_clr = m_runtime_svc;
append_path(&svc_clr, _X("runtime"));
append_path(&svc_clr, _X("coreclr"));
pal::string_t candidate;
if (coreclr_exists_in_dir(svc_clr))
// Servicing override.
if (deps->has_coreclr_entry())
{
return svc_clr;
const deps_entry_t& entry = deps->get_coreclr_entry();
trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in servicing"), entry.library_name.c_str(), entry.library_version.c_str());
if (entry.is_serviceable && m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &candidate))
{
return get_directory(candidate);
}
}
else
{
trace::verbose(_X("Deps has no coreclr entry."));
}
}
// Package cache.
trace::verbose(_X("Probing for CoreCLR in package cache=[%s] deps index: [%d]"), package_cache_dir.c_str(), m_coreclr_index);
pal::string_t coreclr_cache;
if (m_coreclr_index >= 0 && !package_cache_dir.empty() &&
m_deps_entries[m_coreclr_index].to_hash_matched_path(package_cache_dir, &coreclr_cache))
{
return get_directory(coreclr_cache);
}
// Package cache.
pal::string_t coreclr_cache;
if (!package_cache_dir.empty())
{
if (deps->has_coreclr_entry())
{
const deps_entry_t& entry = deps->get_coreclr_entry();
trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in package cache=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), package_cache_dir.c_str());
if (entry.to_hash_matched_path(package_cache_dir, &coreclr_cache))
{
return get_directory(coreclr_cache);
}
}
}
// App dir.
trace::verbose(_X("Probing for CoreCLR in app directory=[%s]"), app_dir.c_str());
if (coreclr_exists_in_dir(app_dir))
{
return app_dir;
}
// Deps directory: lookup relative path if portable, else look sxs.
if (is_portable)
{
pal::string_t coreclr_portable;
if (deps->has_coreclr_entry())
{
const deps_entry_t& entry = deps->get_coreclr_entry();
trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in portable app dir=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), deps_dir.c_str());
if (entry.to_full_path(deps_dir, &coreclr_portable))
{
return get_directory(coreclr_portable);
}
}
}
else
{
// App main dir or standalone app dir.
trace::verbose(_X("Probing for CoreCLR in deps directory=[%s]"), deps_dir.c_str());
if (coreclr_exists_in_dir(deps_dir))
{
return deps_dir;
}
}
// Packages dir
trace::verbose(_X("Probing for CoreCLR in packages=[%s] deps index: [%d]"), package_dir.c_str(), m_coreclr_index);
pal::string_t coreclr_package;
if (m_coreclr_index >= 0 && !package_dir.empty() &&
m_deps_entries[m_coreclr_index].to_full_path(package_dir, &coreclr_package))
// Packages dir.
pal::string_t coreclr_package;
if (!package_dir.empty())
{
if (deps->has_coreclr_entry())
{
const deps_entry_t& entry = deps->get_coreclr_entry();
trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in packages dir=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), package_dir.c_str());
if (entry.to_full_path(package_dir, &coreclr_package))
{
return get_directory(coreclr_package);
}
}
}
return pal::string_t();
};
trace::info(_X("--- Starting CoreCLR Proble from app deps.json"));
pal::string_t clr_dir = process_coreclr(m_portable, app_dir, m_deps.get());
if (clr_dir.empty() && m_portable)
{
return get_directory(coreclr_package);
trace::info(_X("--- Starting CoreCLR Proble from FX deps.json"));
clr_dir = process_coreclr(false, m_fx_dir, m_fx_deps.get());
}
if (!clr_dir.empty())
{
return clr_dir;
}
// Use platform-specific search algorithm
@ -516,19 +257,33 @@ void deps_resolver_t::resolve_tpa_list(
const pal::string_t& clr_dir,
pal::string_t* output)
{
std::vector<deps_entry_t> empty(0);
pal::string_t ni_package_cache_dir;
if (!package_cache_dir.empty())
{
ni_package_cache_dir = package_cache_dir;
append_path(&ni_package_cache_dir, get_arch());
}
// Obtain the local assemblies in the app dir.
get_local_assemblies(app_dir);
if (m_portable)
{
get_dir_assemblies(m_fx_dir, _X("fx"), &m_sxs_assemblies);
}
else
{
get_dir_assemblies(app_dir, _X("local"), &m_sxs_assemblies);
}
std::set<pal::string_t> items;
add_mscorlib_to_tpa(clr_dir, &items, output);
for (const deps_entry_t& entry : m_deps_entries)
auto process_entry = [&](bool is_portable, deps_json_t* deps, const deps_entry_t& entry)
{
// Is this asset a "runtime" type?
if (entry.asset_type != _X("runtime") || items.count(entry.asset_name))
if (items.count(entry.asset_name))
{
continue;
return;
}
pal::string_t candidate;
@ -539,45 +294,63 @@ void deps_resolver_t::resolve_tpa_list(
{
add_tpa_asset(entry.asset_name, candidate, &items, output);
}
// Is this entry present in the secondary package cache?
// Is an NI image for this entry present in the secondary package cache?
else if (entry.to_hash_matched_path(ni_package_cache_dir, &candidate))
{
add_tpa_asset(entry.asset_name, candidate, &items, output);
}
// Is this entry present in the secondary package cache? (note: no .ni extension)
else if (entry.to_hash_matched_path(package_cache_dir, &candidate))
{
add_tpa_asset(entry.asset_name, candidate, &items, output);
}
// Is this entry present locally?
else if (m_local_assemblies.count(entry.asset_name))
else if (!is_portable && m_sxs_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);
add_tpa_asset(entry.asset_name, m_sxs_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))
// The app is portable so the asset should be picked up from relative subpath.
else if (is_portable && deps->try_ni(entry).to_full_path(app_dir, &candidate))
{
add_tpa_asset(entry.asset_name, candidate, &items, output);
}
}
// Is this entry present in the package restore dir?
else if (!package_dir.empty() && deps->try_ni(entry).to_full_path(package_dir, &candidate))
{
add_tpa_asset(entry.asset_name, candidate, &items, output);
}
};
const auto& deps_entries = m_deps->get_entries(deps_entry_t::asset_types::runtime);
std::for_each(deps_entries.begin(), deps_entries.end(), [&](const deps_entry_t& entry) {
process_entry(m_portable, m_deps.get(), entry);
});
const auto& fx_entries = m_portable ? m_fx_deps->get_entries(deps_entry_t::asset_types::runtime) : empty;
std::for_each(fx_entries.begin(), fx_entries.end(), [&](const deps_entry_t& entry) {
process_entry(false, m_fx_deps.get(), entry);
});
// 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)
for (const auto& kv : m_sxs_assemblies)
{
add_tpa_asset(kv.first, kv.second, &items, output);
}
}
// -----------------------------------------------------------------------------
// Resolve the directories order for culture/native lookup
// Resolve the directories order for resources/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
// for both native images and resources specific resource images. Lookup for
// resources 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"
// supports "resources" 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
@ -594,60 +367,89 @@ void deps_resolver_t::resolve_probe_dirs(
const pal::string_t& clr_dir,
pal::string_t* output)
{
assert(asset_type == _X("culture") || asset_type == _X("native"));
assert(asset_type == _X("resources") || asset_type == _X("native"));
// For culture assemblies, we need to provide the base directory of the culture path.
// For resources assemblies, we need to provide the base directory of the resources 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) {
std::function<pal::string_t(const pal::string_t&)> resources = [] (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::function<pal::string_t(const pal::string_t&)>& action = (asset_type == _X("resources")) ? resources : native;
deps_entry_t::asset_types entry_type = (asset_type == _X("resources")) ? deps_entry_t::asset_types::resources : deps_entry_t::asset_types::native;
std::set<pal::string_t> items;
std::vector<deps_entry_t> empty(0);
const auto& entries = m_deps->get_entries(entry_type);
const auto& fx_entries = m_portable ? m_fx_deps->get_entries(entry_type) : empty;
// 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)
auto add_serviced_entry = [&](const deps_entry_t& entry)
{
pal::string_t redirection_path;
if (entry.is_serviceable && entry.asset_type == asset_type && entry.library_type == _X("Package") &&
if (entry.is_serviceable && 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);
}
}
};
std::for_each(entries.begin(), entries.end(), add_serviced_entry);
std::for_each(fx_entries.begin(), fx_entries.end(), add_serviced_entry);
pal::string_t candidate;
// Take care of the secondary cache path
if (!package_cache_dir.empty())
{
for (const deps_entry_t& entry : m_deps_entries)
auto add_package_cache_entry = [&](const deps_entry_t& entry)
{
if (entry.asset_type == asset_type && entry.to_hash_matched_path(package_cache_dir, &candidate))
if (entry.to_hash_matched_path(package_cache_dir, &candidate))
{
add_unique_path(asset_type, action(candidate), &items, output);
}
}
};
std::for_each(entries.begin(), entries.end(), add_package_cache_entry);
std::for_each(fx_entries.begin(), fx_entries.end(), add_package_cache_entry);
}
// App local path
add_unique_path(asset_type, app_dir, &items, output);
// Take care of the package restore path
if (!package_dir.empty())
// For portable path, the app relative directory must be used.
if (m_portable)
{
for (const deps_entry_t& entry : m_deps_entries)
std::for_each(entries.begin(), entries.end(), [&](const deps_entry_t& entry)
{
if (entry.asset_type == asset_type && entry.to_full_path(package_dir, &candidate))
{
add_unique_path(asset_type, action(candidate), &items, output);
}
}
});
}
// FX path if present
if (!m_fx_dir.empty())
{
add_unique_path(asset_type, m_fx_dir, &items, output);
}
// Take care of the package restore path
if (!package_dir.empty())
{
auto add_packages_entry = [&](const deps_entry_t& entry)
{
if (entry.asset_type == asset_type && entry.to_full_path(package_dir, &candidate))
{
add_unique_path(asset_type, action(candidate), &items, output);
}
};
std::for_each(entries.begin(), entries.end(), add_packages_entry);
std::for_each(fx_entries.begin(), fx_entries.end(), add_packages_entry);
}
// CLR path
@ -656,7 +458,7 @@ void deps_resolver_t::resolve_probe_dirs(
// -----------------------------------------------------------------------------
// Entrypoint to resolve TPA, native and culture path ordering to pass to CoreCLR.
// Entrypoint to resolve TPA, native and resources path ordering to pass to CoreCLR.
//
// Parameters:
// app_dir - The application local directory
@ -676,6 +478,6 @@ bool deps_resolver_t::resolve_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);
resolve_probe_dirs(_X("resources"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->resources);
return true;
}

View file

@ -8,48 +8,45 @@
#include "pal.h"
#include "trace.h"
#include "deps_format.h"
#include "deps_entry.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;
};
#include "runtime_config.h"
// Probe paths to be resolved for ordering
struct probe_paths_t
{
pal::string_t tpa;
pal::string_t native;
pal::string_t culture;
pal::string_t resources;
};
class deps_resolver_t
{
public:
deps_resolver_t(const arguments_t& args)
deps_resolver_t(const pal::string_t& fx_dir, const runtime_config_t* config, const arguments_t& args)
: m_svc(args.dotnet_servicing)
, m_runtime_svc(args.dotnet_runtime_servicing)
, m_fx_dir(fx_dir)
, m_coreclr_index(-1)
, m_portable(config->get_portable())
, m_deps(nullptr)
, m_fx_deps(nullptr)
{
m_deps_valid = parse_deps_file(args);
m_deps_file = args.deps_path;
if (m_portable)
{
m_fx_deps_file = get_fx_deps(fx_dir, config->get_fx_name());
m_fx_deps = std::unique_ptr<deps_json_t>(new deps_json_t(false, m_fx_deps_file));
m_deps = std::unique_ptr<deps_json_t>(new deps_json_t(true, m_deps_file, m_fx_deps->get_rid_fallback_graph()));
}
else
{
m_deps = std::unique_ptr<deps_json_t>(new deps_json_t(false, m_deps_file));
}
}
bool valid() { return m_deps_valid; }
bool valid() { return m_deps->is_valid() && (!m_portable || m_fx_deps->is_valid()); }
bool resolve_probe_paths(
const pal::string_t& app_dir,
@ -63,11 +60,24 @@ public:
const pal::string_t& package_dir,
const pal::string_t& package_cache_dir);
const pal::string_t& get_fx_deps_file() const
{
return m_fx_deps_file;
}
const pal::string_t& get_deps_file() const
{
return m_deps_file;
}
private:
bool load();
bool parse_deps_file(const arguments_t& args);
static pal::string_t get_fx_deps(const pal::string_t& fx_dir, const pal::string_t& fx_name)
{
pal::string_t fx_deps = fx_dir;
pal::string_t fx_deps_name = pal::to_lower(fx_name) + _X(".deps.json");
append_path(&fx_deps, fx_deps_name.c_str());
return fx_deps;
}
// Resolve order for TPA lookup.
void resolve_tpa_list(
@ -86,30 +96,42 @@ private:
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);
// Populate assemblies from the directory.
void get_dir_assemblies(
const pal::string_t& dir,
const pal::string_t& dir_name,
std::unordered_map<pal::string_t, pal::string_t>* dir_assemblies);
// Servicing index to resolve serviced assembly paths.
servicing_index_t m_svc;
// Runtime servicing directory.
pal::string_t m_runtime_svc;
// Framework deps file.
pal::string_t m_fx_dir;
// 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;
// Map of simple name -> full path of local/fx assemblies populated
// in priority order of their extensions.
std::unordered_map<pal::string_t, pal::string_t> m_sxs_assemblies;
// Special entry for coreclr in the deps entries
int m_coreclr_index;
// The dep file path
pal::string_t m_deps_path;
// The filepath for the app deps
pal::string_t m_deps_file;
// The filepath for the fx deps
pal::string_t m_fx_deps_file;
// Deps files for the fx
std::unique_ptr<deps_json_t> m_fx_deps;
// Deps files for the app
std::unique_ptr<deps_json_t> m_deps;
// Is the deps file valid
bool m_deps_valid;
// Is the deps file portable app?
bool m_portable;
};
#endif // DEPS_RESOLVER_H

View file

@ -13,16 +13,24 @@ endif()
include(../setup.cmake)
include_directories(../../common)
include_directories(../json/casablanca/include)
# CMake does not recommend using globbing since it messes with the freshness checks
set(SOURCES
../../common/trace.cpp
../../common/utils.cpp
../libhost.cpp
../runtime_config.cpp
../json/casablanca/src/json/json.cpp
../json/casablanca/src/json/json_parsing.cpp
../json/casablanca/src/json/json_serialization.cpp
../json/casablanca/src/utilities/asyncrt_utils.cpp
../args.cpp
../hostpolicy.cpp
../coreclr.cpp
../deps_resolver.cpp
../deps_format.cpp
../deps_entry.cpp
../servicing_index.cpp)
@ -32,6 +40,8 @@ else()
list(APPEND SOURCES ../../common/pal.unix.cpp)
endif()
add_definitions(-D_NO_ASYNCRTIMP)
add_definitions(-D_NO_PPLXIMP)
add_definitions(-DCOREHOST_MAKE_DLL=1)
add_library(hostpolicy SHARED ${SOURCES})

View file

@ -0,0 +1,45 @@
# 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.
cmake_minimum_required (VERSION 2.6)
project(hostpolicy)
if(WIN32)
add_compile_options($<$<CONFIG:RelWithDebInfo>:/MT>)
add_compile_options($<$<CONFIG:Release>:/MT>)
add_compile_options($<$<CONFIG:Debug>:/MTd>)
endif()
include(../setup.cmake)
include_directories(../../common)
include_directories(../json/casablanca/include)
# CMake does not recommend using globbing since it messes with the freshness checks
set(SOURCES
../../common/trace.cpp
../../common/utils.cpp
../../policy_load.cpp
../libhost.cpp
../runtime_config.cpp
../json/casablanca/src/json/json.cpp
../json/casablanca/src/json/json_parsing.cpp
../json/casablanca/src/json/json_serialization.cpp
../json/casablanca/src/utilities/asyncrt_utils.cpp
./fx_ver.cpp
./fx_muxer.cpp)
if(WIN32)
list(APPEND SOURCES ../../common/pal.windows.cpp)
else()
list(APPEND SOURCES ../../common/pal.unix.cpp)
endif()
add_definitions(-D_NO_ASYNCRTIMP)
add_definitions(-D_NO_PPLXIMP)
add_definitions(-DCOREHOST_MAKE_DLL=1)
add_library(hostfxr SHARED ${SOURCES})

View file

@ -0,0 +1,298 @@
// 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 <cassert>
#include "pal.h"
#include "utils.h"
#include "libhost.h"
#include "args.h"
#include "fx_ver.h"
#include "fx_muxer.h"
#include "trace.h"
#include "runtime_config.h"
#include "cpprest/json.h"
#include "corehost.h"
#include "policy_load.h"
typedef web::json::value json_value;
pal::string_t fx_muxer_t::resolve_fx_dir(const pal::string_t& muxer_dir, runtime_config_t* runtime, const pal::string_t& app_path)
{
const auto fx_name = runtime->get_fx_name();
const auto fx_ver = runtime->get_fx_version();
const auto roll_fwd = runtime->get_fx_roll_fwd();
fx_ver_t specified(-1, -1, -1);
if (!fx_ver_t::parse(fx_ver, &specified, false))
{
return pal::string_t();
}
auto fx_dir = muxer_dir;
append_path(&fx_dir, _X("Shared"));
append_path(&fx_dir, fx_name.c_str());
// If not roll forward or if pre-release, just return.
if (!roll_fwd || specified.is_prerelease())
{
append_path(&fx_dir, fx_ver.c_str());
}
else
{
std::vector<pal::string_t> list;
pal::readdir(fx_dir, &list);
fx_ver_t max_specified = specified;
for (const auto& version : list)
{
fx_ver_t ver(-1, -1, -1);
if (fx_ver_t::parse(version, &ver, true) &&
ver.get_major() == max_specified.get_major() &&
ver.get_minor() == max_specified.get_minor())
{
max_specified.set_patch(std::max(ver.get_patch(), max_specified.get_patch()));
}
}
pal::string_t max_specified_str = max_specified.as_str();
append_path(&fx_dir, max_specified_str.c_str());
}
trace::verbose(_X("Found fx in: %s"), fx_dir.c_str());
return pal::directory_exists(fx_dir) ? fx_dir : pal::string_t();
}
pal::string_t fx_muxer_t::resolve_cli_version(const pal::string_t& global_json)
{
pal::string_t retval;
if (!pal::file_exists(global_json))
{
return retval;
}
pal::ifstream_t file(global_json);
if (!file.good())
{
return retval;
}
try
{
const auto root = json_value::parse(file);
const auto& json = root.as_object();
const auto sdk_iter = json.find(_X("sdk"));
if (sdk_iter == json.end() || sdk_iter->second.is_null())
{
return retval;
}
const auto& sdk_obj = sdk_iter->second.as_object();
const auto ver_iter = sdk_obj.find(_X("version"));
if (ver_iter == sdk_obj.end() || ver_iter->second.is_null())
{
return retval;
}
retval = ver_iter->second.as_string();
}
catch (...)
{
}
trace::verbose(_X("Found cli in: %s"), retval.c_str());
return retval;
}
bool fx_muxer_t::resolve_sdk_dotnet_path(const pal::string_t& own_dir, pal::string_t* cli_sdk)
{
pal::string_t cwd;
pal::string_t global;
if (pal::getcwd(&cwd))
{
for (pal::string_t parent_dir, cur_dir = cwd; true; cur_dir = parent_dir)
{
pal::string_t file = cur_dir;
append_path(&file, _X("global.json"));
if (pal::file_exists(file))
{
global = file;
break;
}
parent_dir = get_directory(cur_dir);
if (parent_dir.empty() || parent_dir.size() == cur_dir.size())
{
break;
}
}
}
pal::string_t retval;
if (!global.empty())
{
pal::string_t cli_version = resolve_cli_version(global);
if (!cli_version.empty())
{
pal::string_t sdk_path = own_dir;
append_path(&sdk_path, _X("sdk"));
append_path(&sdk_path, cli_version.c_str());
if (pal::directory_exists(sdk_path))
{
retval = sdk_path;
}
}
}
if (retval.empty())
{
pal::string_t sdk_path = own_dir;
append_path(&sdk_path, _X("sdk"));
std::vector<pal::string_t> versions;
pal::readdir(sdk_path, &versions);
fx_ver_t max_ver(-1, -1, -1);
for (const auto& version : versions)
{
fx_ver_t ver(-1, -1, -1);
if (fx_ver_t::parse(version, &ver, true))
{
max_ver = std::max(ver, max_ver);
}
}
pal::string_t max_ver_str = max_ver.as_str();
append_path(&sdk_path, max_ver_str.c_str());
if (pal::directory_exists(sdk_path))
{
retval = sdk_path;
}
}
cli_sdk->assign(retval);
trace::verbose(_X("Found cli sdk in: %s"), cli_sdk->c_str());
return !retval.empty();
}
/* static */
int fx_muxer_t::execute(const int argc, const pal::char_t* argv[])
{
pal::string_t own_path;
// Get the full name of the application
if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path))
{
trace::error(_X("Failed to locate current executable"));
return StatusCode::LibHostCurExeFindFailure;
}
auto own_dir = get_directory(own_path);
if (argc <= 1)
{
return StatusCode::InvalidArgFailure;
}
if (ends_with(argv[1], _X(".dll"), false))
{
pal::string_t app_path = argv[1];
if (!pal::realpath(&app_path))
{
return StatusCode::LibHostExecModeFailure;
}
runtime_config_t config(get_runtime_config_json(app_path));
if (!config.is_valid())
{
trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str());
return StatusCode::InvalidConfigFile;
}
if (config.get_portable())
{
pal::string_t fx_dir = resolve_fx_dir(own_dir, &config, app_path);
corehost_init_t init(_X(""), _X(""), fx_dir, host_mode_t::muxer, &config);
return policy_load_t::execute_app(fx_dir, &init, argc, argv);
}
else
{
corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::muxer, &config);
return policy_load_t::execute_app(get_directory(app_path), &init, argc, argv);
}
}
else
{
if (pal::strcasecmp(_X("exec"), argv[1]) == 0)
{
std::vector<pal::string_t> known_opts = { _X("--depsfile"), _X("--additionalprobingpath") };
int num_args = 0;
std::unordered_map<pal::string_t, pal::string_t> opts;
if (!parse_known_args(argc - 2, &argv[2], known_opts, &opts, &num_args))
{
return InvalidArgFailure;
}
int cur_i = 2 + num_args;
if (cur_i >= argc)
{
return InvalidArgFailure;
}
// Transform dotnet exec [--additionalprobingpath path] [--depsfile file] dll [args] -> dotnet dll [args]
std::vector<const pal::char_t*> new_argv(argc - cur_i + 1); // +1 for dotnet
memcpy(new_argv.data() + 1, argv + cur_i, (argc - cur_i) * sizeof(pal::char_t*));
new_argv[0] = argv[0];
pal::string_t deps_file = opts.count(_X("--depsfile")) ? opts[_X("--depsfile")] : _X("");
pal::string_t probe_path = opts.count(_X("--additionalprobingpath")) ? opts[_X("--additionalprobingpath")] : _X("");
pal::string_t app_path = argv[cur_i];
runtime_config_t config(get_runtime_config_json(app_path));
if (!config.is_valid())
{
trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str());
return StatusCode::InvalidConfigFile;
}
if (config.get_portable())
{
pal::string_t fx_dir = resolve_fx_dir(own_dir, &config, app_path);
corehost_init_t init(deps_file, probe_path, fx_dir, host_mode_t::muxer, &config);
return policy_load_t::execute_app(fx_dir, &init, new_argv.size(), new_argv.data());
}
else
{
corehost_init_t init(deps_file, probe_path, _X(""), host_mode_t::muxer, &config);
pal::string_t impl_dir = get_directory(deps_file.empty() ? app_path : deps_file);
return policy_load_t::execute_app(impl_dir, &init, new_argv.size(), new_argv.data());
}
}
else
{
pal::string_t sdk_dotnet;
if (!resolve_sdk_dotnet_path(own_dir, &sdk_dotnet))
{
return StatusCode::LibHostSdkFindFailure;
}
append_path(&sdk_dotnet, _X("dotnet.dll"));
// Transform dotnet [command] [args] -> dotnet [dotnet.dll] [command] [args]
std::vector<const pal::char_t*> new_argv(argc + 1);
memcpy(&new_argv.data()[2], argv + 1, (argc - 1) * sizeof(pal::char_t*));
new_argv[0] = argv[0];
new_argv[1] = sdk_dotnet.c_str();
trace::verbose(_X("Using SDK dll=[%s]"), sdk_dotnet.c_str());
assert(ends_with(sdk_dotnet, _X(".dll"), false));
runtime_config_t config(get_runtime_config_json(sdk_dotnet));
if (config.get_portable())
{
pal::string_t fx_dir = resolve_fx_dir(own_dir, &config, sdk_dotnet);
corehost_init_t init(_X(""), _X(""), fx_dir, host_mode_t::muxer, &config);
return policy_load_t::execute_app(fx_dir, &init, new_argv.size(), new_argv.data());
}
else
{
corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::muxer, &config);
return policy_load_t::execute_app(get_directory(sdk_dotnet), &init, new_argv.size(), new_argv.data());
}
}
}
}
SHARED_API int hostfxr_main(const int argc, const pal::char_t* argv[])
{
trace::setup();
return fx_muxer_t().execute(argc, argv);
}

View file

@ -0,0 +1,16 @@
// 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.
class runtime_config_t;
struct fx_ver_t;
class fx_muxer_t
{
public:
static int execute(const int argc, const pal::char_t* argv[]);
private:
static pal::string_t resolve_fx_dir(const pal::string_t& muxer_path, runtime_config_t* runtime, const pal::string_t& app_path);
static pal::string_t resolve_cli_version(const pal::string_t& global);
static bool resolve_sdk_dotnet_path(const pal::string_t& own_dir, pal::string_t* cli_sdk);
};

View file

@ -0,0 +1,168 @@
// 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 <cassert>
#include "pal.h"
#include "fx_ver.h"
fx_ver_t::fx_ver_t(int major, int minor, int patch, const pal::string_t& pre, const pal::string_t& build)
: m_major(major)
, m_minor(minor)
, m_patch(patch)
, m_pre(pre)
, m_build(build)
{
}
fx_ver_t::fx_ver_t(int major, int minor, int patch, const pal::string_t& pre)
: fx_ver_t(major, minor, patch, pre, _X(""))
{
}
fx_ver_t::fx_ver_t(int major, int minor, int patch)
: fx_ver_t(major, minor, patch, _X(""), _X(""))
{
}
bool fx_ver_t::operator ==(const fx_ver_t& b) const
{
return compare(*this, b) == 0;
}
bool fx_ver_t::operator !=(const fx_ver_t& b) const
{
return !operator ==(b);
}
bool fx_ver_t::operator <(const fx_ver_t& b) const
{
return compare(*this, b) < 0;
}
bool fx_ver_t::operator >(const fx_ver_t& b) const
{
return compare(*this, b) > 0;
}
pal::string_t fx_ver_t::as_str()
{
pal::stringstream_t stream;
stream << m_major << _X(".") << m_minor << _X(".") << m_patch;
if (!m_pre.empty())
{
stream << m_pre;
}
if (!m_build.empty())
{
stream << _X("+") << m_build;
}
return stream.str();
}
/* static */
int fx_ver_t::compare(const fx_ver_t&a, const fx_ver_t& b, bool ignore_build)
{
// compare(u.v.w-p+b, x.y.z-q+c)
return
(a.m_major == b.m_major)
? ((a.m_minor == b.m_minor)
? ((a.m_patch == b.m_patch)
? ((a.m_pre.empty() == b.m_pre.empty())
? ((a.m_pre.empty())
? (ignore_build ? 0 : a.m_build.compare(b.m_build))
: a.m_pre.compare(b.m_pre))
: a.m_pre.empty() ? 1 : -1)
: (a.m_patch > b.m_patch ? 1 : -1))
: (a.m_minor > b.m_minor ? 1 : -1))
: ((a.m_major > b.m_major) ? 1 : -1)
;
}
bool try_stou(const pal::string_t& str, unsigned* num)
{
if (str.empty())
{
return false;
}
if (str.find_first_not_of(_X("0123456789")) != pal::string_t::npos)
{
return false;
}
*num = (unsigned) std::stoul(str);
return true;
}
bool parse_internal(const pal::string_t& ver, fx_ver_t* fx_ver, bool parse_only_production)
{
size_t maj_start = 0;
size_t maj_sep = ver.find(_X('.'));
if (maj_sep == pal::string_t::npos)
{
return false;
}
unsigned major = 0;
if (!try_stou(ver.substr(maj_start, maj_sep), &major))
{
return false;
}
size_t min_start = maj_sep + 1;
size_t min_sep = ver.find(_X('.'), min_start);
if (min_sep == pal::string_t::npos)
{
return false;
}
unsigned minor = 0;
if (!try_stou(ver.substr(min_start, min_sep - min_start), &minor))
{
return false;
}
unsigned patch = 0;
size_t pat_start = min_sep + 1;
size_t pat_sep = ver.find_first_not_of(_X("0123456789"), pat_start);
if (pat_sep == pal::string_t::npos)
{
if (!try_stou(ver.substr(pat_start), &patch))
{
return false;
}
*fx_ver = fx_ver_t(major, minor, patch);
return true;
}
if (parse_only_production)
{
// This is a prerelease or has build suffix.
return false;
}
if (!try_stou(ver.substr(pat_start, pat_sep - pat_start), &patch))
{
return false;
}
size_t pre_start = pat_sep;
size_t pre_sep = ver.find(_X('+'), pre_start);
if (pre_sep == pal::string_t::npos)
{
*fx_ver = fx_ver_t(major, minor, patch, ver.substr(pre_start));
return true;
}
else
{
size_t build_start = pre_sep + 1;
*fx_ver = fx_ver_t(major, minor, patch, ver.substr(pre_start, pre_sep - pre_start), ver.substr(build_start));
return true;
}
}
/* static */
bool fx_ver_t::parse(const pal::string_t& ver, fx_ver_t* fx_ver, bool parse_only_production)
{
bool valid = parse_internal(ver, fx_ver, parse_only_production);
assert(!valid || fx_ver->as_str() == ver);
return valid;
}

View file

@ -0,0 +1,40 @@
// 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 "pal.h"
struct fx_ver_t
{
fx_ver_t(int major, int minor, int patch);
fx_ver_t(int major, int minor, int patch, const pal::string_t& pre);
fx_ver_t(int major, int minor, int patch, const pal::string_t& pre, const pal::string_t& build);
int get_major() { return m_major; }
int get_minor() { return m_minor; }
int get_patch() { return m_patch; }
void set_major(int m) { m_major = m; }
void set_minor(int m) { m_minor = m; }
void set_patch(int p) { m_patch = p; }
bool is_prerelease() { return !m_pre.empty(); }
pal::string_t as_str();
bool operator ==(const fx_ver_t& b) const;
bool operator !=(const fx_ver_t& b) const;
bool operator <(const fx_ver_t& b) const;
bool operator >(const fx_ver_t& b) const;
static bool parse(const pal::string_t& ver, fx_ver_t* fx_ver, bool parse_only_production = false);
private:
int m_major;
int m_minor;
int m_patch;
pal::string_t m_pre;
pal::string_t m_build;
static int compare(const fx_ver_t&a, const fx_ver_t& b, bool ignore_build = false);
};

View file

@ -5,25 +5,21 @@
#include "args.h"
#include "trace.h"
#include "deps_resolver.h"
#include "fx_muxer.h"
#include "utils.h"
#include "coreclr.h"
#include "cpprest/json.h"
#include "libhost.h"
#include "error_codes.h"
enum StatusCode
{
// 0x80 prefix to distinguish from corehost main's error codes.
InvalidArgFailure = 0x81,
CoreClrResolveFailure = 0x82,
CoreClrBindFailure = 0x83,
CoreClrInitFailure = 0x84,
CoreClrExeFailure = 0x85,
ResolverInitFailure = 0x86,
ResolverResolveFailure = 0x87,
};
int run(const arguments_t& args)
corehost_init_t* g_init = nullptr;
int run(const corehost_init_t* init, const runtime_config_t& config, const arguments_t& args)
{
// Load the deps resolver
deps_resolver_t resolver(args);
deps_resolver_t resolver(init->fx_dir(), &config, args);
if (!resolver.valid())
{
trace::error(_X("Invalid .deps file"));
@ -31,8 +27,8 @@ int run(const arguments_t& args)
}
// Add packages directory
pal::string_t packages_dir = args.nuget_packages;
if (!pal::directory_exists(packages_dir))
pal::string_t packages_dir = init->probe_dir();
if (packages_dir.empty() || !pal::directory_exists(packages_dir))
{
(void)pal::get_default_packages_directory(&packages_dir);
}
@ -55,6 +51,8 @@ int run(const arguments_t& args)
return StatusCode::ResolverResolveFailure;
}
// TODO: config.get_runtime_properties();
// Build CoreCLR properties
const char* property_keys[] = {
"TRUSTED_PLATFORM_ASSEMBLIES",
@ -63,20 +61,22 @@ int run(const arguments_t& args)
"NATIVE_DLL_SEARCH_DIRECTORIES",
"PLATFORM_RESOURCE_ROOTS",
"AppDomainCompatSwitch",
// TODO: pipe this from corehost.json
"SERVER_GC",
// Workaround: mscorlib does not resolve symlinks for AppContext.BaseDirectory dotnet/coreclr/issues/2128
"APP_CONTEXT_BASE_DIRECTORY",
"APP_CONTEXT_DEPS_FILES"
};
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);
auto resources_dirs_cstr = pal::to_stdstring(probe_paths.resources);
// Workaround for dotnet/cli Issue #488 and #652
pal::string_t server_gc;
std::string server_gc_cstr = (pal::getenv(_X("COREHOST_SERVER_GC"), &server_gc) && !server_gc.empty()) ? pal::to_stdstring(server_gc) : "0";
std::string deps = pal::to_stdstring(resolver.get_deps_file() + _X(";") + resolver.get_fx_deps_file());
const char* property_values[] = {
// TRUSTED_PLATFORM_ASSEMBLIES
@ -88,13 +88,15 @@ int run(const arguments_t& args)
// NATIVE_DLL_SEARCH_DIRECTORIES
native_dirs_cstr.c_str(),
// PLATFORM_RESOURCE_ROOTS
culture_dirs_cstr.c_str(),
resources_dirs_cstr.c_str(),
// AppDomainCompatSwitch
"UseLatestBehaviorWhenTFMNotSpecified",
// SERVER_GC
server_gc_cstr.c_str(),
// APP_CONTEXT_BASE_DIRECTORY
app_base_cstr.c_str()
app_base_cstr.c_str(),
// APP_CONTEXT_DEPS_FILES,
deps.c_str(),
};
size_t property_size = sizeof(property_keys) / sizeof(property_keys[0]);
@ -188,16 +190,43 @@ int run(const arguments_t& args)
return exit_code;
}
SHARED_API int corehost_load(corehost_init_t* init)
{
g_init = init;
return 0;
}
SHARED_API int corehost_main(const int argc, const pal::char_t* argv[])
{
trace::setup();
assert(g_init);
// Take care of arguments
arguments_t args;
if (!parse_arguments(argc, argv, args))
if (!parse_arguments(g_init->deps_file(), g_init->probe_dir(), g_init->host_mode(), argc, argv, &args))
{
return StatusCode::InvalidArgFailure;
return StatusCode::LibHostInvalidArgs;
}
return run(args);
if (g_init->runtime_config())
{
return run(g_init, *g_init->runtime_config(), args);
}
else
{
runtime_config_t config(get_runtime_config_json(args.managed_application));
if (!config.is_valid())
{
trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str());
return StatusCode::InvalidConfigFile;
}
return run(g_init, config, args);
}
}
SHARED_API int corehost_unload()
{
g_init = nullptr;
return 0;
}

View file

@ -0,0 +1 @@
https://github.com/Microsoft/cpprestsdk

View file

@ -0,0 +1,600 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Various common utilities.
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <string>
#include <vector>
#include <cstdint>
#include <system_error>
#include <random>
#include <locale.h>
#include "cpprest/details/basic_types.h"
#if !defined(_WIN32) || (_MSC_VER >= 1700)
#include <chrono>
#endif
#ifndef _WIN32
//#include <boost/algorithm/string.hpp>
#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269
#include <xlocale.h>
#endif
#endif
/// Various utilities for string conversions and date and time manipulation.
namespace utility
{
// Left over from VS2010 support, remains to avoid breaking.
typedef std::chrono::seconds seconds;
/// Functions for converting to/from std::chrono::seconds to xml string.
namespace timespan
{
/// <summary>
/// Converts a timespan/interval in seconds to xml duration string as specified by
/// http://www.w3.org/TR/xmlschema-2/#duration
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl seconds_to_xml_duration(utility::seconds numSecs);
/// <summary>
/// Converts an xml duration to timespan/interval in seconds
/// http://www.w3.org/TR/xmlschema-2/#duration
/// </summary>
_ASYNCRTIMP utility::seconds __cdecl xml_duration_to_seconds(const utility::string_t &timespanString);
}
/// Functions for Unicode string conversions.
namespace conversions
{
/// <summary>
/// Converts a UTF-16 string to a UTF-8 string.
/// </summary>
/// <param name="w">A two byte character UTF-16 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP std::string __cdecl utf16_to_utf8(const utf16string &w);
/// <summary>
/// Converts a UTF-8 string to a UTF-16
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl utf8_to_utf16(const std::string &s);
/// <summary>
/// Converts a ASCII (us-ascii) string to a UTF-16 string.
/// </summary>
/// <param name="s">A single byte character us-ascii string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl usascii_to_utf16(const std::string &s);
/// <summary>
/// Converts a Latin1 (iso-8859-1) string to a UTF-16 string.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl latin1_to_utf16(const std::string &s);
/// <summary>
/// Converts a Latin1 (iso-8859-1) string to a UTF-8 string.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP utf8string __cdecl latin1_to_utf8(const std::string &s);
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A platform dependent string type.</returns>
_ASYNCRTIMP utility::string_t __cdecl to_string_t(std::string &&s);
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A two byte character UTF-16 string.</param>
/// <returns>A platform dependent string type.</returns>
_ASYNCRTIMP utility::string_t __cdecl to_string_t(utf16string &&s);
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A single byte character UTF-8 string.</param>
/// <returns>A platform dependent string type.</returns>
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const std::string &s);
/// <summary>
/// Converts to a platform dependent Unicode string type.
/// </summary>
/// <param name="s">A two byte character UTF-16 string.</param>
/// <returns>A platform dependent string type.</returns>
_ASYNCRTIMP utility::string_t __cdecl to_string_t(const utf16string &s);
/// <summary>
/// Converts to a UTF-16 from string.
/// </summary>
/// <param name="value">A single byte character UTF-8 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl to_utf16string(const std::string &value);
/// <summary>
/// Converts to a UTF-16 from string.
/// </summary>
/// <param name="value">A two byte character UTF-16 string.</param>
/// <returns>A two byte character UTF-16 string.</returns>
_ASYNCRTIMP utf16string __cdecl to_utf16string(utf16string value);
/// <summary>
/// Converts to a UTF-8 string.
/// </summary>
/// <param name="value">A single byte character UTF-8 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP std::string __cdecl to_utf8string(std::string value);
/// <summary>
/// Converts to a UTF-8 string.
/// </summary>
/// <param name="value">A two byte character UTF-16 string.</param>
/// <returns>A single byte character UTF-8 string.</returns>
_ASYNCRTIMP std::string __cdecl to_utf8string(const utf16string &value);
/// <summary>
/// Encode the given byte array into a base64 string
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl to_base64(const std::vector<unsigned char>& data);
/// <summary>
/// Encode the given 8-byte integer into a base64 string
/// </summary>
_ASYNCRTIMP utility::string_t __cdecl to_base64(uint64_t data);
/// <summary>
/// Decode the given base64 string to a byte array
/// </summary>
_ASYNCRTIMP std::vector<unsigned char> __cdecl from_base64(const utility::string_t& str);
template <typename Source>
utility::string_t print_string(const Source &val, const std::locale &loc)
{
utility::ostringstream_t oss;
oss.imbue(loc);
oss << val;
if (oss.bad())
{
throw std::bad_cast();
}
return oss.str();
}
template <typename Source>
utility::string_t print_string(const Source &val)
{
return print_string(val, std::locale());
}
template <typename Target>
Target scan_string(const utility::string_t &str, const std::locale &loc)
{
Target t;
utility::istringstream_t iss(str);
iss.imbue(loc);
iss >> t;
if (iss.bad())
{
throw std::bad_cast();
}
return t;
}
template <typename Target>
Target scan_string(const utility::string_t &str)
{
return scan_string<Target>(str, std::locale());
}
}
namespace details
{
/// <summary>
/// Cross platform RAII container for setting thread local locale.
/// </summary>
class scoped_c_thread_locale
{
public:
_ASYNCRTIMP scoped_c_thread_locale();
_ASYNCRTIMP ~scoped_c_thread_locale();
#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269
#ifdef _WIN32
typedef _locale_t xplat_locale;
#else
typedef locale_t xplat_locale;
#endif
static _ASYNCRTIMP xplat_locale __cdecl c_locale();
#endif
private:
#ifdef _WIN32
std::string m_prevLocale;
int m_prevThreadSetting;
#elif !(defined(ANDROID) || defined(__ANDROID__))
locale_t m_prevLocale;
#endif
scoped_c_thread_locale(const scoped_c_thread_locale &);
scoped_c_thread_locale & operator=(const scoped_c_thread_locale &);
};
/// <summary>
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
/// taking global lock for performance reasons.
/// </summary>
inline bool __cdecl is_alnum(char ch)
{
return (ch >= '0' && ch <= '9')
|| (ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z');
}
/// <summary>
/// Simplistic implementation of make_unique. A better implementation would be based on variadic templates
/// and therefore not be compatible with Dev10.
/// </summary>
template <typename _Type>
std::unique_ptr<_Type> make_unique() {
return std::unique_ptr<_Type>(new _Type());
}
template <typename _Type, typename _Arg1>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1) {
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1)));
}
template <typename _Type, typename _Arg1, typename _Arg2>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2) {
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2)));
}
template <typename _Type, typename _Arg1, typename _Arg2, typename _Arg3>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3) {
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3)));
}
template <typename _Type, typename _Arg1, typename _Arg2, typename _Arg3, typename _Arg4>
std::unique_ptr<_Type> make_unique(_Arg1&& arg1, _Arg2&& arg2, _Arg3&& arg3, _Arg4&& arg4) {
return std::unique_ptr<_Type>(new _Type(std::forward<_Arg1>(arg1), std::forward<_Arg2>(arg2), std::forward<_Arg3>(arg3), std::forward<_Arg4>(arg4)));
}
/// <summary>
/// Cross platform utility function for performing case insensitive string comparision.
/// </summary>
/// <param name="left">First string to compare.</param>
/// <param name="right">Second strong to compare.</param>
/// <returns>true if the strings are equivalent, false otherwise</returns>
/* inline bool str_icmp(const utility::string_t &left, const utility::string_t &right)
{
#ifdef _WIN32
return _wcsicmp(left.c_str(), right.c_str()) == 0;
#else
return boost::iequals(left, right);
#endif
}
*/
#ifdef _WIN32
/// <summary>
/// Category error type for Windows OS errors.
/// </summary>
class windows_category_impl : public std::error_category
{
public:
virtual const char *name() const CPPREST_NOEXCEPT { return "windows"; }
_ASYNCRTIMP virtual std::string message(int errorCode) const CPPREST_NOEXCEPT;
_ASYNCRTIMP virtual std::error_condition default_error_condition(int errorCode) const CPPREST_NOEXCEPT;
};
/// <summary>
/// Gets the one global instance of the windows error category.
/// </summary>
/// </returns>An error category instance.</returns>
_ASYNCRTIMP const std::error_category & __cdecl windows_category();
#else
/// <summary>
/// Gets the one global instance of the linux error category.
/// </summary>
/// </returns>An error category instance.</returns>
_ASYNCRTIMP const std::error_category & __cdecl linux_category();
#endif
/// <summary>
/// Gets the one global instance of the current platform's error category.
/// <summary>
_ASYNCRTIMP const std::error_category & __cdecl platform_category();
/// <summary>
/// Creates an instance of std::system_error from a OS error code.
/// </summary>
inline std::system_error __cdecl create_system_error(unsigned long errorCode)
{
std::error_code code((int)errorCode, platform_category());
return std::system_error(code, code.message());
}
/// <summary>
/// Creates a std::error_code from a OS error code.
/// </summary>
inline std::error_code __cdecl create_error_code(unsigned long errorCode)
{
return std::error_code((int)errorCode, platform_category());
}
/// <summary>
/// Creates the corresponding error message from a OS error code.
/// </summary>
inline utility::string_t __cdecl create_error_message(unsigned long errorCode)
{
return utility::conversions::to_string_t(create_error_code(errorCode).message());
}
}
class datetime
{
public:
typedef uint64_t interval_type;
/// <summary>
/// Defines the supported date and time string formats.
/// </summary>
enum date_format { RFC_1123, ISO_8601 };
/// <summary>
/// Returns the current UTC time.
/// </summary>
// static _ASYNCRTIMP datetime __cdecl utc_now();
/// <summary>
/// An invalid UTC timestamp value.
/// </summary>
enum:interval_type { utc_timestamp_invalid = static_cast<interval_type>(-1) };
/// <summary>
/// Returns seconds since Unix/POSIX time epoch at 01-01-1970 00:00:00.
/// If time is before epoch, utc_timestamp_invalid is returned.
/// </summary>
/*
static interval_type utc_timestamp()
{
const auto seconds = utc_now().to_interval() / _secondTicks;
if (seconds >= 11644473600LL)
{
return seconds - 11644473600LL;
}
else
{
return utc_timestamp_invalid;
}
}
*/
datetime() : m_interval(0)
{
}
/// <summary>
/// Creates <c>datetime</c> from a string representing time in UTC in RFC 1123 format.
/// </summary>
/// <returns>Returns a <c>datetime</c> of zero if not successful.</returns>
// static _ASYNCRTIMP datetime __cdecl from_string(const utility::string_t& timestring, date_format format = RFC_1123);
/// <summary>
/// Returns a string representation of the <c>datetime</c>.
/// </summary>
_ASYNCRTIMP utility::string_t to_string(date_format format = RFC_1123) const;
/// <summary>
/// Returns the integral time value.
/// </summary>
interval_type to_interval() const
{
return m_interval;
}
datetime operator- (interval_type value) const
{
return datetime(m_interval - value);
}
datetime operator+ (interval_type value) const
{
return datetime(m_interval + value);
}
bool operator== (datetime dt) const
{
return m_interval == dt.m_interval;
}
bool operator!= (const datetime& dt) const
{
return !(*this == dt);
}
static interval_type from_milliseconds(unsigned int milliseconds)
{
return milliseconds*_msTicks;
}
static interval_type from_seconds(unsigned int seconds)
{
return seconds*_secondTicks;
}
static interval_type from_minutes(unsigned int minutes)
{
return minutes*_minuteTicks;
}
static interval_type from_hours(unsigned int hours)
{
return hours*_hourTicks;
}
static interval_type from_days(unsigned int days)
{
return days*_dayTicks;
}
bool is_initialized() const
{
return m_interval != 0;
}
private:
friend int operator- (datetime t1, datetime t2);
static const interval_type _msTicks = static_cast<interval_type>(10000);
static const interval_type _secondTicks = 1000*_msTicks;
static const interval_type _minuteTicks = 60*_secondTicks;
static const interval_type _hourTicks = 60*60*_secondTicks;
static const interval_type _dayTicks = 24*60*60*_secondTicks;
#ifdef _WIN32
// void* to avoid pulling in windows.h
static _ASYNCRTIMP bool __cdecl datetime::system_type_to_datetime(/*SYSTEMTIME*/ void* psysTime, uint64_t seconds, datetime * pdt);
#else
static datetime timeval_to_datetime(const timeval &time);
#endif
// Private constructor. Use static methods to create an instance.
datetime(interval_type interval) : m_interval(interval)
{
}
// Storing as hundreds of nanoseconds 10e-7, i.e. 1 here equals 100ns.
interval_type m_interval;
};
#ifndef _WIN32
// temporary workaround for the fact that
// utf16char is not fully supported in GCC
class cmp
{
public:
static int icmp(std::string left, std::string right)
{
size_t i;
for (i = 0; i < left.size(); ++i)
{
if (i == right.size()) return 1;
auto l = cmp::tolower(left[i]);
auto r = cmp::tolower(right[i]);
if (l > r) return 1;
if (l < r) return -1;
}
if (i < right.size()) return -1;
return 0;
}
private:
static char tolower(char c)
{
if (c >= 'A' && c <= 'Z')
return static_cast<char>(c - 'A' + 'a');
return c;
}
};
#endif
inline int operator- (datetime t1, datetime t2)
{
auto diff = (t1.m_interval - t2.m_interval);
// Round it down to seconds
diff /= 10 * 1000 * 1000;
return static_cast<int>(diff);
}
/*
/// <summary>
/// Nonce string generator class.
/// </summary>
class nonce_generator
{
public:
/// <summary>
/// Define default nonce length.
/// </summary>
enum { default_length = 32 };
/// <summary>
/// Nonce generator constructor.
/// </summary>
/// <param name="length">Length of the generated nonce string.</param>
nonce_generator(int length=default_length) :
m_random(static_cast<unsigned int>(utility::datetime::utc_timestamp())),
m_length(length)
{}
/// <summary>
/// Generate a nonce string containing random alphanumeric characters (A-Za-z0-9).
/// Length of the generated string is set by length().
/// </summary>
/// <returns>The generated nonce string.</returns>
_ASYNCRTIMP utility::string_t generate();
/// <summary>
/// Get length of generated nonce string.
/// </summary>
/// <returns>Nonce string length.</returns>
int length() const { return m_length; }
/// <summary>
/// Set length of the generated nonce string.
/// </summary>
/// <param name="length">Lenght of nonce string.</param>
void set_length(int length) { m_length = length; }
private:
static const utility::string_t c_allowed_chars;
std::mt19937 m_random;
int m_length;
};
*/
} // namespace utility;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,140 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Platform-dependent type definitions
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include "cpprest/details/cpprest_compat.h"
#ifndef _WIN32
# define __STDC_LIMIT_MACROS
# include <stdint.h>
#else
#include <cstdint>
#endif
#include "cpprest/details/SafeInt3.hpp"
namespace utility
{
#ifdef _WIN32
#define _UTF16_STRINGS
#endif
// We should be using a 64-bit size type for most situations that do
// not involve specifying the size of a memory allocation or buffer.
typedef uint64_t size64_t;
#ifndef _WIN32
typedef uint32_t HRESULT; // Needed for PPLX
#endif
#ifdef _UTF16_STRINGS
//
// On Windows, all strings are wide
//
typedef wchar_t char_t ;
typedef std::wstring string_t;
#define _XPLATSTR(x) L ## x
typedef std::wostringstream ostringstream_t;
typedef std::wofstream ofstream_t;
typedef std::wostream ostream_t;
typedef std::wistream istream_t;
typedef std::wifstream ifstream_t;
typedef std::wistringstream istringstream_t;
typedef std::wstringstream stringstream_t;
#define ucout std::wcout
#define ucin std::wcin
#define ucerr std::wcerr
#else
//
// On POSIX platforms, all strings are narrow
//
typedef char char_t;
typedef std::string string_t;
#define _XPLATSTR(x) x
typedef std::ostringstream ostringstream_t;
typedef std::ofstream ofstream_t;
typedef std::ostream ostream_t;
typedef std::istream istream_t;
typedef std::ifstream ifstream_t;
typedef std::istringstream istringstream_t;
typedef std::stringstream stringstream_t;
#define ucout std::cout
#define ucin std::cin
#define ucerr std::cerr
#endif // endif _UTF16_STRINGS
#ifndef _TURN_OFF_PLATFORM_STRING
#define U(x) _XPLATSTR(x)
#endif // !_TURN_OFF_PLATFORM_STRING
}// namespace utility
typedef char utf8char;
typedef std::string utf8string;
typedef std::stringstream utf8stringstream;
typedef std::ostringstream utf8ostringstream;
typedef std::ostream utf8ostream;
typedef std::istream utf8istream;
typedef std::istringstream utf8istringstream;
#ifdef _UTF16_STRINGS
typedef wchar_t utf16char;
typedef std::wstring utf16string;
typedef std::wstringstream utf16stringstream;
typedef std::wostringstream utf16ostringstream;
typedef std::wostream utf16ostream;
typedef std::wistream utf16istream;
typedef std::wistringstream utf16istringstream;
#else
typedef char16_t utf16char;
typedef std::u16string utf16string;
typedef std::basic_stringstream<utf16char> utf16stringstream;
typedef std::basic_ostringstream<utf16char> utf16ostringstream;
typedef std::basic_ostream<utf16char> utf16ostream;
typedef std::basic_istream<utf16char> utf16istream;
typedef std::basic_istringstream<utf16char> utf16istringstream;
#endif
#if defined(_WIN32)
// Include on everything except Windows Desktop ARM, unless explicitly excluded.
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
#if defined(WINAPI_FAMILY)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && defined(_M_ARM)
#define CPPREST_EXCLUDE_WEBSOCKETS
#endif
#else
#if defined(_M_ARM)
#define CPPREST_EXCLUDE_WEBSOCKETS
#endif
#endif
#endif
#endif

View file

@ -0,0 +1,97 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Standard macros and definitions.
* This header has minimal dependency on windows headers and is safe for use in the public API
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if defined(_WIN32) // Settings specific to Windows
#if _MSC_VER >= 1900
#define CPPREST_NOEXCEPT noexcept
#else
#define CPPREST_NOEXCEPT
#endif
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (x)
#include <sal.h>
#else // End settings specific to Windows
// Settings common to all but Windows
#define __declspec(x) __attribute__ ((x))
#define dllimport
#define novtable /* no novtable equivalent */
#define __assume(x) do { if (!(x)) __builtin_unreachable(); } while (false)
#define CASABLANCA_UNREFERENCED_PARAMETER(x) (void)x
#define CPPREST_NOEXCEPT noexcept
#include <assert.h>
#define _ASSERTE(x) assert(x)
// No SAL on non Windows platforms
#include "cpprest/details/nosal.h"
#if not defined __cdecl
#if defined cdecl
#define __cdecl __attribute__ ((cdecl))
#else
#define __cdecl
#endif
#if defined(__ANDROID__)
// This is needed to disable the use of __thread inside the boost library.
// Android does not support thread local storage -- if boost is included
// without this macro defined, it will create references to __tls_get_addr
// which (while able to link) will not be available at runtime and prevent
// the .so from loading.
#define BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION
#endif
#ifdef __clang__
#include <cstdio>
#endif
#endif // defined(__APPLE__)
#endif
#ifdef _NO_ASYNCRTIMP
#define _ASYNCRTIMP
#else
#ifdef _ASYNCRT_EXPORT
#define _ASYNCRTIMP __declspec(dllexport)
#else
#define _ASYNCRTIMP __declspec(dllimport)
#endif
#endif
#ifdef CASABLANCA_DEPRECATION_NO_WARNINGS
#define CASABLANCA_DEPRECATED(x)
#else
#define CASABLANCA_DEPRECATED(x) __declspec(deprecated(x))
#endif

View file

@ -0,0 +1,89 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
***/
#pragma once
// selected MS SAL annotations
#ifdef _In_
#undef _In_
#endif
#define _In_
#ifdef _Inout_
#undef _Inout_
#endif
#define _Inout_
#ifdef _Out_
#undef _Out_
#endif
#define _Out_
#ifdef _In_z_
#undef _In_z_
#endif
#define _In_z_
#ifdef _Out_z_
#undef _Out_z_
#endif
#define _Out_z_
#ifdef _Inout_z_
#undef _Inout_z_
#endif
#define _Inout_z_
#ifdef _In_opt_
#undef _In_opt_
#endif
#define _In_opt_
#ifdef _Out_opt_
#undef _Out_opt_
#endif
#define _Out_opt_
#ifdef _Inout_opt_
#undef _Inout_opt_
#endif
#define _Inout_opt_
#ifdef _Out_writes_
#undef _Out_writes_
#endif
#define _Out_writes_(x)
#ifdef _Out_writes_opt_
#undef _Out_writes_opt_
#endif
#define _Out_writes_opt_(x)
#ifdef _In_reads_
#undef _In_reads_
#endif
#define _In_reads_(x)
#ifdef _Inout_updates_bytes_
#undef _Inout_updates_bytes_
#endif
#define _Inout_updates_bytes_(x)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,109 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Pre-compiled headers
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-local-typedef"
#endif
#include <limits.h>
#include <cstring>
#ifdef _WIN32
#ifdef CPPREST_TARGET_XP
#include <winsdkver.h>
#ifndef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_WS03 //Windows XP with SP2
#endif
#endif
#include <SDKDDKVer.h>
// use the debug version of the CRT if _DEBUG is defined
#ifdef _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#define NOMINMAX
#endif
#include <windows.h>
#include <objbase.h>
// Windows Header Files:
#if !defined(__cplusplus_winrt)
#include <winhttp.h>
#endif // #if !defined(__cplusplus_winrt)
#else // LINUX or APPLE
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <cstdint>
#include <string>
#include <sstream>
#include <thread>
#include <atomic>
#include <signal.h>
#include "pthread.h"
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#endif // _WIN32
// Macro indicating the C++ Rest SDK product itself is being built.
// This is to help track how many developers are directly building from source themselves.
#define _CASA_BUILD_FROM_SRC
#include <iostream>
#include <fstream>
#include <algorithm>
#include <exception>
#include <assert.h>
#include <streambuf>
#include <mutex>
#include <array>
#include <vector>
#include <memory>
#include <thread>
// json
#include "cpprest/json.h"
#if defined(max)
#error: max macro defined -- make sure to #define NOMINMAX before including windows.h
#endif
#if defined(min)
#error: min macro defined -- make sure to #define NOMINMAX before including windows.h
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#endif

View file

@ -0,0 +1,495 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: JSON parser and writer
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#include "stdafx.h"
using namespace web;
bool json::details::g_keep_json_object_unsorted = false;
void json::keep_object_element_order(bool keep_order)
{
json::details::g_keep_json_object_unsorted = keep_order;
}
utility::ostream_t& web::json::operator << (utility::ostream_t &os, const web::json::value &val)
{
val.serialize(os);
return os;
}
utility::istream_t& web::json::operator >> (utility::istream_t &is, json::value &val)
{
val = json::value::parse(is);
return is;
}
web::json::value::value() :
m_value(utility::details::make_unique<web::json::details::_Null>())
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::Null)
#endif
{ }
web::json::value::value(int32_t value) :
m_value(utility::details::make_unique<web::json::details::_Number>(value))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::Number)
#endif
{ }
web::json::value::value(uint32_t value) :
m_value(utility::details::make_unique<web::json::details::_Number>(value))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::Number)
#endif
{ }
web::json::value::value(int64_t value) :
m_value(utility::details::make_unique<web::json::details::_Number>(value))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::Number)
#endif
{ }
web::json::value::value(uint64_t value) :
m_value(utility::details::make_unique<web::json::details::_Number>(value))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::Number)
#endif
{ }
web::json::value::value(double value) :
m_value(utility::details::make_unique<web::json::details::_Number>(value))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::Number)
#endif
{ }
web::json::value::value(bool value) :
m_value(utility::details::make_unique<web::json::details::_Boolean>(value))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::Boolean)
#endif
{ }
web::json::value::value(utility::string_t value) :
m_value(utility::details::make_unique<web::json::details::_String>(std::move(value)))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::String)
#endif
{ }
web::json::value::value(utility::string_t value, bool has_escape_chars) :
m_value(utility::details::make_unique<web::json::details::_String>(std::move(value), has_escape_chars))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
, m_kind(value::String)
#endif
{ }
web::json::value::value(const utility::char_t* value) :
m_value(utility::details::make_unique<web::json::details::_String>(value))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(value::String)
#endif
{ }
web::json::value::value(const utility::char_t* value, bool has_escape_chars) :
m_value(utility::details::make_unique<web::json::details::_String>(utility::string_t(value), has_escape_chars))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
, m_kind(value::String)
#endif
{ }
web::json::value::value(const value &other) :
m_value(other.m_value->_copy_value())
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(other.m_kind)
#endif
{ }
web::json::value &web::json::value::operator=(const value &other)
{
if(this != &other)
{
m_value = std::unique_ptr<details::_Value>(other.m_value->_copy_value());
#ifdef ENABLE_JSON_VALUE_VISUALIZER
m_kind = other.m_kind;
#endif
}
return *this;
}
web::json::value::value(value &&other) CPPREST_NOEXCEPT :
m_value(std::move(other.m_value))
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,m_kind(other.m_kind)
#endif
{}
web::json::value &web::json::value::operator=(web::json::value &&other) CPPREST_NOEXCEPT
{
if(this != &other)
{
m_value.swap(other.m_value);
#ifdef ENABLE_JSON_VALUE_VISUALIZER
m_kind = other.m_kind;
#endif
}
return *this;
}
web::json::value web::json::value::null()
{
return web::json::value();
}
web::json::value web::json::value::number(double value)
{
return web::json::value(value);
}
web::json::value web::json::value::number(int32_t value)
{
return web::json::value(value);
}
web::json::value web::json::value::number(uint32_t value)
{
return web::json::value(value);
}
web::json::value web::json::value::number(int64_t value)
{
return web::json::value(value);
}
web::json::value web::json::value::number(uint64_t value)
{
return web::json::value(value);
}
web::json::value web::json::value::boolean(bool value)
{
return web::json::value(value);
}
web::json::value web::json::value::string(utility::string_t value)
{
std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_String>(std::move(value));
return web::json::value(std::move(ptr)
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,value::String
#endif
);
}
web::json::value web::json::value::string(utility::string_t value, bool has_escape_chars)
{
std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_String>(std::move(value), has_escape_chars);
return web::json::value(std::move(ptr)
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,value::String
#endif
);
}
#ifdef _WIN32
web::json::value web::json::value::string(const std::string &value)
{
std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_String>(utility::conversions::to_utf16string(value));
return web::json::value(std::move(ptr)
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,value::String
#endif
);
}
#endif
web::json::value web::json::value::object(bool keep_order)
{
std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Object>(keep_order);
return web::json::value(std::move(ptr)
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,value::Object
#endif
);
}
web::json::value web::json::value::object(std::vector<std::pair<::utility::string_t, value>> fields, bool keep_order)
{
std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Object>(std::move(fields), keep_order);
return web::json::value(std::move(ptr)
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,value::Object
#endif
);
}
web::json::value web::json::value::array()
{
std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Array>();
return web::json::value(std::move(ptr)
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,value::Array
#endif
);
}
web::json::value web::json::value::array(size_t size)
{
std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Array>(size);
return web::json::value(std::move(ptr)
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,value::Array
#endif
);
}
web::json::value web::json::value::array(std::vector<value> elements)
{
std::unique_ptr<details::_Value> ptr = utility::details::make_unique<details::_Array>(std::move(elements));
return web::json::value(std::move(ptr)
#ifdef ENABLE_JSON_VALUE_VISUALIZER
,value::Array
#endif
);
}
const web::json::number& web::json::value::as_number() const
{
return m_value->as_number();
}
double web::json::value::as_double() const
{
return m_value->as_double();
}
int web::json::value::as_integer() const
{
return m_value->as_integer();
}
bool web::json::value::as_bool() const
{
return m_value->as_bool();
}
json::array& web::json::value::as_array()
{
return m_value->as_array();
}
const json::array& web::json::value::as_array() const
{
return m_value->as_array();
}
json::object& web::json::value::as_object()
{
return m_value->as_object();
}
const json::object& web::json::value::as_object() const
{
return m_value->as_object();
}
bool web::json::number::is_int32() const
{
switch (m_type)
{
case signed_type : return m_intval >= std::numeric_limits<int32_t>::min() && m_intval <= std::numeric_limits<int32_t>::max();
case unsigned_type : return m_uintval <= std::numeric_limits<int32_t>::max();
case double_type :
default :
return false;
}
}
bool web::json::number::is_uint32() const
{
switch (m_type)
{
case signed_type : return m_intval >= 0 && m_intval <= std::numeric_limits<uint32_t>::max();
case unsigned_type : return m_uintval <= std::numeric_limits<uint32_t>::max();
case double_type :
default :
return false;
}
}
bool web::json::number::is_int64() const
{
switch (m_type)
{
case signed_type : return true;
case unsigned_type : return m_uintval <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
case double_type :
default :
return false;
}
}
bool web::json::details::_String::has_escape_chars(const _String &str)
{
return std::any_of(std::begin(str.m_string), std::end(str.m_string), [](utility::string_t::value_type const x)
{
if (x <= 31) { return true; }
if (x == '"') { return true; }
if (x == '\\') { return true; }
return false;
});
}
web::json::value::value_type json::value::type() const { return m_value->type(); }
bool json::value::is_integer() const
{
if(!is_number())
{
return false;
}
return m_value->is_integer();
}
bool json::value::is_double() const
{
if(!is_number())
{
return false;
}
return m_value->is_double();
}
json::value& web::json::details::_Object::index(const utility::string_t &key)
{
return m_object[key];
}
bool web::json::details::_Object::has_field(const utility::string_t &key) const
{
return m_object.find(key) != m_object.end();
}
utility::string_t json::value::to_string() const
{
#ifndef _WIN32
utility::details::scoped_c_thread_locale locale;
#endif
return m_value->to_string();
}
bool json::value::operator==(const json::value &other) const
{
if (this->m_value.get() == other.m_value.get())
return true;
if (this->type() != other.type())
return false;
switch(this->type())
{
case Null:
return true;
case Number:
return this->as_number() == other.as_number();
case Boolean:
return this->as_bool() == other.as_bool();
case String:
return this->as_string() == other.as_string();
case Object:
return static_cast<const json::details::_Object*>(this->m_value.get())->is_equal(static_cast<const json::details::_Object*>(other.m_value.get()));
case Array:
return static_cast<const json::details::_Array*>(this->m_value.get())->is_equal(static_cast<const json::details::_Array*>(other.m_value.get()));
}
__assume(0);
}
void web::json::value::erase(size_t index)
{
return this->as_array().erase(index);
}
void web::json::value::erase(const utility::string_t &key)
{
return this->as_object().erase(key);
}
// at() overloads
web::json::value& web::json::value::at(size_t index)
{
return this->as_array().at(index);
}
const web::json::value& web::json::value::at(size_t index) const
{
return this->as_array().at(index);
}
web::json::value& web::json::value::at(const utility::string_t& key)
{
return this->as_object().at(key);
}
const web::json::value& web::json::value::at(const utility::string_t& key) const
{
return this->as_object().at(key);
}
web::json::value& web::json::value::operator [] (const utility::string_t &key)
{
if ( this->is_null() )
{
m_value.reset(new web::json::details::_Object(details::g_keep_json_object_unsorted));
#ifdef ENABLE_JSON_VALUE_VISUALIZER
m_kind = value::Object;
#endif
}
return m_value->index(key);
}
web::json::value& web::json::value::operator[](size_t index)
{
if ( this->is_null() )
{
m_value.reset(new web::json::details::_Array());
#ifdef ENABLE_JSON_VALUE_VISUALIZER
m_kind = value::Array;
#endif
}
return m_value->index(index);
}
// Remove once VS 2013 is no longer supported.
#if defined(_WIN32) && _MSC_VER < 1900
static web::json::details::json_error_category_impl instance;
#endif
const web::json::details::json_error_category_impl& web::json::details::json_error_category()
{
#if !defined(_WIN32) || _MSC_VER >= 1900
static web::json::details::json_error_category_impl instance;
#endif
return instance;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,274 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* HTTP Library: JSON parser and writer
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#include "stdafx.h"
#include <stdio.h>
#ifndef _WIN32
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#endif
using namespace web;
using namespace web::json;
using namespace utility;
using namespace utility::conversions;
//
// JSON Serialization
//
#ifdef _WIN32
void web::json::value::serialize(std::ostream& stream) const
{
// This has better performance than writing directly to stream.
std::string str;
m_value->serialize_impl(str);
stream << str;
}
void web::json::value::format(std::basic_string<wchar_t> &string) const
{
m_value->format(string);
}
#endif
void web::json::value::serialize(utility::ostream_t &stream) const
{
#ifndef _WIN32
utility::details::scoped_c_thread_locale locale;
#endif
// This has better performance than writing directly to stream.
utility::string_t str;
m_value->serialize_impl(str);
stream << str;
}
void web::json::value::format(std::basic_string<char>& string) const
{
m_value->format(string);
}
template<typename CharType>
void web::json::details::append_escape_string(std::basic_string<CharType>& str, const std::basic_string<CharType>& escaped)
{
for (const auto &ch : escaped)
{
switch (ch)
{
case '\"':
str += '\\';
str += '\"';
break;
case '\\':
str += '\\';
str += '\\';
break;
case '\b':
str += '\\';
str += 'b';
break;
case '\f':
str += '\\';
str += 'f';
break;
case '\r':
str += '\\';
str += 'r';
break;
case '\n':
str += '\\';
str += 'n';
break;
case '\t':
str += '\\';
str += 't';
break;
default:
// If a control character then must unicode escaped.
if (ch >= 0 && ch <= 0x1F)
{
static const std::array<CharType, 16> intToHex = { { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' } };
str += '\\';
str += 'u';
str += '0';
str += '0';
str += intToHex[(ch & 0xF0) >> 4];
str += intToHex[ch & 0x0F];
}
else
{
str += ch;
}
}
}
}
void web::json::details::format_string(const utility::string_t& key, utility::string_t& str)
{
str.push_back('"');
append_escape_string(str, key);
str.push_back('"');
}
#ifdef _WIN32
void web::json::details::format_string(const utility::string_t& key, std::string& str)
{
str.push_back('"');
append_escape_string(str, utility::conversions::to_utf8string(key));
str.push_back('"');
}
#endif
void web::json::details::_String::format(std::basic_string<char>& str) const
{
str.push_back('"');
if(m_has_escape_char)
{
append_escape_string(str, utility::conversions::to_utf8string(m_string));
}
else
{
str.append(utility::conversions::to_utf8string(m_string));
}
str.push_back('"');
}
void web::json::details::_Number::format(std::basic_string<char>& stream) const
{
if(m_number.m_type != number::type::double_type)
{
// #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator.
const size_t tempSize = std::numeric_limits<uint64_t>::digits10 + 3;
char tempBuffer[tempSize];
#ifdef _WIN32
// This can be improved performance-wise if we implement our own routine
if (m_number.m_type == number::type::signed_type)
_i64toa_s(m_number.m_intval, tempBuffer, tempSize, 10);
else
_ui64toa_s(m_number.m_uintval, tempBuffer, tempSize, 10);
const auto numChars = strnlen_s(tempBuffer, tempSize);
#else
int numChars;
if (m_number.m_type == number::type::signed_type)
numChars = snprintf(tempBuffer, tempSize, "%" PRId64, m_number.m_intval);
else
numChars = snprintf(tempBuffer, tempSize, "%" PRIu64, m_number.m_uintval);
#endif
stream.append(tempBuffer, numChars);
}
else
{
// #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null terminator
const size_t tempSize = std::numeric_limits<double>::digits10 + 10;
char tempBuffer[tempSize];
#ifdef _WIN32
const auto numChars = _sprintf_s_l(
tempBuffer,
tempSize,
"%.*g",
utility::details::scoped_c_thread_locale::c_locale(),
std::numeric_limits<double>::digits10 + 2,
m_number.m_value);
#else
const auto numChars = snprintf(tempBuffer, tempSize, "%.*g", std::numeric_limits<double>::digits10 + 2, m_number.m_value);
#endif
stream.append(tempBuffer, numChars);
}
}
#ifdef _WIN32
void web::json::details::_String::format(std::basic_string<wchar_t>& str) const
{
str.push_back(L'"');
if(m_has_escape_char)
{
append_escape_string(str, m_string);
}
else
{
str.append(m_string);
}
str.push_back(L'"');
}
void web::json::details::_Number::format(std::basic_string<wchar_t>& stream) const
{
if(m_number.m_type != number::type::double_type)
{
// #digits + 1 to avoid loss + 1 for the sign + 1 for null terminator.
const size_t tempSize = std::numeric_limits<uint64_t>::digits10 + 3;
wchar_t tempBuffer[tempSize];
if (m_number.m_type == number::type::signed_type)
_i64tow_s(m_number.m_intval, tempBuffer, tempSize, 10);
else
_ui64tow_s(m_number.m_uintval, tempBuffer, tempSize, 10);
stream.append(tempBuffer, wcsnlen_s(tempBuffer, tempSize));
}
else
{
// #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null terminator
const size_t tempSize = std::numeric_limits<double>::digits10 + 10;
wchar_t tempBuffer[tempSize];
const int numChars = _swprintf_s_l(
tempBuffer,
tempSize,
L"%.*g",
utility::details::scoped_c_thread_locale::c_locale(),
std::numeric_limits<double>::digits10 + 2,
m_number.m_value);
stream.append(tempBuffer, numChars);
}
}
#endif
const utility::string_t & web::json::details::_String::as_string() const
{
return m_string;
}
const utility::string_t & web::json::value::as_string() const
{
return m_value->as_string();
}
utility::string_t json::value::serialize() const
{
#ifndef _WIN32
utility::details::scoped_c_thread_locale locale;
#endif
return m_value->to_string();
}

View file

@ -0,0 +1,496 @@
/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Utilities
*
* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#include "stdafx.h"
#ifndef _WIN32
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-local-typedef"
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif
// Could use C++ standard library if not __GLIBCXX__,
// For testing purposes we just the handwritten on all platforms.
#if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS)
#include <codecvt>
#endif
using namespace web;
using namespace utility;
using namespace utility::conversions;
namespace utility
{
namespace details
{
#if !defined(ANDROID) && !defined(__ANDROID__)
std::once_flag g_c_localeFlag;
std::unique_ptr<scoped_c_thread_locale::xplat_locale, void(*)(scoped_c_thread_locale::xplat_locale *)> g_c_locale(nullptr, [](scoped_c_thread_locale::xplat_locale *){});
scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale()
{
std::call_once(g_c_localeFlag, [&]()
{
scoped_c_thread_locale::xplat_locale *clocale = new scoped_c_thread_locale::xplat_locale();
#ifdef _WIN32
*clocale = _create_locale(LC_ALL, "C");
if (*clocale == nullptr)
{
throw std::runtime_error("Unable to create 'C' locale.");
}
auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale)
{
_free_locale(*clocale);
delete clocale;
};
#else
*clocale = newlocale(LC_ALL, "C", nullptr);
if (*clocale == nullptr)
{
throw std::runtime_error("Unable to create 'C' locale.");
}
auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale)
{
freelocale(*clocale);
delete clocale;
};
#endif
g_c_locale = std::unique_ptr<scoped_c_thread_locale::xplat_locale, void(*)(scoped_c_thread_locale::xplat_locale *)>(clocale, deleter);
});
return *g_c_locale;
}
#endif
#ifdef _WIN32
scoped_c_thread_locale::scoped_c_thread_locale()
: m_prevLocale(), m_prevThreadSetting(-1)
{
char *prevLocale = setlocale(LC_ALL, nullptr);
if (prevLocale == nullptr)
{
throw std::runtime_error("Unable to retrieve current locale.");
}
if (std::strcmp(prevLocale, "C") != 0)
{
m_prevLocale = prevLocale;
m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
if (m_prevThreadSetting == -1)
{
throw std::runtime_error("Unable to enable per thread locale.");
}
if (setlocale(LC_ALL, "C") == nullptr)
{
_configthreadlocale(m_prevThreadSetting);
throw std::runtime_error("Unable to set locale");
}
}
}
scoped_c_thread_locale::~scoped_c_thread_locale()
{
if (m_prevThreadSetting != -1)
{
setlocale(LC_ALL, m_prevLocale.c_str());
_configthreadlocale(m_prevThreadSetting);
}
}
#elif (defined(ANDROID) || defined(__ANDROID__))
scoped_c_thread_locale::scoped_c_thread_locale() {}
scoped_c_thread_locale::~scoped_c_thread_locale() {}
#else
scoped_c_thread_locale::scoped_c_thread_locale()
: m_prevLocale(nullptr)
{
char *prevLocale = setlocale(LC_ALL, nullptr);
if (prevLocale == nullptr)
{
throw std::runtime_error("Unable to retrieve current locale.");
}
if (std::strcmp(prevLocale, "C") != 0)
{
m_prevLocale = uselocale(c_locale());
if (m_prevLocale == nullptr)
{
throw std::runtime_error("Unable to set locale");
}
}
}
scoped_c_thread_locale::~scoped_c_thread_locale()
{
if (m_prevLocale != nullptr)
{
uselocale(m_prevLocale);
}
}
#endif
}
namespace details
{
const std::error_category & __cdecl platform_category()
{
#ifdef _WIN32
return windows_category();
#else
return linux_category();
#endif
}
#ifdef _WIN32
// Remove once VS 2013 is no longer supported.
#if _MSC_VER < 1900
static details::windows_category_impl instance;
#endif
const std::error_category & __cdecl windows_category()
{
#if _MSC_VER >= 1900
static details::windows_category_impl instance;
#endif
return instance;
}
std::string windows_category_impl::message(int errorCode) const CPPREST_NOEXCEPT
{
const size_t buffer_size = 4096;
DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
LPCVOID lpSource = NULL;
#if !defined(__cplusplus_winrt)
if (errorCode >= 12000)
{
dwFlags = FORMAT_MESSAGE_FROM_HMODULE;
lpSource = GetModuleHandleA("winhttp.dll"); // this handle DOES NOT need to be freed
}
#endif
std::wstring buffer;
buffer.resize(buffer_size);
const auto result = ::FormatMessageW(
dwFlags,
lpSource,
errorCode,
0,
&buffer[0],
buffer_size,
NULL);
if (result == 0)
{
std::ostringstream os;
os << "Unable to get an error message for error code: " << errorCode << ".";
return os.str();
}
return utility::conversions::to_utf8string(buffer);
}
std::error_condition windows_category_impl::default_error_condition(int errorCode) const CPPREST_NOEXCEPT
{
// First see if the STL implementation can handle the mapping for common cases.
const std::error_condition errCondition = std::system_category().default_error_condition(errorCode);
const std::string errConditionMsg = errCondition.message();
if(_stricmp(errConditionMsg.c_str(), "unknown error") != 0)
{
return errCondition;
}
switch(errorCode)
{
#ifndef __cplusplus_winrt
case ERROR_WINHTTP_TIMEOUT:
return std::errc::timed_out;
case ERROR_WINHTTP_CANNOT_CONNECT:
return std::errc::host_unreachable;
case ERROR_WINHTTP_CONNECTION_ERROR:
return std::errc::connection_aborted;
#endif
case INET_E_RESOURCE_NOT_FOUND:
case INET_E_CANNOT_CONNECT:
return std::errc::host_unreachable;
case INET_E_CONNECTION_TIMEOUT:
return std::errc::timed_out;
case INET_E_DOWNLOAD_FAILURE:
return std::errc::connection_aborted;
default:
break;
}
return std::error_condition(errorCode, *this);
}
#else
const std::error_category & __cdecl linux_category()
{
// On Linux we are using boost error codes which have the exact same
// mapping and are equivalent with std::generic_category error codes.
return std::generic_category();
}
#endif
}
#define LOW_3BITS 0x7
#define LOW_4BITS 0xF
#define LOW_5BITS 0x1F
#define LOW_6BITS 0x3F
#define BIT4 0x8
#define BIT5 0x10
#define BIT6 0x20
#define BIT7 0x40
#define BIT8 0x80
#define L_SURROGATE_START 0xDC00
#define L_SURROGATE_END 0xDFFF
#define H_SURROGATE_START 0xD800
#define H_SURROGATE_END 0xDBFF
#define SURROGATE_PAIR_START 0x10000
utf16string __cdecl conversions::utf8_to_utf16(const std::string &s)
{
#if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS)
std::wstring_convert<std::codecvt_utf8_utf16<utf16char>, utf16char> conversion;
return conversion.from_bytes(src);
#else
utf16string dest;
// Save repeated heap allocations, use less than source string size assuming some
// of the characters are not just ASCII and collapse.
dest.reserve(static_cast<size_t>(static_cast<double>(s.size()) * .70));
for (auto src = s.begin(); src != s.end(); ++src)
{
if ((*src & BIT8) == 0) // single byte character, 0x0 to 0x7F
{
dest.push_back(utf16string::value_type(*src));
}
else
{
unsigned char numContBytes = 0;
uint32_t codePoint;
if ((*src & BIT7) == 0)
{
throw std::range_error("UTF-8 string character can never start with 10xxxxxx");
}
else if ((*src & BIT6) == 0) // 2 byte character, 0x80 to 0x7FF
{
codePoint = *src & LOW_5BITS;
numContBytes = 1;
}
else if ((*src & BIT5) == 0) // 3 byte character, 0x800 to 0xFFFF
{
codePoint = *src & LOW_4BITS;
numContBytes = 2;
}
else if ((*src & BIT4) == 0) // 4 byte character, 0x10000 to 0x10FFFF
{
codePoint = *src & LOW_3BITS;
numContBytes = 3;
}
else
{
throw std::range_error("UTF-8 string has invalid Unicode code point");
}
for (unsigned char i = 0; i < numContBytes; ++i)
{
if (++src == s.end())
{
throw std::range_error("UTF-8 string is missing bytes in character");
}
if ((*src & BIT8) == 0 || (*src & BIT7) != 0)
{
throw std::range_error("UTF-8 continuation byte is missing leading byte");
}
codePoint <<= 6;
codePoint |= *src & LOW_6BITS;
}
if (codePoint >= SURROGATE_PAIR_START)
{
// In UTF-16 U+10000 to U+10FFFF are represented as two 16-bit code units, surrogate pairs.
// - 0x10000 is subtracted from the code point
// - high surrogate is 0xD800 added to the top ten bits
// - low surrogate is 0xDC00 added to the low ten bits
codePoint -= SURROGATE_PAIR_START;
dest.push_back(utf16string::value_type((codePoint >> 10) | H_SURROGATE_START));
dest.push_back(utf16string::value_type((codePoint & 0x3FF) | L_SURROGATE_START));
}
else
{
// In UTF-16 U+0000 to U+D7FF and U+E000 to U+FFFF are represented exactly as the Unicode code point value.
// U+D800 to U+DFFF are not valid characters, for simplicity we assume they are not present but will encode
// them if encountered.
dest.push_back(utf16string::value_type(codePoint));
}
}
}
return dest;
#endif
}
std::string __cdecl conversions::utf16_to_utf8(const utf16string &w)
{
#if defined(CPPREST_STDLIB_UNICODE_CONVERSIONS)
std::wstring_convert<std::codecvt_utf8_utf16<utf16char>, utf16char> conversion;
return conversion.to_bytes(w);
#else
std::string dest;
dest.reserve(w.size());
for (auto src = w.begin(); src != w.end(); ++src)
{
// Check for high surrogate.
if (*src >= H_SURROGATE_START && *src <= H_SURROGATE_END)
{
const auto highSurrogate = *src++;
if (src == w.end())
{
throw std::range_error("UTF-16 string is missing low surrogate");
}
const auto lowSurrogate = *src;
if (lowSurrogate < L_SURROGATE_START || lowSurrogate > L_SURROGATE_END)
{
throw std::range_error("UTF-16 string has invalid low surrogate");
}
// To get from surrogate pair to Unicode code point:
// - subract 0xD800 from high surrogate, this forms top ten bits
// - subract 0xDC00 from low surrogate, this forms low ten bits
// - add 0x10000
// Leaves a code point in U+10000 to U+10FFFF range.
uint32_t codePoint = highSurrogate - H_SURROGATE_START;
codePoint <<= 10;
codePoint |= lowSurrogate - L_SURROGATE_START;
codePoint += SURROGATE_PAIR_START;
// 4 bytes need using 21 bits
dest.push_back(char((codePoint >> 18) | 0xF0)); // leading 3 bits
dest.push_back(char(((codePoint >> 12) & LOW_6BITS) | BIT8)); // next 6 bits
dest.push_back(char(((codePoint >> 6) & LOW_6BITS) | BIT8)); // next 6 bits
dest.push_back(char((codePoint & LOW_6BITS) | BIT8)); // trailing 6 bits
}
else
{
if (*src <= 0x7F) // single byte character
{
dest.push_back(static_cast<char>(*src));
}
else if (*src <= 0x7FF) // 2 bytes needed (11 bits used)
{
dest.push_back(char((*src >> 6) | 0xC0)); // leading 5 bits
dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits
}
else // 3 bytes needed (16 bits used)
{
dest.push_back(char((*src >> 12) | 0xE0)); // leading 4 bits
dest.push_back(char(((*src >> 6) & LOW_6BITS) | BIT8)); // middle 6 bits
dest.push_back(char((*src & LOW_6BITS) | BIT8)); // trailing 6 bits
}
}
}
return dest;
#endif
}
utf16string __cdecl conversions::usascii_to_utf16(const std::string &s)
{
// Ascii is a subset of UTF-8 so just convert to UTF-16
return utf8_to_utf16(s);
}
utf16string __cdecl conversions::latin1_to_utf16(const std::string &s)
{
// Latin1 is the first 256 code points in Unicode.
// In UTF-16 encoding each of these is represented as exactly the numeric code point.
utf16string dest;
dest.resize(s.size());
for (size_t i = 0; i < s.size(); ++i)
{
dest[i] = utf16char(s[i]);
}
return dest;
}
utf8string __cdecl conversions::latin1_to_utf8(const std::string &s)
{
return utf16_to_utf8(latin1_to_utf16(s));
}
utility::string_t __cdecl conversions::to_string_t(utf16string &&s)
{
#ifdef _UTF16_STRINGS
return std::move(s);
#else
return utf16_to_utf8(std::move(s));
#endif
}
utility::string_t __cdecl conversions::to_string_t(std::string &&s)
{
#ifdef _UTF16_STRINGS
return utf8_to_utf16(std::move(s));
#else
return std::move(s);
#endif
}
utility::string_t __cdecl conversions::to_string_t(const utf16string &s)
{
#ifdef _UTF16_STRINGS
return s;
#else
return utf16_to_utf8(s);
#endif
}
utility::string_t __cdecl conversions::to_string_t(const std::string &s)
{
#ifdef _UTF16_STRINGS
return utf8_to_utf16(s);
#else
return s;
#endif
}
std::string __cdecl conversions::to_utf8string(std::string value) { return std::move(value); }
std::string __cdecl conversions::to_utf8string(const utf16string &value) { return utf16_to_utf8(value); }
utf16string __cdecl conversions::to_utf16string(const std::string &value) { return utf8_to_utf16(value); }
utf16string __cdecl conversions::to_utf16string(utf16string value) { return std::move(value); }
static bool is_digit(utility::char_t c) { return c >= _XPLATSTR('0') && c <= _XPLATSTR('9'); }
}

View file

@ -0,0 +1,61 @@
// 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 "pal.h"
#include "utils.h"
#include "trace.h"
#include "libhost.h"
pal::string_t get_runtime_config_json(const pal::string_t& app_path)
{
auto name = get_filename_without_ext(app_path);
auto json_name = name + _X(".runtimeconfig.json");
auto json_path = get_directory(app_path);
append_path(&json_path, json_name.c_str());
if (pal::file_exists(json_path))
{
return json_path;
}
return pal::string_t();
}
host_mode_t detect_operating_mode(const int argc, const pal::char_t* argv[], pal::string_t* p_own_dir)
{
pal::string_t own_path;
if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path))
{
trace::error(_X("Failed to locate current executable"));
return host_mode_t::invalid;
}
pal::string_t own_name = get_filename(own_path);
pal::string_t own_dir = get_directory(own_path);
if (p_own_dir)
{
p_own_dir->assign(own_dir);
}
pal::string_t own_dll_filename = strip_file_ext(own_name) + _X(".dll");
pal::string_t own_dll = own_dir;
append_path(&own_dll, own_dll_filename.c_str());
trace::info(_X("Exists %s"), own_dll.c_str());
if (coreclr_exists_in_dir(own_dir) || pal::file_exists(own_dll))
{
pal::string_t own_deps_json = own_dir;
pal::string_t own_deps_filename = strip_file_ext(own_name) + _X(".deps.json");
pal::string_t own_config_filename = strip_file_ext(own_name) + _X(".runtimeconfig.json");
append_path(&own_deps_json, own_deps_filename.c_str());
if (trace::is_enabled())
{
trace::info(_X("Detecting mode... CoreCLR present in own dir [%s] and checking if [%s] file present=[%d]"),
own_dir.c_str(), own_deps_filename.c_str(), pal::file_exists(own_deps_json));
}
return ((pal::file_exists(own_deps_json) || !pal::file_exists(own_config_filename)) && pal::file_exists(own_dll)) ? host_mode_t::standalone : host_mode_t::split_fx;
}
else
{
return host_mode_t::muxer;
}
}

View file

@ -1,4 +1,70 @@
// 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 __LIBHOST_H__
#define __LIBHOST_H__
#define LIBHOST_NAME MAKE_LIBNAME("hostpolicy")
enum host_mode_t
{
invalid = 0,
muxer,
standalone,
split_fx
};
class runtime_config_t;
class corehost_init_t
{
const pal::string_t m_probe_path;
const pal::string_t m_deps_file;
const pal::string_t m_fx_dir;
host_mode_t m_host_mode;
const runtime_config_t* m_runtime_config;
public:
corehost_init_t(
const pal::string_t& deps_file,
const pal::string_t& probe_path,
const pal::string_t& fx_dir,
const host_mode_t mode,
const runtime_config_t* runtime_config)
: m_fx_dir(fx_dir)
, m_runtime_config(runtime_config)
, m_deps_file(deps_file)
, m_probe_path(probe_path)
, m_host_mode(mode)
{
}
const host_mode_t host_mode() const
{
return m_host_mode;
}
const pal::string_t& deps_file() const
{
return m_deps_file;
}
const pal::string_t& probe_dir() const
{
return m_probe_path;
}
const pal::string_t& fx_dir() const
{
return m_fx_dir;
}
const runtime_config_t* runtime_config() const
{
return m_runtime_config;
}
};
pal::string_t get_runtime_config_json(const pal::string_t& app_path);
host_mode_t detect_operating_mode(const int argc, const pal::char_t* argv[], pal::string_t* own_dir = nullptr);
#endif // __LIBHOST_H__

View file

@ -0,0 +1,114 @@
// 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 "pal.h"
#include "utils.h"
#include "cpprest/json.h"
#include "runtime_config.h"
#include <cassert>
typedef web::json::value json_value;
runtime_config_t::runtime_config_t(const pal::string_t& path)
: m_fx_roll_fwd(true)
, m_path(path)
, m_portable(false)
, m_gc_server(_X("0"))
{
m_valid = ensure_parsed();
}
void parse_fx(const json_value& opts, pal::string_t* name, pal::string_t* version, bool* roll_fwd, bool* portable)
{
name->clear();
version->clear();
*roll_fwd = true;
*portable = false;
if (opts.is_null())
{
return;
}
const auto& opts_obj = opts.as_object();
auto framework = opts_obj.find(_X("framework"));
if (framework == opts_obj.end())
{
return;
}
*portable = true;
const auto& fx_obj = framework->second.as_object();
*name = fx_obj.at(_X("name")).as_string();
*version = fx_obj.at(_X("version")).as_string();
auto value = fx_obj.find(_X("rollForward"));
if (value == fx_obj.end())
{
return;
}
*roll_fwd = value->second.as_bool();
}
bool runtime_config_t::ensure_parsed()
{
pal::string_t retval;
if (!pal::file_exists(m_path))
{
// Not existing is not an error.
return true;
}
pal::ifstream_t file(m_path);
if (!file.good())
{
return false;
}
try
{
const auto root = json_value::parse(file);
const auto& json = root.as_object();
const auto iter = json.find(_X("runtimeOptions"));
if (iter != json.end())
{
parse_fx(iter->second, &m_fx_name, &m_fx_ver, &m_fx_roll_fwd, &m_portable);
}
}
catch (...)
{
return false;
}
return true;
}
const pal::string_t& runtime_config_t::get_gc_server() const
{
assert(m_valid);
return m_gc_server;
}
const pal::string_t& runtime_config_t::get_fx_name() const
{
assert(m_valid);
return m_fx_name;
}
const pal::string_t& runtime_config_t::get_fx_version() const
{
assert(m_valid);
return m_fx_ver;
}
bool runtime_config_t::get_fx_roll_fwd() const
{
assert(m_valid);
return m_fx_roll_fwd;
}
bool runtime_config_t::get_portable() const
{
return m_portable;
}

View file

@ -0,0 +1,29 @@
// 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 "pal.h"
class runtime_config_t
{
public:
runtime_config_t(const pal::string_t& path);
bool is_valid() { return m_valid; }
const pal::string_t& get_path() { return m_path; }
const pal::string_t& get_gc_server() const;
const pal::string_t& get_fx_version() const;
const pal::string_t& get_fx_name() const;
bool get_fx_roll_fwd() const;
bool get_portable() const;
private:
bool ensure_parsed();
pal::string_t m_gc_server;
pal::string_t m_fx_name;
pal::string_t m_fx_ver;
bool m_fx_roll_fwd;
pal::string_t m_path;
bool m_portable;
bool m_valid;
};

View file

@ -38,18 +38,33 @@ bool servicing_index_t::find_redirection(
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))
pal::string_t ni_root = m_patch_root;
append_path(&ni_root, get_arch());
// First prefer the architecture specific NI image.
pal::string_t paths[2] = { ni_root, m_patch_root };
for (pal::string_t& full_path : paths)
{
*redirection = full_path;
trace::verbose(_X("Servicing %s with %s"), stream.str().c_str(), redirection->c_str());
return true;
append_path(&full_path, iter->second.c_str());
if (pal::file_exists(full_path))
{
*redirection = full_path;
if (trace::is_enabled())
{
pal::string_t stream_str = stream.str();
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("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());
if (trace::is_enabled())
{
auto stream_str = stream.str();
trace::verbose(_X("Entry %s not serviced or file doesn't exist"), stream_str.c_str());
}
return false;
}
@ -112,7 +127,8 @@ void servicing_index_t::ensure_redirections()
if (trace::is_enabled())
{
trace::verbose(_X("Adding servicing entry %s => %s"), sstream.str().c_str(), str.substr(from).c_str());
auto stream_str = sstream.str();
trace::verbose(_X("Adding servicing entry %s => %s"), stream_str.c_str(), str.substr(from).c_str());
}
// Store just the filename.

View file

@ -33,4 +33,20 @@ if(WIN32)
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF")
set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF")
else()
add_compile_options(-Wno-unused-local-typedef)
endif()
if(CLI_CMAKE_PLATFORM_ARCH_I386)
add_definitions(-D_TARGET_X86_=1)
elseif(CLI_CMAKE_PLATFORM_ARCH_AMD64)
add_definitions(-D_TARGET_AMD64_=1)
else()
message(FATAL_ERROR "Unknown target architecture")
endif()
if(${CLI_CMAKE_RUNTIME_ID} STREQUAL "")
message(FATAL_ERROR "Runtime ID not specified")
else()
add_definitions(-DTARGET_RUNTIME_ID="${CLI_CMAKE_RUNTIME_ID}")
endif()

View file

@ -15,10 +15,12 @@
#include <unordered_map>
#include <memory>
#include <algorithm>
#include <cassert>
#if defined(_WIN32)
#include <Windows.h>
#define NOMINMAX
#include <windows.h>
#define HOST_EXE_NAME L"corehost.exe"
#define xerr std::wcerr
@ -93,11 +95,17 @@ namespace pal
typedef HMODULE dll_t;
typedef FARPROC proc_t;
pal::string_t to_string(int value);
bool getcwd(pal::string_t* recv);
inline int strcmp(const char_t* str1, const char_t* str2) { return ::wcscmp(str1, str2); }
inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::_wcsicmp(str1, str2); }
inline int strncmp(const char_t* str1, const char_t* str2, int len) { return ::wcsncmp(str1, str2, len); }
inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::_wcsnicmp(str1, str2, len); }
pal::string_t to_lower(const pal::string_t& in);
inline size_t strlen(const char_t* str) { return ::wcslen(str); }
inline void err_vprintf(const char_t* format, va_list vl) { ::vfwprintf(stderr, format, vl); ::fputws(_X("\r\n"), stderr); }
@ -126,10 +134,17 @@ namespace pal
typedef void* dll_t;
typedef void* proc_t;
pal::string_t to_string(int value);
bool getcwd(pal::string_t* recv);
inline int strcmp(const char_t* str1, const char_t* str2) { return ::strcmp(str1, str2); }
inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::strcasecmp(str1, str2); }
inline int strncmp(const char_t* str1, const char_t* str2, int len) { return ::strncmp(str1, str2, len); }
inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::strncasecmp(str1, str2, len); }
pal::string_t to_lower(const pal::string_t& in);
inline size_t strlen(const char_t* str) { return ::strlen(str); }
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; }

View file

@ -10,6 +10,8 @@
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
@ -20,6 +22,33 @@
#define symlinkEntrypointExecutable "/proc/curproc/exe"
#endif
pal::string_t pal::to_string(int value) { return std::to_string(value); }
pal::string_t pal::to_lower(const pal::string_t& in)
{
pal::string_t ret = in;
std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
return ret;
}
bool pal::getcwd(pal::string_t* recv)
{
recv->clear();
pal::char_t* buf = ::getcwd(nullptr, PATH_MAX + 1);
if (buf == nullptr)
{
if (errno == ENOENT)
{
return false;
}
perror("getcwd()");
return false;
}
recv->assign(buf);
::free(buf);
return true;
}
bool pal::find_coreclr(pal::string_t* recv)
{
pal::string_t candidate;
@ -133,8 +162,7 @@ bool pal::getenv(const pal::char_t* name, pal::string_t* recv)
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(), nullptr);
if (resolved == nullptr)
{
if (errno == ENOENT)
@ -145,6 +173,7 @@ bool pal::realpath(pal::string_t* path)
return false;
}
path->assign(resolved);
::free(resolved);
return true;
}

View file

@ -11,6 +11,18 @@
static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> g_converter;
pal::string_t pal::to_lower(const pal::string_t& in)
{
pal::string_t ret = in;
std::transform(ret.begin(), ret.end(), ret.begin(), ::towlower);
return ret;
}
pal::string_t pal::to_string(int value)
{
return std::to_wstring(value);
}
bool pal::find_coreclr(pal::string_t* recv)
{
pal::string_t candidate;
@ -31,6 +43,35 @@ bool pal::find_coreclr(pal::string_t* recv)
return false;
}
bool pal::getcwd(pal::string_t* recv)
{
recv->clear();
pal::char_t buf[MAX_PATH];
DWORD result = GetCurrentDirectoryW(MAX_PATH, buf);
if (result < MAX_PATH)
{
recv->assign(buf);
return true;
}
else if (result != 0)
{
std::vector<pal::char_t> str;
str.resize(result);
result = GetCurrentDirectoryW(str.size(), str.data());
assert(result <= str.size());
if (result != 0)
{
recv->assign(str.data());
return true;
}
}
assert(result == 0);
trace::error(_X("Failed to obtain working directory, HRESULT: 0x%X"), HRESULT_FROM_WIN32(GetLastError()));
return false;
}
bool pal::load_library(const char_t* path, dll_t* dll)
{
*dll = ::LoadLibraryW(path);

View file

@ -55,16 +55,43 @@ pal::string_t get_executable(const pal::string_t& filename)
return result;
}
pal::string_t get_filename(const pal::string_t& path)
pal::string_t strip_file_ext(const pal::string_t& path)
{
// Find the last dir separator
auto path_sep = path.find_last_of(DIR_SEPARATOR);
if (path_sep == pal::string_t::npos)
if (path.empty())
{
return pal::string_t(path);
return path;
}
return path.substr(0, path.rfind(_X('.')));
}
pal::string_t get_filename_without_ext(const pal::string_t& path)
{
if (path.empty())
{
return path;
}
return path.substr(path_sep + 1);
size_t name_pos = path.find_last_of(_X("/\\"));
size_t dot_pos = path.rfind(_X('.'));
size_t start_pos = (name_pos == pal::string_t::npos) ? 0 : (name_pos + 1);
size_t count = (dot_pos == pal::string_t::npos) ? pal::string_t::npos : (dot_pos - start_pos);
return path.substr(start_pos, count);
}
pal::string_t get_filename(const pal::string_t& path)
{
if (path.empty())
{
return path;
}
auto name_pos = path.find_last_of(DIR_SEPARATOR);
if (name_pos == pal::string_t::npos)
{
return path;
}
return path.substr(name_pos + 1);
}
pal::string_t get_directory(const pal::string_t& path)
@ -87,3 +114,49 @@ void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl)
(*path)[pos] = repl;
}
}
const pal::char_t* get_arch()
{
#if _TARGET_AMD64_
return _X("x64");
#elif _TARGET_X86_
return _X("x86");
#else
#error "Unknown target"
#endif
}
bool parse_known_args(
const int argc,
const pal::char_t* argv[],
const std::vector<pal::string_t>& known_opts,
std::unordered_map<pal::string_t, pal::string_t>* opts,
int* num_args)
{
int arg_i = *num_args;
while (arg_i < argc)
{
pal::string_t arg = argv[arg_i];
if (std::find(known_opts.begin(), known_opts.end(), pal::to_lower(arg)) == known_opts.end())
{
// Unknown argument.
break;
}
// Known argument, so expect one more arg (value) to be present.
if (arg_i + 1 >= argc)
{
return false;
}
(*opts)[arg] = argv[arg_i + 1];
// Increment for both the option and its value.
arg_i += 2;
}
*num_args = arg_i;
return true;
}

View file

@ -10,8 +10,17 @@ bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool mat
bool starts_with(const pal::string_t& value, const pal::string_t& prefix, bool match_case);
pal::string_t get_executable(const pal::string_t& filename);
pal::string_t get_directory(const pal::string_t& path);
pal::string_t strip_file_ext(const pal::string_t& path);
pal::string_t get_filename(const pal::string_t& path);
pal::string_t get_filename_without_ext(const pal::string_t& path);
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);
const pal::char_t* get_arch();
bool parse_known_args(
const int argc,
const pal::char_t* argv[],
const std::vector<pal::string_t>& known_opts,
std::unordered_map<pal::string_t, pal::string_t>* opts,
int* num_args);
#endif

View file

@ -2,64 +2,140 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "trace.h"
#include "pal.h"
#include "utils.h"
#include "libhost.h"
#include "corehost.h"
#include "fx_ver.h"
#include "error_codes.h"
#include "policy_load.h"
#include <array>
extern int corehost_main(const int argc, const pal::char_t* argv[]);
#define LIBFXR_NAME MAKE_LIBNAME("hostfxr")
namespace
bool corehost_t::hostpolicy_exists_in_svc(pal::string_t* resolved_dir)
{
enum StatusCode
{
Success = 0,
CoreHostLibLoadFailure = 0x41,
CoreHostLibMissingFailure = 0x42,
CoreHostEntryPointFailure = 0x43,
CoreHostCurExeFindFailure = 0x44,
};
typedef int (*corehost_main_fn) (const int argc, const pal::char_t* argv[]);
// -----------------------------------------------------------------------------
// Load the corehost library from the path specified
//
// Parameters:
// lib_dir - dir path to the corehost library
// h_host - handle to the library which will be kept live
// main_fn - Contains the entrypoint "corehost_main" when returns success.
//
// Returns:
// Non-zero exit code on failure. "main_fn" contains "corehost_main"
// entrypoint on success.
//
StatusCode load_host_lib(const pal::string_t& lib_dir, pal::dll_t* h_host, corehost_main_fn* main_fn)
{
pal::string_t host_path = lib_dir;
append_path(&host_path, LIBHOST_NAME);
// Missing library
if (!pal::file_exists(host_path))
#ifdef COREHOST_PACKAGE_SERVICING
pal::string_t svc_dir;
if (!pal::getenv(_X("DOTNET_SERVICING"), &svc_dir))
{
return StatusCode::CoreHostLibMissingFailure;
return false;
}
// Load library
if (!pal::load_library(host_path.c_str(), h_host))
pal::string_t path = svc_dir;
append_path(&path, COREHOST_PACKAGE_NAME);
append_path(&path, COREHOST_PACKAGE_VERSION);
append_path(&path, COREHOST_PACKAGE_COREHOST_RELATIVE_DIR);
if (library_exists_in_dir(path, LIBHOST_NAME))
{
trace::info(_X("Load library of %s failed"), host_path.c_str());
resolved_dir->assign(path);
}
return true;
#else
return false;
#endif
}
pal::string_t corehost_t::resolve_fxr_path(const pal::string_t& own_dir)
{
pal::string_t fxr_path;
pal::string_t fxr_dir = own_dir;
append_path(&fxr_dir, _X("dotnethost"));
append_path(&fxr_dir, _X("fxr"));
if (pal::directory_exists(fxr_dir))
{
trace::info(_X("Reading fx resolver directory=[%s]"), fxr_dir.c_str());
std::vector<pal::string_t> list;
pal::readdir(fxr_dir, &list);
fx_ver_t max_ver(-1, -1, -1);
for (const auto& dir : list)
{
trace::info(_X("Considering fxr version=[%s]..."), dir.c_str());
pal::string_t ver = get_filename(dir);
fx_ver_t fx_ver(-1, -1, -1);
if (fx_ver_t::parse(ver, &fx_ver, false))
{
max_ver = std::max(max_ver, fx_ver);
}
}
pal::string_t max_ver_str = max_ver.as_str();
append_path(&fxr_dir, max_ver_str.c_str());
trace::info(_X("Detected latest fxr version=[%s]..."), fxr_dir.c_str());
}
const pal::string_t* dirs[] = { &fxr_dir, &own_dir };
for (const auto& dir : dirs)
{
trace::info(_X("Considering fxr dir=[%s]..."), fxr_dir.c_str());
if (policy_load_t::library_exists_in_dir(*dir, LIBFXR_NAME, &fxr_path))
{
trace::info(_X("Resolved fxr [%s]..."), fxr_path.c_str());
return fxr_path;
}
}
return pal::string_t();
}
int corehost_t::resolve_fx_and_execute_app(const pal::string_t& own_dir, const int argc, const pal::char_t* argv[])
{
pal::dll_t fxr;
pal::string_t fxr_path = resolve_fxr_path(own_dir);
// Load library
if (!pal::load_library(fxr_path.c_str(), &fxr))
{
trace::info(_X("Load library of %s failed"), fxr_path.c_str());
return StatusCode::CoreHostLibLoadFailure;
}
// Obtain entrypoint symbol
*main_fn = (corehost_main_fn) pal::get_symbol(*h_host, "corehost_main");
return (*main_fn != nullptr)
? StatusCode::Success
: StatusCode::CoreHostEntryPointFailure;
// Obtain entrypoint symbols
hostfxr_main_fn main_fn = (hostfxr_main_fn) pal::get_symbol(fxr, "hostfxr_main");
return main_fn(argc, argv);
}
}; // end of anonymous namespace
int corehost_t::run(const int argc, const pal::char_t* argv[])
{
pal::string_t own_dir;
auto mode = detect_operating_mode(argc, argv, &own_dir);
switch (mode)
{
case muxer:
trace::info(_X("Host operating in Muxer mode"));
return resolve_fx_and_execute_app(own_dir, argc, argv);
case split_fx:
{
trace::info(_X("Host operating in split mode; own dir=[%s]"), own_dir.c_str());
corehost_init_t init(_X(""), _X(""), own_dir, host_mode_t::split_fx, nullptr);
return policy_load_t::execute_app(own_dir, &init, argc, argv);
}
case standalone:
{
trace::info(_X("Host operating from standalone app dir %s"), own_dir.c_str());
pal::string_t svc_dir;
corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::standalone, nullptr);
return policy_load_t::execute_app(
hostpolicy_exists_in_svc(&svc_dir) ? svc_dir : own_dir, &init, argc, argv);
}
return StatusCode::CoreHostLibMissingFailure;
default:
trace::error(_X("Unknown mode detected or could not resolve the mode."));
return StatusCode::CoreHostResolveModeFailure;
}
}
#include <cassert>
#include "deps_format.h"
#if defined(_WIN32)
int __cdecl wmain(const int argc, const pal::char_t* argv[])
@ -69,56 +145,19 @@ int main(const int argc, const pal::char_t* argv[])
{
trace::setup();
pal::dll_t corehost;
//deps_json_t deps(true, _X("H:\\code\\sharedfx\\PortableApp\\PortableAppWithNative.deps.json"));
//deps_json_t deps2(false, _X("H:\\code\\sharedfx\\StandaloneApp\\StandaloneApp.deps.json"));
#ifdef COREHOST_PACKAGE_SERVICING
// No custom host asked, so load the corehost if serviced first.
pal::string_t svc_dir;
if (pal::getenv(_X("DOTNET_SERVICING"), &svc_dir))
if (trace::is_enabled())
{
pal::string_t path = svc_dir;
append_path(&path, COREHOST_PACKAGE_NAME);
append_path(&path, COREHOST_PACKAGE_VERSION);
append_path(&path, COREHOST_PACKAGE_COREHOST_RELATIVE_DIR);
corehost_main_fn host_main;
StatusCode code = load_host_lib(path, &corehost, &host_main);
if (code != StatusCode::Success)
trace::info(_X("--- Invoked host main = {"));
for (int i = 0; i < argc; ++i)
{
trace::info(_X("Failed to load host library from servicing dir: %s; Status=%08X"), path.c_str(), code);
// Ignore all errors for the servicing case, and proceed to the next step.
}
else
{
trace::info(_X("Calling host entrypoint from library at servicing dir %s"), path.c_str());
return host_main(argc, argv);
trace::info(_X("%s"), argv[i]);
}
trace::info(_X("}"));
}
#endif
// Get current path to look for the library app locally.
pal::string_t own_path;
if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path))
{
trace::error(_X("Failed to locate current executable"));
return StatusCode::CoreHostCurExeFindFailure;
}
// Local load of the corehost library.
auto own_dir = get_directory(own_path);
corehost_main_fn host_main;
StatusCode code = load_host_lib(own_dir, &corehost, &host_main);
switch (code)
{
// Success, call the entrypoint.
case StatusCode::Success:
trace::info(_X("Calling host entrypoint from library at own dir %s"), own_dir.c_str());
return host_main(argc, argv);
// Some other fatal error including StatusCode::CoreHostLibMissingFailure.
default:
trace::error(_X("Error loading the host library from own dir: %s; Status=%08X"), own_dir.c_str(), code);
return code;
}
corehost_t corehost;
return corehost.run(argc, argv);
}

36
src/corehost/corehost.h Normal file
View file

@ -0,0 +1,36 @@
// 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 "pal.h"
#include "libhost.h"
#include "policy_load.h"
typedef int(*hostfxr_main_fn) (const int argc, const pal::char_t* argv[]);
class corehost_t
{
public:
int run(const int argc, const pal::char_t* argv[]);
static int execute_app(
const pal::string_t& policy_dir,
const pal::string_t& fx_dir,
const runtime_config_t* config,
const int argc,
const pal::char_t* argv[]);
private:
static int load_host_library(
const pal::string_t& lib_dir,
pal::dll_t* h_host,
corehost_load_fn* load_fn,
corehost_main_fn* main_fn,
corehost_unload_fn* unload_fn);
pal::string_t resolve_fxr_path(const pal::string_t& own_dir);
int resolve_fx_and_execute_app(const pal::string_t& own_dir, const int argc, const pal::char_t* argv[]);
static bool hostpolicy_exists_in_svc(pal::string_t* resolved_path);
static bool library_exists_in_dir(const pal::string_t& lib_dir, const pal::string_t& lib_name, pal::string_t* p_host_path);
};

View file

@ -0,0 +1,29 @@
// 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 __ERROR_CODES_H__
#define __ERROR_CODES_H__
enum StatusCode
{
Success = 0,
InvalidArgFailure = 0x81,
CoreHostLibLoadFailure = 0x82,
CoreHostLibMissingFailure = 0x83,
CoreHostEntryPointFailure = 0x84,
CoreHostCurExeFindFailure = 0x85,
CoreHostResolveModeFailure = 0x86,
CoreClrResolveFailure = 0x87,
CoreClrBindFailure = 0x88,
CoreClrInitFailure = 0x89,
CoreClrExeFailure = 0x90,
ResolverInitFailure = 0x91,
ResolverResolveFailure = 0x92,
LibHostCurExeFindFailure = 0x93,
LibHostInitFailure = 0x94,
LibHostMuxFailure = 0x95,
LibHostExecModeFailure = 0x96,
LibHostSdkFindFailure = 0x97,
LibHostInvalidArgs = 0x98,
InvalidConfigFile = 0x99,
};
#endif // __ERROR_CODES_H__

View file

@ -0,0 +1,79 @@
// 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 "policy_load.h"
#include "corehost.h"
bool policy_load_t::library_exists_in_dir(const pal::string_t& lib_dir, const pal::string_t& lib_name, pal::string_t* p_host_path)
{
pal::string_t host_path = lib_dir;
append_path(&host_path, lib_name.c_str());
if (!pal::file_exists(host_path))
{
return false;
}
if (p_host_path)
{
*p_host_path = host_path;
}
return true;
}
int policy_load_t::load_host_library(
const pal::string_t& lib_dir,
pal::dll_t* h_host,
corehost_load_fn* load_fn,
corehost_main_fn* main_fn,
corehost_unload_fn* unload_fn)
{
pal::string_t host_path;
if (!library_exists_in_dir(lib_dir, LIBHOST_NAME, &host_path))
{
return StatusCode::CoreHostLibMissingFailure;
}
// Load library
if (!pal::load_library(host_path.c_str(), h_host))
{
trace::info(_X("Load library of %s failed"), host_path.c_str());
return StatusCode::CoreHostLibLoadFailure;
}
// Obtain entrypoint symbols
*load_fn = (corehost_load_fn)pal::get_symbol(*h_host, "corehost_load");
*main_fn = (corehost_main_fn)pal::get_symbol(*h_host, "corehost_main");
*unload_fn = (corehost_unload_fn)pal::get_symbol(*h_host, "corehost_unload");
return (*main_fn) && (*load_fn) && (*unload_fn)
? StatusCode::Success
: StatusCode::CoreHostEntryPointFailure;
}
int policy_load_t::execute_app(
const pal::string_t& impl_dll_dir,
const corehost_init_t* init,
const int argc,
const pal::char_t* argv[])
{
pal::dll_t corehost;
corehost_main_fn host_main = nullptr;
corehost_load_fn host_load = nullptr;
corehost_unload_fn host_unload = nullptr;
int code = load_host_library(impl_dll_dir, &corehost, &host_load, &host_main, &host_unload);
if (code != StatusCode::Success)
{
trace::error(_X("Could not load host policy library [%s]"), impl_dll_dir.c_str());
return code;
}
if ((code = host_load(init)) == 0)
{
code = host_main(argc, argv);
(void) host_unload();
}
return code;
}

View file

@ -0,0 +1,38 @@
// 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 __POLICY_LOAD_H__
#define __POLICY_LOAD_H__
#include "pal.h"
#include "utils.h"
#include "trace.h"
#include "error_codes.h"
class corehost_init_t;
class runtime_config_t;
typedef int(*corehost_load_fn) (const corehost_init_t* init);
typedef int(*corehost_main_fn) (const int argc, const pal::char_t* argv[]);
typedef int(*corehost_unload_fn) ();
class policy_load_t
{
public:
static int execute_app(
const pal::string_t& impl_dll_dir,
const corehost_init_t* init,
const int argc,
const pal::char_t* argv[]);
static bool library_exists_in_dir(const pal::string_t& lib_dir, const pal::string_t& lib_name, pal::string_t* p_host_path = nullptr);
static int load_host_library(
const pal::string_t& lib_dir,
pal::dll_t* h_host,
corehost_load_fn* load_fn,
corehost_main_fn* main_fn,
corehost_unload_fn* unload_fn);
};
#endif // __POLICY_LOAD_H__