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:
parent
6bd9d80fed
commit
f615f74a39
49 changed files with 14979 additions and 592 deletions
|
@ -70,15 +70,20 @@ namespace Microsoft.DotNet.Cli.Build
|
||||||
var configuration = c.BuildContext.Get<string>("Configuration");
|
var configuration = c.BuildContext.Get<string>("Configuration");
|
||||||
|
|
||||||
// Run the build
|
// 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))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
// Why does Windows directly call cmake but Linux/Mac calls "build.sh" in the corehost dir?
|
// 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.
|
// 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 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 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",
|
ExecIn(cmakeOut, "cmake",
|
||||||
Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost"),
|
Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost"),
|
||||||
archMacro,
|
archMacro,
|
||||||
|
ridMacro,
|
||||||
"-G",
|
"-G",
|
||||||
visualStudio);
|
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", 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.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", "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
|
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
|
// Copy the output out
|
||||||
File.Copy(Path.Combine(cmakeOut, "cli", "corehost"), Path.Combine(Dirs.Corehost, "corehost"), overwrite: true);
|
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", "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();
|
return c.Success();
|
||||||
|
@ -193,6 +205,7 @@ namespace Microsoft.DotNet.Cli.Build
|
||||||
// Copy corehost
|
// 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, $"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}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
|
// Corehostify binaries
|
||||||
foreach (var binaryToCorehostify in BinariesForCoreHost)
|
foreach (var binaryToCorehostify in BinariesForCoreHost)
|
||||||
|
|
|
@ -98,7 +98,8 @@ namespace Microsoft.DotNet.Cli.Utils
|
||||||
|
|
||||||
if (depsFilePath != null)
|
if (depsFilePath != null)
|
||||||
{
|
{
|
||||||
arguments.Add($"--depsfile:{depsFilePath}");
|
arguments.Add("--depsfile");
|
||||||
|
arguments.Add(depsFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
arguments.AddRange(commandArguments);
|
arguments.AddRange(commandArguments);
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace Microsoft.DotNet.Cli.Utils
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.Deps;
|
var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.DepsJson;
|
||||||
|
|
||||||
var dependencyLibraries = GetAllDependencyLibraries(projectContext);
|
var dependencyLibraries = GetAllDependencyLibraries(projectContext);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,8 @@ namespace Microsoft.DotNet.Cli.Utils
|
||||||
public static readonly string HostExecutableName = "corehost" + ExeSuffix;
|
public static readonly string HostExecutableName = "corehost" + ExeSuffix;
|
||||||
public static readonly string[] HostBinaryNames = new string[] {
|
public static readonly string[] HostBinaryNames = new string[] {
|
||||||
HostExecutableName,
|
HostExecutableName,
|
||||||
(CurrentPlatform == Platform.Windows ? "hostpolicy" : "libhostpolicy") + DynamicLibSuffix
|
(CurrentPlatform == Platform.Windows ? "hostpolicy" : "libhostpolicy") + DynamicLibSuffix,
|
||||||
|
(CurrentPlatform == Platform.Windows ? "hostfxr" : "libhostfxr") + DynamicLibSuffix
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,46 @@ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli
|
||||||
done
|
done
|
||||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
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)"
|
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
|
make
|
||||||
|
|
|
@ -14,13 +14,26 @@ include(setup.cmake)
|
||||||
|
|
||||||
set (CMAKE_CXX_STANDARD 11)
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
|
|
||||||
|
include_directories(..)
|
||||||
include_directories(../common)
|
include_directories(../common)
|
||||||
include_directories(.)
|
include_directories(.)
|
||||||
|
include_directories(./fxr)
|
||||||
|
include_directories(./json/casablanca/include)
|
||||||
|
|
||||||
# CMake does not recommend using globbing since it messes with the freshness checks
|
# CMake does not recommend using globbing since it messes with the freshness checks
|
||||||
set(SOURCES
|
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/trace.cpp
|
||||||
../common/utils.cpp)
|
../common/utils.cpp)
|
||||||
|
|
||||||
|
@ -32,6 +45,9 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(corehost ${SOURCES})
|
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
|
# 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)")
|
if(${CMAKE_CXX_COMPILER_ID} MATCHES "(Clang|GNU)")
|
||||||
|
@ -44,3 +60,4 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(dll)
|
add_subdirectory(dll)
|
||||||
|
add_subdirectory(fxr)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "args.h"
|
#include "args.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "coreclr.h"
|
#include "coreclr.h"
|
||||||
|
#include "libhost.h"
|
||||||
|
|
||||||
arguments_t::arguments_t() :
|
arguments_t::arguments_t() :
|
||||||
managed_application(_X("")),
|
managed_application(_X("")),
|
||||||
|
@ -11,11 +12,8 @@ arguments_t::arguments_t() :
|
||||||
app_dir(_X("")),
|
app_dir(_X("")),
|
||||||
app_argc(0),
|
app_argc(0),
|
||||||
app_argv(nullptr),
|
app_argv(nullptr),
|
||||||
nuget_packages(_X("")),
|
|
||||||
dotnet_packages_cache(_X("")),
|
dotnet_packages_cache(_X("")),
|
||||||
dotnet_servicing(_X("")),
|
dotnet_servicing(_X("")),
|
||||||
dotnet_runtime_servicing(_X("")),
|
|
||||||
dotnet_home(_X("")),
|
|
||||||
deps_path(_X(""))
|
deps_path(_X(""))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -26,12 +24,13 @@ void display_help()
|
||||||
_X("Usage: " HOST_EXE_NAME " [ASSEMBLY] [ARGUMENTS]\n")
|
_X("Usage: " HOST_EXE_NAME " [ASSEMBLY] [ARGUMENTS]\n")
|
||||||
_X("Execute the specified managed assembly with the passed in arguments\n\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("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");
|
_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
|
// Get the full name of the application
|
||||||
if (!pal::get_own_executable_path(&args.own_path) || !pal::realpath(&args.own_path))
|
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_name = get_filename(args.own_path);
|
||||||
auto own_dir = get_directory(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
|
// corerun mode. First argument is managed app
|
||||||
if (argc < 2)
|
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;
|
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]);
|
return false;
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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())
|
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.append(app_base);
|
||||||
args.deps_path.push_back(DIR_SEPARATOR);
|
args.deps_path.push_back(DIR_SEPARATOR);
|
||||||
args.deps_path.append(app_name, 0, app_name.find_last_of(_X(".")));
|
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_PACKAGES_CACHE"), &args.dotnet_packages_cache);
|
||||||
pal::getenv(_X("DOTNET_SERVICING"), &args.dotnet_servicing);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "pal.h"
|
#include "pal.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "libhost.h"
|
||||||
static const pal::string_t s_deps_arg_prefix = _X("--depsfile:");
|
|
||||||
|
|
||||||
struct arguments_t
|
struct arguments_t
|
||||||
{
|
{
|
||||||
|
@ -18,7 +17,7 @@ struct arguments_t
|
||||||
pal::string_t dotnet_servicing;
|
pal::string_t dotnet_servicing;
|
||||||
pal::string_t dotnet_runtime_servicing;
|
pal::string_t dotnet_runtime_servicing;
|
||||||
pal::string_t dotnet_home;
|
pal::string_t dotnet_home;
|
||||||
pal::string_t nuget_packages;
|
pal::string_t probe_dir;
|
||||||
pal::string_t dotnet_packages_cache;
|
pal::string_t dotnet_packages_cache;
|
||||||
pal::string_t managed_application;
|
pal::string_t managed_application;
|
||||||
|
|
||||||
|
@ -28,6 +27,6 @@ struct arguments_t
|
||||||
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
|
#endif // ARGS_H
|
||||||
|
|
144
src/corehost/cli/deps_entry.cpp
Normal file
144
src/corehost/cli/deps_entry.cpp
Normal 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);
|
||||||
|
}
|
38
src/corehost/cli/deps_entry.h
Normal file
38
src/corehost/cli/deps_entry.h
Normal 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_
|
305
src/corehost/cli/deps_format.cpp
Normal file
305
src/corehost/cli/deps_format.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
113
src/corehost/cli/deps_format.h
Normal file
113
src/corehost/cli/deps_format.h
Normal 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_
|
|
@ -6,80 +6,12 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "deps_format.h"
|
||||||
#include "deps_resolver.h"
|
#include "deps_resolver.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
namespace
|
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
|
// A uniqifying append helper that doesn't let two entries with the same
|
||||||
// "asset_name" be part of the "output" paths.
|
// "asset_name" be part of the "output" paths.
|
||||||
|
@ -106,28 +38,6 @@ void add_tpa_asset(
|
||||||
items->insert(asset_name);
|
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
|
// A uniqifying append helper that doesn't let two "paths" to be identical in
|
||||||
// the "output" string.
|
// the "output" string.
|
||||||
|
@ -157,234 +67,16 @@ void add_unique_path(
|
||||||
|
|
||||||
} // end of anonymous namespace
|
} // 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
|
// Load local assemblies by priority order of their file extensions and
|
||||||
// unique-fied by their simple name.
|
// 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.
|
// 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") };
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Do a case insensitive lookup.
|
|
||||||
// Already added entry for this asset, by priority order skip this ext
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add entry for this asset
|
// Add entry for this asset
|
||||||
pal::string_t file_path = dir + DIR_SEPARATOR + file;
|
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());
|
trace::verbose(_X("Adding %s to %s assembly set from %s"), file_name.c_str(), dir_name.c_str(), file_path.c_str());
|
||||||
m_local_assemblies.emplace(file_name, file_path);
|
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_dir,
|
||||||
const pal::string_t& package_cache_dir)
|
const pal::string_t& package_cache_dir)
|
||||||
{
|
{
|
||||||
// Runtime servicing
|
auto process_coreclr = [&]
|
||||||
trace::verbose(_X("Probing for CoreCLR in servicing dir=[%s]"), m_runtime_svc.c_str());
|
(bool is_portable, const pal::string_t& deps_dir, deps_json_t* deps) -> pal::string_t
|
||||||
if (!m_runtime_svc.empty())
|
|
||||||
{
|
{
|
||||||
pal::string_t svc_clr = m_runtime_svc;
|
pal::string_t candidate;
|
||||||
append_path(&svc_clr, _X("runtime"));
|
|
||||||
append_path(&svc_clr, _X("coreclr"));
|
|
||||||
|
|
||||||
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.
|
// 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;
|
||||||
pal::string_t coreclr_cache;
|
if (!package_cache_dir.empty())
|
||||||
if (m_coreclr_index >= 0 && !package_cache_dir.empty() &&
|
{
|
||||||
m_deps_entries[m_coreclr_index].to_hash_matched_path(package_cache_dir, &coreclr_cache))
|
if (deps->has_coreclr_entry())
|
||||||
{
|
{
|
||||||
return get_directory(coreclr_cache);
|
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.
|
// Deps directory: lookup relative path if portable, else look sxs.
|
||||||
trace::verbose(_X("Probing for CoreCLR in app directory=[%s]"), app_dir.c_str());
|
if (is_portable)
|
||||||
if (coreclr_exists_in_dir(app_dir))
|
{
|
||||||
{
|
pal::string_t coreclr_portable;
|
||||||
return app_dir;
|
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
|
// 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;
|
||||||
pal::string_t coreclr_package;
|
if (!package_dir.empty())
|
||||||
if (m_coreclr_index >= 0 && !package_dir.empty() &&
|
{
|
||||||
m_deps_entries[m_coreclr_index].to_full_path(package_dir, &coreclr_package))
|
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
|
// Use platform-specific search algorithm
|
||||||
|
@ -516,19 +257,33 @@ void deps_resolver_t::resolve_tpa_list(
|
||||||
const pal::string_t& clr_dir,
|
const pal::string_t& clr_dir,
|
||||||
pal::string_t* output)
|
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.
|
// 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;
|
std::set<pal::string_t> items;
|
||||||
|
|
||||||
add_mscorlib_to_tpa(clr_dir, &items, output);
|
auto process_entry = [&](bool is_portable, deps_json_t* deps, const deps_entry_t& entry)
|
||||||
|
|
||||||
for (const deps_entry_t& entry : m_deps_entries)
|
|
||||||
{
|
{
|
||||||
// Is this asset a "runtime" type?
|
// 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;
|
pal::string_t candidate;
|
||||||
|
@ -539,45 +294,63 @@ void deps_resolver_t::resolve_tpa_list(
|
||||||
{
|
{
|
||||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
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))
|
else if (entry.to_hash_matched_path(package_cache_dir, &candidate))
|
||||||
{
|
{
|
||||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||||
}
|
}
|
||||||
// Is this entry present locally?
|
// 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_sxs_assemblies.find(entry.asset_name)->second, &items, output);
|
||||||
add_tpa_asset(entry.asset_name, m_local_assemblies.find(entry.asset_name)->second, &items, output);
|
|
||||||
}
|
}
|
||||||
// Is this entry present in the package restore dir?
|
// The app is portable so the asset should be picked up from relative subpath.
|
||||||
else if (entry.to_full_path(package_dir, &candidate))
|
else if (is_portable && deps->try_ni(entry).to_full_path(app_dir, &candidate))
|
||||||
{
|
{
|
||||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
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
|
// Finally, if the deps file wasn't present or has missing entries, then
|
||||||
// add the app local assemblies to the TPA.
|
// 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);
|
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:
|
// Description:
|
||||||
// This general purpose function specifies priority order of directory lookup
|
// This general purpose function specifies priority order of directory lookup
|
||||||
// for both native images and culture specific resource images. Lookup for
|
// for both native images and resources specific resource images. Lookup for
|
||||||
// culture assemblies is done by looking up two levels above from the file
|
// 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
|
// path. Lookup for native images is done by looking up one level from the
|
||||||
// file path.
|
// file path.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// asset_type - The type of the asset that needs lookup, currently
|
// 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
|
// app_dir - The application local directory
|
||||||
// package_dir - The directory path to where packages are restored
|
// package_dir - The directory path to where packages are restored
|
||||||
// package_cache_dir - The directory path to secondary cache for packages
|
// 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,
|
const pal::string_t& clr_dir,
|
||||||
pal::string_t* output)
|
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
|
// 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));
|
return get_directory(get_directory(str));
|
||||||
};
|
};
|
||||||
// For native assemblies, obtain the directory path from the file path
|
// 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) {
|
std::function<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) {
|
||||||
return get_directory(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::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
|
// Fill the "output" with serviced DLL directories if they are serviceable
|
||||||
// and have an entry present.
|
// 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;
|
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))
|
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);
|
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;
|
pal::string_t candidate;
|
||||||
|
|
||||||
// Take care of the secondary cache path
|
// Take care of the secondary cache path
|
||||||
if (!package_cache_dir.empty())
|
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);
|
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
|
// App local path
|
||||||
add_unique_path(asset_type, app_dir, &items, output);
|
add_unique_path(asset_type, app_dir, &items, output);
|
||||||
|
|
||||||
// Take care of the package restore path
|
// For portable path, the app relative directory must be used.
|
||||||
if (!package_dir.empty())
|
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))
|
if (entry.asset_type == asset_type && entry.to_full_path(package_dir, &candidate))
|
||||||
{
|
{
|
||||||
add_unique_path(asset_type, action(candidate), &items, output);
|
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
|
// 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:
|
// Parameters:
|
||||||
// app_dir - The application local directory
|
// 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_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("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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,48 +8,45 @@
|
||||||
|
|
||||||
#include "pal.h"
|
#include "pal.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "deps_format.h"
|
||||||
|
#include "deps_entry.h"
|
||||||
#include "servicing_index.h"
|
#include "servicing_index.h"
|
||||||
|
#include "runtime_config.h"
|
||||||
struct deps_entry_t
|
|
||||||
{
|
|
||||||
pal::string_t library_type;
|
|
||||||
pal::string_t library_name;
|
|
||||||
pal::string_t library_version;
|
|
||||||
pal::string_t library_hash;
|
|
||||||
pal::string_t asset_type;
|
|
||||||
pal::string_t asset_name;
|
|
||||||
pal::string_t relative_path;
|
|
||||||
bool is_serviceable;
|
|
||||||
|
|
||||||
// Given a "base" dir, yield the relative path in the package layout.
|
|
||||||
bool to_full_path(const pal::string_t& root, pal::string_t* str) const;
|
|
||||||
|
|
||||||
// Given a "base" dir, yield the relative path in the package layout only if
|
|
||||||
// the hash matches contents of the hash file.
|
|
||||||
bool to_hash_matched_path(const pal::string_t& root, pal::string_t* str) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Probe paths to be resolved for ordering
|
// Probe paths to be resolved for ordering
|
||||||
struct probe_paths_t
|
struct probe_paths_t
|
||||||
{
|
{
|
||||||
pal::string_t tpa;
|
pal::string_t tpa;
|
||||||
pal::string_t native;
|
pal::string_t native;
|
||||||
pal::string_t culture;
|
pal::string_t resources;
|
||||||
};
|
};
|
||||||
|
|
||||||
class deps_resolver_t
|
class deps_resolver_t
|
||||||
{
|
{
|
||||||
public:
|
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_svc(args.dotnet_servicing)
|
||||||
, m_runtime_svc(args.dotnet_runtime_servicing)
|
, m_fx_dir(fx_dir)
|
||||||
, m_coreclr_index(-1)
|
, 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(
|
bool resolve_probe_paths(
|
||||||
const pal::string_t& app_dir,
|
const pal::string_t& app_dir,
|
||||||
|
@ -63,11 +60,24 @@ public:
|
||||||
const pal::string_t& package_dir,
|
const pal::string_t& package_dir,
|
||||||
const pal::string_t& package_cache_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:
|
private:
|
||||||
|
|
||||||
bool load();
|
static pal::string_t get_fx_deps(const pal::string_t& fx_dir, const pal::string_t& fx_name)
|
||||||
|
{
|
||||||
bool parse_deps_file(const arguments_t& args);
|
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.
|
// Resolve order for TPA lookup.
|
||||||
void resolve_tpa_list(
|
void resolve_tpa_list(
|
||||||
|
@ -86,30 +96,42 @@ private:
|
||||||
const pal::string_t& clr_dir,
|
const pal::string_t& clr_dir,
|
||||||
pal::string_t* output);
|
pal::string_t* output);
|
||||||
|
|
||||||
// Populate local assemblies from app_dir listing.
|
// Populate assemblies from the directory.
|
||||||
void get_local_assemblies(const pal::string_t& dir);
|
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 to resolve serviced assembly paths.
|
||||||
servicing_index_t m_svc;
|
servicing_index_t m_svc;
|
||||||
|
|
||||||
// Runtime servicing directory.
|
// Framework deps file.
|
||||||
pal::string_t m_runtime_svc;
|
pal::string_t m_fx_dir;
|
||||||
|
|
||||||
// Map of simple name -> full path of local assemblies populated in priority
|
// Map of simple name -> full path of local/fx assemblies populated
|
||||||
// order of their extensions.
|
// in priority order of their extensions.
|
||||||
std::unordered_map<pal::string_t, pal::string_t> m_local_assemblies;
|
std::unordered_map<pal::string_t, pal::string_t> m_sxs_assemblies;
|
||||||
|
|
||||||
// Entries in the dep file
|
|
||||||
std::vector<deps_entry_t> m_deps_entries;
|
|
||||||
|
|
||||||
// Special entry for coreclr in the deps entries
|
// Special entry for coreclr in the deps entries
|
||||||
int m_coreclr_index;
|
int m_coreclr_index;
|
||||||
|
|
||||||
// The dep file path
|
// The filepath for the app deps
|
||||||
pal::string_t m_deps_path;
|
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
|
// Is the deps file valid
|
||||||
bool m_deps_valid;
|
bool m_deps_valid;
|
||||||
|
|
||||||
|
// Is the deps file portable app?
|
||||||
|
bool m_portable;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DEPS_RESOLVER_H
|
#endif // DEPS_RESOLVER_H
|
||||||
|
|
|
@ -13,16 +13,24 @@ endif()
|
||||||
include(../setup.cmake)
|
include(../setup.cmake)
|
||||||
|
|
||||||
include_directories(../../common)
|
include_directories(../../common)
|
||||||
|
include_directories(../json/casablanca/include)
|
||||||
|
|
||||||
# CMake does not recommend using globbing since it messes with the freshness checks
|
# CMake does not recommend using globbing since it messes with the freshness checks
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
../../common/trace.cpp
|
../../common/trace.cpp
|
||||||
../../common/utils.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
|
../args.cpp
|
||||||
../hostpolicy.cpp
|
../hostpolicy.cpp
|
||||||
../coreclr.cpp
|
../coreclr.cpp
|
||||||
../deps_resolver.cpp
|
../deps_resolver.cpp
|
||||||
|
../deps_format.cpp
|
||||||
|
../deps_entry.cpp
|
||||||
../servicing_index.cpp)
|
../servicing_index.cpp)
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,6 +40,8 @@ else()
|
||||||
list(APPEND SOURCES ../../common/pal.unix.cpp)
|
list(APPEND SOURCES ../../common/pal.unix.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_definitions(-D_NO_ASYNCRTIMP)
|
||||||
|
add_definitions(-D_NO_PPLXIMP)
|
||||||
add_definitions(-DCOREHOST_MAKE_DLL=1)
|
add_definitions(-DCOREHOST_MAKE_DLL=1)
|
||||||
|
|
||||||
add_library(hostpolicy SHARED ${SOURCES})
|
add_library(hostpolicy SHARED ${SOURCES})
|
||||||
|
|
45
src/corehost/cli/fxr/CMakeLists.txt
Normal file
45
src/corehost/cli/fxr/CMakeLists.txt
Normal 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})
|
||||||
|
|
||||||
|
|
298
src/corehost/cli/fxr/fx_muxer.cpp
Normal file
298
src/corehost/cli/fxr/fx_muxer.cpp
Normal 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);
|
||||||
|
}
|
16
src/corehost/cli/fxr/fx_muxer.h
Normal file
16
src/corehost/cli/fxr/fx_muxer.h
Normal 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);
|
||||||
|
};
|
||||||
|
|
168
src/corehost/cli/fxr/fx_ver.cpp
Normal file
168
src/corehost/cli/fxr/fx_ver.cpp
Normal 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;
|
||||||
|
}
|
40
src/corehost/cli/fxr/fx_ver.h
Normal file
40
src/corehost/cli/fxr/fx_ver.h
Normal 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);
|
||||||
|
};
|
||||||
|
|
|
@ -5,25 +5,21 @@
|
||||||
#include "args.h"
|
#include "args.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "deps_resolver.h"
|
#include "deps_resolver.h"
|
||||||
|
#include "fx_muxer.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "coreclr.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
|
// Load the deps resolver
|
||||||
deps_resolver_t resolver(args);
|
deps_resolver_t resolver(init->fx_dir(), &config, args);
|
||||||
|
|
||||||
if (!resolver.valid())
|
if (!resolver.valid())
|
||||||
{
|
{
|
||||||
trace::error(_X("Invalid .deps file"));
|
trace::error(_X("Invalid .deps file"));
|
||||||
|
@ -31,8 +27,8 @@ int run(const arguments_t& args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add packages directory
|
// Add packages directory
|
||||||
pal::string_t packages_dir = args.nuget_packages;
|
pal::string_t packages_dir = init->probe_dir();
|
||||||
if (!pal::directory_exists(packages_dir))
|
if (packages_dir.empty() || !pal::directory_exists(packages_dir))
|
||||||
{
|
{
|
||||||
(void)pal::get_default_packages_directory(&packages_dir);
|
(void)pal::get_default_packages_directory(&packages_dir);
|
||||||
}
|
}
|
||||||
|
@ -55,6 +51,8 @@ int run(const arguments_t& args)
|
||||||
return StatusCode::ResolverResolveFailure;
|
return StatusCode::ResolverResolveFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: config.get_runtime_properties();
|
||||||
|
|
||||||
// Build CoreCLR properties
|
// Build CoreCLR properties
|
||||||
const char* property_keys[] = {
|
const char* property_keys[] = {
|
||||||
"TRUSTED_PLATFORM_ASSEMBLIES",
|
"TRUSTED_PLATFORM_ASSEMBLIES",
|
||||||
|
@ -63,20 +61,22 @@ int run(const arguments_t& args)
|
||||||
"NATIVE_DLL_SEARCH_DIRECTORIES",
|
"NATIVE_DLL_SEARCH_DIRECTORIES",
|
||||||
"PLATFORM_RESOURCE_ROOTS",
|
"PLATFORM_RESOURCE_ROOTS",
|
||||||
"AppDomainCompatSwitch",
|
"AppDomainCompatSwitch",
|
||||||
// TODO: pipe this from corehost.json
|
|
||||||
"SERVER_GC",
|
"SERVER_GC",
|
||||||
// Workaround: mscorlib does not resolve symlinks for AppContext.BaseDirectory dotnet/coreclr/issues/2128
|
// Workaround: mscorlib does not resolve symlinks for AppContext.BaseDirectory dotnet/coreclr/issues/2128
|
||||||
"APP_CONTEXT_BASE_DIRECTORY",
|
"APP_CONTEXT_BASE_DIRECTORY",
|
||||||
|
"APP_CONTEXT_DEPS_FILES"
|
||||||
};
|
};
|
||||||
|
|
||||||
auto tpa_paths_cstr = pal::to_stdstring(probe_paths.tpa);
|
auto tpa_paths_cstr = pal::to_stdstring(probe_paths.tpa);
|
||||||
auto app_base_cstr = pal::to_stdstring(args.app_dir);
|
auto app_base_cstr = pal::to_stdstring(args.app_dir);
|
||||||
auto native_dirs_cstr = pal::to_stdstring(probe_paths.native);
|
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
|
// Workaround for dotnet/cli Issue #488 and #652
|
||||||
pal::string_t server_gc;
|
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 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[] = {
|
const char* property_values[] = {
|
||||||
// TRUSTED_PLATFORM_ASSEMBLIES
|
// TRUSTED_PLATFORM_ASSEMBLIES
|
||||||
|
@ -88,13 +88,15 @@ int run(const arguments_t& args)
|
||||||
// NATIVE_DLL_SEARCH_DIRECTORIES
|
// NATIVE_DLL_SEARCH_DIRECTORIES
|
||||||
native_dirs_cstr.c_str(),
|
native_dirs_cstr.c_str(),
|
||||||
// PLATFORM_RESOURCE_ROOTS
|
// PLATFORM_RESOURCE_ROOTS
|
||||||
culture_dirs_cstr.c_str(),
|
resources_dirs_cstr.c_str(),
|
||||||
// AppDomainCompatSwitch
|
// AppDomainCompatSwitch
|
||||||
"UseLatestBehaviorWhenTFMNotSpecified",
|
"UseLatestBehaviorWhenTFMNotSpecified",
|
||||||
// SERVER_GC
|
// SERVER_GC
|
||||||
server_gc_cstr.c_str(),
|
server_gc_cstr.c_str(),
|
||||||
// APP_CONTEXT_BASE_DIRECTORY
|
// 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]);
|
size_t property_size = sizeof(property_keys) / sizeof(property_keys[0]);
|
||||||
|
@ -188,16 +190,43 @@ int run(const arguments_t& args)
|
||||||
return exit_code;
|
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[])
|
SHARED_API int corehost_main(const int argc, const pal::char_t* argv[])
|
||||||
{
|
{
|
||||||
trace::setup();
|
trace::setup();
|
||||||
|
|
||||||
|
assert(g_init);
|
||||||
|
|
||||||
// Take care of arguments
|
// Take care of arguments
|
||||||
arguments_t args;
|
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;
|
||||||
}
|
}
|
||||||
|
|
1
src/corehost/cli/json/casablanca/LICENSE.txt
Normal file
1
src/corehost/cli/json/casablanca/LICENSE.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
https://github.com/Microsoft/cpprestsdk
|
600
src/corehost/cli/json/casablanca/include/cpprest/asyncrt_utils.h
Normal file
600
src/corehost/cli/json/casablanca/include/cpprest/asyncrt_utils.h
Normal 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 ×panString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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;
|
7048
src/corehost/cli/json/casablanca/include/cpprest/details/SafeInt3.hpp
Executable file
7048
src/corehost/cli/json/casablanca/include/cpprest/details/SafeInt3.hpp
Executable file
File diff suppressed because it is too large
Load diff
140
src/corehost/cli/json/casablanca/include/cpprest/details/basic_types.h
Executable file
140
src/corehost/cli/json/casablanca/include/cpprest/details/basic_types.h
Executable 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
|
97
src/corehost/cli/json/casablanca/include/cpprest/details/cpprest_compat.h
Executable file
97
src/corehost/cli/json/casablanca/include/cpprest/details/cpprest_compat.h
Executable 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
|
89
src/corehost/cli/json/casablanca/include/cpprest/details/nosal.h
Executable file
89
src/corehost/cli/json/casablanca/include/cpprest/details/nosal.h
Executable 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)
|
1936
src/corehost/cli/json/casablanca/include/cpprest/json.h
Executable file
1936
src/corehost/cli/json/casablanca/include/cpprest/json.h
Executable file
File diff suppressed because it is too large
Load diff
109
src/corehost/cli/json/casablanca/include/stdafx.h
Normal file
109
src/corehost/cli/json/casablanca/include/stdafx.h
Normal 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
|
||||||
|
|
495
src/corehost/cli/json/casablanca/src/json/json.cpp
Normal file
495
src/corehost/cli/json/casablanca/src/json/json.cpp
Normal 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;
|
||||||
|
}
|
1312
src/corehost/cli/json/casablanca/src/json/json_parsing.cpp
Normal file
1312
src/corehost/cli/json/casablanca/src/json/json_parsing.cpp
Normal file
File diff suppressed because it is too large
Load diff
274
src/corehost/cli/json/casablanca/src/json/json_serialization.cpp
Normal file
274
src/corehost/cli/json/casablanca/src/json/json_serialization.cpp
Normal 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();
|
||||||
|
}
|
496
src/corehost/cli/json/casablanca/src/utilities/asyncrt_utils.cpp
Normal file
496
src/corehost/cli/json/casablanca/src/utilities/asyncrt_utils.cpp
Normal 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'); }
|
||||||
|
|
||||||
|
}
|
61
src/corehost/cli/libhost.cpp
Normal file
61
src/corehost/cli/libhost.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,70 @@
|
||||||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
// 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.
|
// 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")
|
#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__
|
||||||
|
|
114
src/corehost/cli/runtime_config.cpp
Normal file
114
src/corehost/cli/runtime_config.cpp
Normal 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;
|
||||||
|
}
|
29
src/corehost/cli/runtime_config.h
Normal file
29
src/corehost/cli/runtime_config.h
Normal 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;
|
||||||
|
};
|
|
@ -38,18 +38,33 @@ bool servicing_index_t::find_redirection(
|
||||||
auto iter = m_redirections.find(stream.str());
|
auto iter = m_redirections.find(stream.str());
|
||||||
if (iter != m_redirections.end())
|
if (iter != m_redirections.end())
|
||||||
{
|
{
|
||||||
pal::string_t full_path = m_patch_root;
|
pal::string_t ni_root = m_patch_root;
|
||||||
append_path(&full_path, iter->second.c_str());
|
append_path(&ni_root, get_arch());
|
||||||
if (pal::file_exists(full_path))
|
|
||||||
|
// 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;
|
append_path(&full_path, iter->second.c_str());
|
||||||
trace::verbose(_X("Servicing %s with %s"), stream.str().c_str(), redirection->c_str());
|
if (pal::file_exists(full_path))
|
||||||
return true;
|
{
|
||||||
|
*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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +127,8 @@ void servicing_index_t::ensure_redirections()
|
||||||
|
|
||||||
if (trace::is_enabled())
|
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.
|
// Store just the filename.
|
||||||
|
|
|
@ -33,4 +33,20 @@ if(WIN32)
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF")
|
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_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")
|
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()
|
endif()
|
||||||
|
|
|
@ -15,10 +15,12 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
#include <Windows.h>
|
#define NOMINMAX
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
#define HOST_EXE_NAME L"corehost.exe"
|
#define HOST_EXE_NAME L"corehost.exe"
|
||||||
#define xerr std::wcerr
|
#define xerr std::wcerr
|
||||||
|
@ -93,11 +95,17 @@ namespace pal
|
||||||
typedef HMODULE dll_t;
|
typedef HMODULE dll_t;
|
||||||
typedef FARPROC proc_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 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 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 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); }
|
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 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); }
|
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* dll_t;
|
||||||
typedef void* proc_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 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 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 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); }
|
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 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 void err_vprintf(const char_t* format, va_list vl) { ::vfprintf(stderr, format, vl); ::fputc('\n', stderr); }
|
||||||
inline pal::string_t to_palstring(const std::string& str) { return str; }
|
inline pal::string_t to_palstring(const std::string& str) { return str; }
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,6 +22,33 @@
|
||||||
#define symlinkEntrypointExecutable "/proc/curproc/exe"
|
#define symlinkEntrypointExecutable "/proc/curproc/exe"
|
||||||
#endif
|
#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)
|
bool pal::find_coreclr(pal::string_t* recv)
|
||||||
{
|
{
|
||||||
pal::string_t candidate;
|
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)
|
bool pal::realpath(pal::string_t* path)
|
||||||
{
|
{
|
||||||
pal::char_t buf[PATH_MAX];
|
auto resolved = ::realpath(path->c_str(), nullptr);
|
||||||
auto resolved = ::realpath(path->c_str(), buf);
|
|
||||||
if (resolved == nullptr)
|
if (resolved == nullptr)
|
||||||
{
|
{
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
|
@ -145,6 +173,7 @@ bool pal::realpath(pal::string_t* path)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
path->assign(resolved);
|
path->assign(resolved);
|
||||||
|
::free(resolved);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,18 @@
|
||||||
|
|
||||||
static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> g_converter;
|
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)
|
bool pal::find_coreclr(pal::string_t* recv)
|
||||||
{
|
{
|
||||||
pal::string_t candidate;
|
pal::string_t candidate;
|
||||||
|
@ -31,6 +43,35 @@ bool pal::find_coreclr(pal::string_t* recv)
|
||||||
return false;
|
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)
|
bool pal::load_library(const char_t* path, dll_t* dll)
|
||||||
{
|
{
|
||||||
*dll = ::LoadLibraryW(path);
|
*dll = ::LoadLibraryW(path);
|
||||||
|
|
|
@ -55,16 +55,43 @@ pal::string_t get_executable(const pal::string_t& filename)
|
||||||
return result;
|
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
|
if (path.empty())
|
||||||
auto path_sep = path.find_last_of(DIR_SEPARATOR);
|
|
||||||
if (path_sep == pal::string_t::npos)
|
|
||||||
{
|
{
|
||||||
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)
|
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;
|
(*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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
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_executable(const pal::string_t& filename);
|
||||||
pal::string_t get_directory(const pal::string_t& path);
|
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(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);
|
void append_path(pal::string_t* path1, const pal::char_t* path2);
|
||||||
bool coreclr_exists_in_dir(const pal::string_t& candidate);
|
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);
|
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
|
#endif
|
||||||
|
|
|
@ -2,64 +2,140 @@
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "pal.h"
|
|
||||||
#include "utils.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
|
#ifdef COREHOST_PACKAGE_SERVICING
|
||||||
{
|
pal::string_t svc_dir;
|
||||||
Success = 0,
|
if (!pal::getenv(_X("DOTNET_SERVICING"), &svc_dir))
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
return StatusCode::CoreHostLibMissingFailure;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load library
|
pal::string_t path = svc_dir;
|
||||||
if (!pal::load_library(host_path.c_str(), h_host))
|
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;
|
return StatusCode::CoreHostLibLoadFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain entrypoint symbol
|
// Obtain entrypoint symbols
|
||||||
*main_fn = (corehost_main_fn) pal::get_symbol(*h_host, "corehost_main");
|
hostfxr_main_fn main_fn = (hostfxr_main_fn) pal::get_symbol(fxr, "hostfxr_main");
|
||||||
|
return main_fn(argc, argv);
|
||||||
return (*main_fn != nullptr)
|
|
||||||
? StatusCode::Success
|
|
||||||
: StatusCode::CoreHostEntryPointFailure;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // 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)
|
#if defined(_WIN32)
|
||||||
int __cdecl wmain(const int argc, const pal::char_t* argv[])
|
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();
|
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
|
if (trace::is_enabled())
|
||||||
// No custom host asked, so load the corehost if serviced first.
|
|
||||||
pal::string_t svc_dir;
|
|
||||||
if (pal::getenv(_X("DOTNET_SERVICING"), &svc_dir))
|
|
||||||
{
|
{
|
||||||
pal::string_t path = svc_dir;
|
trace::info(_X("--- Invoked host main = {"));
|
||||||
append_path(&path, COREHOST_PACKAGE_NAME);
|
for (int i = 0; i < argc; ++i)
|
||||||
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("Failed to load host library from servicing dir: %s; Status=%08X"), path.c_str(), code);
|
trace::info(_X("%s"), argv[i]);
|
||||||
// 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("}"));
|
||||||
}
|
}
|
||||||
#endif
|
corehost_t corehost;
|
||||||
|
return corehost.run(argc, argv);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/corehost/corehost.h
Normal file
36
src/corehost/corehost.h
Normal 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);
|
||||||
|
};
|
29
src/corehost/error_codes.h
Normal file
29
src/corehost/error_codes.h
Normal 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__
|
79
src/corehost/policy_load.cpp
Normal file
79
src/corehost/policy_load.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
38
src/corehost/policy_load.h
Normal file
38
src/corehost/policy_load.h
Normal 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__
|
Loading…
Add table
Add a link
Reference in a new issue