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");
|
||||
|
||||
// Run the build
|
||||
string version = DotNetCli.Stage0.Exec("", "--version").CaptureStdOut().Execute().StdOut;
|
||||
string rid = Array.Find<string>(version.Split(Environment.NewLine.ToCharArray()), (e) => e.Contains("Runtime Id:")).Replace("Runtime Id:", "").Trim();
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// Why does Windows directly call cmake but Linux/Mac calls "build.sh" in the corehost dir?
|
||||
// See the comment in "src/corehost/build.sh" for details. It doesn't work for some reason.
|
||||
var visualStudio = IsWinx86 ? "Visual Studio 14 2015" : "Visual Studio 14 2015 Win64";
|
||||
var archMacro = IsWinx86 ? "-DCLI_CMAKE_PLATFORM_ARCH_I386=1" : "-DCLI_CMAKE_PLATFORM_ARCH_AMD64=1";
|
||||
var ridMacro = $"-DCLI_CMAKE_RUNTIME_ID:STRING={rid}";
|
||||
|
||||
ExecIn(cmakeOut, "cmake",
|
||||
Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost"),
|
||||
archMacro,
|
||||
ridMacro,
|
||||
"-G",
|
||||
visualStudio);
|
||||
|
||||
|
@ -101,14 +106,21 @@ namespace Microsoft.DotNet.Cli.Build
|
|||
File.Copy(Path.Combine(cmakeOut, "cli", configuration, "corehost.pdb"), Path.Combine(Dirs.Corehost, "corehost.pdb"), overwrite: true);
|
||||
File.Copy(Path.Combine(cmakeOut, "cli", "dll", configuration, "hostpolicy.dll"), Path.Combine(Dirs.Corehost, "hostpolicy.dll"), overwrite: true);
|
||||
File.Copy(Path.Combine(cmakeOut, "cli", "dll", configuration, "hostpolicy.pdb"), Path.Combine(Dirs.Corehost, "hostpolicy.pdb"), overwrite: true);
|
||||
File.Copy(Path.Combine(cmakeOut, "cli", "fxr", configuration, "hostfxr.dll"), Path.Combine(Dirs.Corehost, "hostfxr.dll"), overwrite: true);
|
||||
File.Copy(Path.Combine(cmakeOut, "cli", "fxr", configuration, "hostfxr.pdb"), Path.Combine(Dirs.Corehost, "hostfxr.pdb"), overwrite: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecIn(cmakeOut, Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost", "build.sh"));
|
||||
ExecIn(cmakeOut, Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost", "build.sh"),
|
||||
"--arch",
|
||||
"amd64",
|
||||
"--rid",
|
||||
rid);
|
||||
|
||||
// Copy the output out
|
||||
File.Copy(Path.Combine(cmakeOut, "cli", "corehost"), Path.Combine(Dirs.Corehost, "corehost"), overwrite: true);
|
||||
File.Copy(Path.Combine(cmakeOut, "cli", "dll", $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), overwrite: true);
|
||||
File.Copy(Path.Combine(cmakeOut, "cli", "fxr", $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), overwrite: true);
|
||||
}
|
||||
|
||||
return c.Success();
|
||||
|
@ -193,6 +205,7 @@ namespace Microsoft.DotNet.Cli.Build
|
|||
// Copy corehost
|
||||
File.Copy(Path.Combine(Dirs.Corehost, $"corehost{Constants.ExeSuffix}"), Path.Combine(binDir, $"corehost{Constants.ExeSuffix}"), overwrite: true);
|
||||
File.Copy(Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), Path.Combine(binDir, $"{Constants.DynamicLibPrefix}hostpolicy{Constants.DynamicLibSuffix}"), overwrite: true);
|
||||
File.Copy(Path.Combine(Dirs.Corehost, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), Path.Combine(binDir, $"{Constants.DynamicLibPrefix}hostfxr{Constants.DynamicLibSuffix}"), overwrite: true);
|
||||
|
||||
// Corehostify binaries
|
||||
foreach (var binaryToCorehostify in BinariesForCoreHost)
|
||||
|
|
|
@ -98,7 +98,8 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
|
||||
if (depsFilePath != null)
|
||||
{
|
||||
arguments.Add($"--depsfile:{depsFilePath}");
|
||||
arguments.Add("--depsfile");
|
||||
arguments.Add(depsFilePath);
|
||||
}
|
||||
|
||||
arguments.AddRange(commandArguments);
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
return null;
|
||||
}
|
||||
|
||||
var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.Deps;
|
||||
var depsFilePath = projectContext.GetOutputPaths(configuration, outputPath: outputPath).RuntimeFiles.DepsJson;
|
||||
|
||||
var dependencyLibraries = GetAllDependencyLibraries(projectContext);
|
||||
|
||||
|
|
|
@ -37,7 +37,8 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
public static readonly string HostExecutableName = "corehost" + ExeSuffix;
|
||||
public static readonly string[] HostBinaryNames = new string[] {
|
||||
HostExecutableName,
|
||||
(CurrentPlatform == Platform.Windows ? "hostpolicy" : "libhostpolicy") + DynamicLibSuffix
|
||||
(CurrentPlatform == Platform.Windows ? "hostpolicy" : "libhostpolicy") + DynamicLibSuffix,
|
||||
(CurrentPlatform == Platform.Windows ? "hostfxr" : "libhostfxr") + DynamicLibSuffix
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,46 @@ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli
|
|||
done
|
||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||
|
||||
__build_arch=
|
||||
__runtime_id=
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
lowerI="$(echo $1 | awk '{print tolower($0)}')"
|
||||
case $lowerI in
|
||||
-h|--help)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
--arch)
|
||||
shift
|
||||
__build_arch=$1
|
||||
;;
|
||||
--rid)
|
||||
shift
|
||||
__runtime_id=$1
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument to build.sh $1"; exit 1
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
__cmake_defines=
|
||||
|
||||
case $__build_arch in
|
||||
amd64)
|
||||
__define=-DCLI_CMAKE_PLATFORM_ARCH_AMD64=1
|
||||
;;
|
||||
x86)
|
||||
__define=-DCLI_CMAKE_PLATFORM_ARCH_I386=1
|
||||
;;
|
||||
*)
|
||||
echo "Unknown architecture $__build_arch"; exit 1
|
||||
;;
|
||||
esac
|
||||
__cmake_defines="${__cmake_defines} ${__define}"
|
||||
|
||||
|
||||
echo "Building Corehost from $DIR to $(pwd)"
|
||||
cmake "$DIR" -G "Unix Makefiles"
|
||||
cmake "$DIR" -G "Unix Makefiles" $__cmake_defines -DCLI_CMAKE_RUNTIME_ID:STRING=$__runtime_id
|
||||
make
|
||||
|
|
|
@ -14,13 +14,26 @@ include(setup.cmake)
|
|||
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(../common)
|
||||
include_directories(.)
|
||||
include_directories(./fxr)
|
||||
include_directories(./json/casablanca/include)
|
||||
|
||||
# CMake does not recommend using globbing since it messes with the freshness checks
|
||||
set(SOURCES
|
||||
../corehost.cpp
|
||||
libhost.cpp
|
||||
|
||||
#deps_format.cpp
|
||||
#./json/casablanca/src/json/json.cpp
|
||||
#./json/casablanca/src/json/json_parsing.cpp
|
||||
#./json/casablanca/src/json/json_serialization.cpp
|
||||
#./json/casablanca/src/utilities/asyncrt_utils.cpp
|
||||
|
||||
|
||||
./fxr/fx_ver.cpp
|
||||
../corehost.cpp
|
||||
../policy_load.cpp
|
||||
../common/trace.cpp
|
||||
../common/utils.cpp)
|
||||
|
||||
|
@ -32,6 +45,9 @@ else()
|
|||
endif()
|
||||
|
||||
add_executable(corehost ${SOURCES})
|
||||
install(TARGETS corehost DESTINATION bin)
|
||||
add_definitions(-D_NO_ASYNCRTIMP)
|
||||
add_definitions(-D_NO_PPLXIMP)
|
||||
|
||||
# Older CMake doesn't support CMAKE_CXX_STANDARD and GCC/Clang need a switch to enable C++ 11
|
||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "(Clang|GNU)")
|
||||
|
@ -44,3 +60,4 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
|||
endif()
|
||||
|
||||
add_subdirectory(dll)
|
||||
add_subdirectory(fxr)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "args.h"
|
||||
#include "utils.h"
|
||||
#include "coreclr.h"
|
||||
#include "libhost.h"
|
||||
|
||||
arguments_t::arguments_t() :
|
||||
managed_application(_X("")),
|
||||
|
@ -11,11 +12,8 @@ arguments_t::arguments_t() :
|
|||
app_dir(_X("")),
|
||||
app_argc(0),
|
||||
app_argv(nullptr),
|
||||
nuget_packages(_X("")),
|
||||
dotnet_packages_cache(_X("")),
|
||||
dotnet_servicing(_X("")),
|
||||
dotnet_runtime_servicing(_X("")),
|
||||
dotnet_home(_X("")),
|
||||
deps_path(_X(""))
|
||||
{
|
||||
}
|
||||
|
@ -26,12 +24,13 @@ void display_help()
|
|||
_X("Usage: " HOST_EXE_NAME " [ASSEMBLY] [ARGUMENTS]\n")
|
||||
_X("Execute the specified managed assembly with the passed in arguments\n\n")
|
||||
_X("The Host's behavior can be altered using the following environment variables:\n")
|
||||
_X(" DOTNET_HOME Set the dotnet home directory. The CLR is expected to be in the runtime subdirectory of this directory. Overrides all other values for CLR search paths\n")
|
||||
_X(" COREHOST_TRACE Set to affect trace levels (0 = Errors only (default), 1 = Warnings, 2 = Info, 3 = Verbose)\n");
|
||||
}
|
||||
|
||||
bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& args)
|
||||
bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_dir, host_mode_t mode,
|
||||
const int argc, const pal::char_t* argv[], arguments_t* arg_out)
|
||||
{
|
||||
arguments_t& args = *arg_out;
|
||||
// Get the full name of the application
|
||||
if (!pal::get_own_executable_path(&args.own_path) || !pal::realpath(&args.own_path))
|
||||
{
|
||||
|
@ -41,8 +40,8 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
|
|||
|
||||
auto own_name = get_filename(args.own_path);
|
||||
auto own_dir = get_directory(args.own_path);
|
||||
|
||||
if (own_name.compare(HOST_EXE_NAME) == 0)
|
||||
|
||||
if (mode != host_mode_t::standalone)
|
||||
{
|
||||
// corerun mode. First argument is managed app
|
||||
if (argc < 2)
|
||||
|
@ -78,23 +77,26 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
|
|||
args.app_argc = argc - 1;
|
||||
}
|
||||
|
||||
if (args.app_argc > 0)
|
||||
std::unordered_map<pal::string_t, pal::string_t> opts;
|
||||
std::vector<pal::string_t> known_opts = { _X("--depsfile"), _X("--additionalprobingpath") };
|
||||
int num_args = 0;
|
||||
if (!parse_known_args(args.app_argc, args.app_argv, known_opts, &opts, &num_args))
|
||||
{
|
||||
auto depsfile_candidate = pal::string_t(args.app_argv[0]);
|
||||
|
||||
if (starts_with(depsfile_candidate, s_deps_arg_prefix, false))
|
||||
{
|
||||
args.deps_path = depsfile_candidate.substr(s_deps_arg_prefix.length());
|
||||
if (!pal::realpath(&args.deps_path))
|
||||
{
|
||||
trace::error(_X("Failed to locate deps file: %s"), args.deps_path.c_str());
|
||||
return false;
|
||||
}
|
||||
args.app_dir = get_directory(args.deps_path);
|
||||
args.app_argc = args.app_argc - 1;
|
||||
args.app_argv = &args.app_argv[1];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
args.app_argc -= num_args;
|
||||
args.app_argv += num_args;
|
||||
pal::string_t deps_file = opts.count(_X("--depsfile")) ? opts[_X("--depsfile")] : deps_path;
|
||||
pal::string_t probe_path = opts.count(_X("--additionalprobingpath")) ? opts[_X("--additionalprobingpath")] : probe_dir;
|
||||
|
||||
if (!deps_file.empty())
|
||||
{
|
||||
args.deps_path = deps_file;
|
||||
args.app_dir = get_directory(args.deps_path);
|
||||
}
|
||||
|
||||
args.probe_dir = probe_path;
|
||||
|
||||
if (args.deps_path.empty())
|
||||
{
|
||||
|
@ -105,13 +107,10 @@ bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& arg
|
|||
args.deps_path.append(app_base);
|
||||
args.deps_path.push_back(DIR_SEPARATOR);
|
||||
args.deps_path.append(app_name, 0, app_name.find_last_of(_X(".")));
|
||||
args.deps_path.append(_X(".deps"));
|
||||
args.deps_path.append(_X(".deps.json"));
|
||||
}
|
||||
|
||||
pal::getenv(_X("NUGET_PACKAGES"), &args.nuget_packages);
|
||||
pal::getenv(_X("DOTNET_PACKAGES_CACHE"), &args.dotnet_packages_cache);
|
||||
pal::getenv(_X("DOTNET_SERVICING"), &args.dotnet_servicing);
|
||||
pal::getenv(_X("DOTNET_RUNTIME_SERVICING"), &args.dotnet_runtime_servicing);
|
||||
pal::getenv(_X("DOTNET_HOME"), &args.dotnet_home);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
#include "utils.h"
|
||||
#include "pal.h"
|
||||
#include "trace.h"
|
||||
|
||||
static const pal::string_t s_deps_arg_prefix = _X("--depsfile:");
|
||||
#include "libhost.h"
|
||||
|
||||
struct arguments_t
|
||||
{
|
||||
|
@ -18,7 +17,7 @@ struct arguments_t
|
|||
pal::string_t dotnet_servicing;
|
||||
pal::string_t dotnet_runtime_servicing;
|
||||
pal::string_t dotnet_home;
|
||||
pal::string_t nuget_packages;
|
||||
pal::string_t probe_dir;
|
||||
pal::string_t dotnet_packages_cache;
|
||||
pal::string_t managed_application;
|
||||
|
||||
|
@ -28,6 +27,6 @@ struct arguments_t
|
|||
arguments_t();
|
||||
};
|
||||
|
||||
bool parse_arguments(const int argc, const pal::char_t* argv[], arguments_t& args);
|
||||
bool parse_arguments(const pal::string_t& deps_path, const pal::string_t& probe_dir, host_mode_t mode, const int argc, const pal::char_t* argv[], arguments_t* args);
|
||||
|
||||
#endif // ARGS_H
|
||||
|
|
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 "trace.h"
|
||||
#include "deps_format.h"
|
||||
#include "deps_resolver.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// -----------------------------------------------------------------------------
|
||||
// Read a single field from the deps entry
|
||||
//
|
||||
// Parameters:
|
||||
// line - A deps file entry line
|
||||
// buf - The temporary buffer to use while parsing (with size to contain "line")
|
||||
// ofs - The offset that this method will read from "line" on invocation and
|
||||
// the offset that has been consumed by this method upon successful exit
|
||||
// field - The current field read from the line
|
||||
//
|
||||
// Assumption:
|
||||
// The line should be in a CSV format, with commas separating the fields.
|
||||
// The fields themselves will be quoted. The escape character is '\\'
|
||||
//
|
||||
// Returns:
|
||||
// True if parsed successfully. Else, false
|
||||
//
|
||||
// Note:
|
||||
// Callers cannot call with the same "line" upon an unsuccessful exit.
|
||||
bool read_field(const pal::string_t& line, pal::char_t* buf, unsigned* ofs, pal::string_t* field)
|
||||
{
|
||||
unsigned& offset = *ofs;
|
||||
pal::string_t& value_recv = *field;
|
||||
|
||||
// The first character should be a '"'
|
||||
if (line[offset] != '"')
|
||||
{
|
||||
trace::error(_X("Error reading TPA file"));
|
||||
return false;
|
||||
}
|
||||
offset++;
|
||||
|
||||
auto buf_offset = 0;
|
||||
|
||||
// Iterate through characters in the string
|
||||
for (; offset < line.length(); offset++)
|
||||
{
|
||||
// Is this a '\'?
|
||||
if (line[offset] == '\\')
|
||||
{
|
||||
// Skip this character and read the next character into the buffer
|
||||
offset++;
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
// Is this a '"'?
|
||||
else if (line[offset] == '\"')
|
||||
{
|
||||
// Done! Advance to the pointer after the input
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Take the character
|
||||
buf[buf_offset] = line[offset];
|
||||
}
|
||||
buf_offset++;
|
||||
}
|
||||
buf[buf_offset] = '\0';
|
||||
value_recv.assign(buf);
|
||||
|
||||
// Consume the ',' if we have one
|
||||
if (line[offset] == ',')
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A uniqifying append helper that doesn't let two entries with the same
|
||||
// "asset_name" be part of the "output" paths.
|
||||
|
@ -106,28 +38,6 @@ void add_tpa_asset(
|
|||
items->insert(asset_name);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Add mscorlib from the CLR directory. Even if CLR is serviced, we should pick
|
||||
// mscorlib from the CLR directory. If mscorlib could not be found in the CLR
|
||||
// location, then leave it to the CLR to pick the right mscorlib.
|
||||
//
|
||||
void add_mscorlib_to_tpa(const pal::string_t& clr_dir, std::set<pal::string_t>* items, pal::string_t* output)
|
||||
{
|
||||
pal::string_t mscorlib_ni_path = clr_dir + DIR_SEPARATOR + _X("mscorlib.ni.dll");
|
||||
if (pal::file_exists(mscorlib_ni_path))
|
||||
{
|
||||
add_tpa_asset(_X("mscorlib"), mscorlib_ni_path, items, output);
|
||||
return;
|
||||
}
|
||||
|
||||
pal::string_t mscorlib_path = clr_dir + DIR_SEPARATOR + _X("mscorlib.dll");
|
||||
if (pal::file_exists(mscorlib_path))
|
||||
{
|
||||
add_tpa_asset(_X("mscorlib"), mscorlib_path, items, output);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A uniqifying append helper that doesn't let two "paths" to be identical in
|
||||
// the "output" string.
|
||||
|
@ -157,234 +67,16 @@ void add_unique_path(
|
|||
|
||||
} // end of anonymous namespace
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Given a "base" directory, yield the relative path of this file in the package
|
||||
// layout.
|
||||
//
|
||||
// Parameters:
|
||||
// base - The base directory to look for the relative path of this entry
|
||||
// str - If the method returns true, contains the file path for this deps
|
||||
// entry relative to the "base" directory
|
||||
//
|
||||
// Returns:
|
||||
// If the file exists in the path relative to the "base" directory.
|
||||
//
|
||||
bool deps_entry_t::to_full_path(const pal::string_t& base, pal::string_t* str) const
|
||||
{
|
||||
pal::string_t& candidate = *str;
|
||||
|
||||
candidate.clear();
|
||||
|
||||
// Entry relative path contains '/' separator, sanitize it to use
|
||||
// platform separator. Perf: avoid extra copy if it matters.
|
||||
pal::string_t pal_relative_path = relative_path;
|
||||
if (_X('/') != DIR_SEPARATOR)
|
||||
{
|
||||
replace_char(&pal_relative_path, _X('/'), DIR_SEPARATOR);
|
||||
}
|
||||
|
||||
// Reserve space for the path below
|
||||
candidate.reserve(base.length() +
|
||||
library_name.length() +
|
||||
library_version.length() +
|
||||
pal_relative_path.length() + 3);
|
||||
|
||||
candidate.assign(base);
|
||||
append_path(&candidate, library_name.c_str());
|
||||
append_path(&candidate, library_version.c_str());
|
||||
append_path(&candidate, pal_relative_path.c_str());
|
||||
|
||||
bool exists = pal::file_exists(candidate);
|
||||
if (!exists)
|
||||
{
|
||||
candidate.clear();
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Given a "base" directory, yield the relative path of this file in the package
|
||||
// layout if the entry hash matches the hash file in the "base" directory
|
||||
//
|
||||
// Parameters:
|
||||
// base - The base directory to look for the relative path of this entry and
|
||||
// the hash file.
|
||||
// str - If the method returns true, contains the file path for this deps
|
||||
// entry relative to the "base" directory
|
||||
//
|
||||
// Description:
|
||||
// Looks for a file named "{PackageName}.{PackageVersion}.nupkg.{HashAlgorithm}"
|
||||
// If the deps entry's {HashAlgorithm}-{HashValue} matches the contents then
|
||||
// yields the relative path of this entry in the "base" dir.
|
||||
//
|
||||
// Returns:
|
||||
// If the file exists in the path relative to the "base" directory and there
|
||||
// was hash file match with this deps entry.
|
||||
//
|
||||
// See: to_full_path(base, str)
|
||||
//
|
||||
bool deps_entry_t::to_hash_matched_path(const pal::string_t& base, pal::string_t* str) const
|
||||
{
|
||||
pal::string_t& candidate = *str;
|
||||
|
||||
candidate.clear();
|
||||
|
||||
// Base directory must be present to perform hash lookup.
|
||||
if (base.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// First detect position of hyphen in [Algorithm]-[Hash] in the string.
|
||||
size_t pos = library_hash.find(_X("-"));
|
||||
if (pos == 0 || pos == pal::string_t::npos)
|
||||
{
|
||||
trace::verbose(_X("Invalid hash %s value for deps file entry: %s"), library_hash.c_str(), library_name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the nupkg file name. Just reserve approx 8 char_t's for the algorithm name.
|
||||
pal::string_t nupkg_filename;
|
||||
nupkg_filename.reserve(library_name.length() + 1 + library_version.length() + 16);
|
||||
nupkg_filename.append(library_name);
|
||||
nupkg_filename.append(_X("."));
|
||||
nupkg_filename.append(library_version);
|
||||
nupkg_filename.append(_X(".nupkg."));
|
||||
nupkg_filename.append(library_hash.substr(0, pos));
|
||||
|
||||
// Build the hash file path str.
|
||||
pal::string_t hash_file;
|
||||
hash_file.reserve(base.length() + library_name.length() + library_version.length() + nupkg_filename.length() + 3);
|
||||
hash_file.assign(base);
|
||||
append_path(&hash_file, library_name.c_str());
|
||||
append_path(&hash_file, library_version.c_str());
|
||||
append_path(&hash_file, nupkg_filename.c_str());
|
||||
|
||||
// Read the contents of the hash file.
|
||||
pal::ifstream_t fstream(hash_file);
|
||||
if (!fstream.good())
|
||||
{
|
||||
trace::verbose(_X("The hash file is invalid [%s]"), hash_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Obtain the hash from the file.
|
||||
std::string hash;
|
||||
hash.assign(pal::istreambuf_iterator_t(fstream),
|
||||
pal::istreambuf_iterator_t());
|
||||
pal::string_t pal_hash;
|
||||
pal::to_palstring(hash.c_str(), &pal_hash);
|
||||
|
||||
// Check if contents match deps entry.
|
||||
pal::string_t entry_hash = library_hash.substr(pos + 1);
|
||||
if (entry_hash != pal_hash)
|
||||
{
|
||||
trace::verbose(_X("The file hash [%s][%d] did not match entry hash [%s][%d]"),
|
||||
pal_hash.c_str(), pal_hash.length(), entry_hash.c_str(), entry_hash.length());
|
||||
return false;
|
||||
}
|
||||
|
||||
// All good, just append the relative dir to base.
|
||||
return to_full_path(base, &candidate);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load the deps file and parse its "entry" lines which contain the "fields" of
|
||||
// the entry. Populate an array of these entries.
|
||||
//
|
||||
bool deps_resolver_t::load()
|
||||
{
|
||||
// If file doesn't exist, then assume parsed.
|
||||
if (!pal::file_exists(m_deps_path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Somehow the file stream could not be opened. This is an error.
|
||||
pal::ifstream_t file(m_deps_path);
|
||||
if (!file.good())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the "entry" lines of the deps file.
|
||||
std::string stdline;
|
||||
while (std::getline(file, stdline))
|
||||
{
|
||||
pal::string_t line;
|
||||
pal::to_palstring(stdline.c_str(), &line);
|
||||
|
||||
deps_entry_t entry;
|
||||
pal::string_t is_serviceable;
|
||||
pal::string_t* fields[] = {
|
||||
&entry.library_type,
|
||||
&entry.library_name,
|
||||
&entry.library_version,
|
||||
&entry.library_hash,
|
||||
&entry.asset_type,
|
||||
&entry.asset_name,
|
||||
&entry.relative_path,
|
||||
// TODO: Add when the deps file support is enabled.
|
||||
// &is_serviceable
|
||||
};
|
||||
|
||||
std::vector<pal::char_t> buf(line.length());
|
||||
|
||||
for (unsigned i = 0, offset = 0; i < sizeof(fields) / sizeof(fields[0]); ++i)
|
||||
{
|
||||
if (!(read_field(line, buf.data(), &offset, fields[i])))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Serviceable, if not false, default is true.
|
||||
entry.is_serviceable = pal::strcasecmp(is_serviceable.c_str(), _X("false")) != 0;
|
||||
|
||||
// TODO: Deps file does not follow spec. It uses '\\', should use '/'
|
||||
replace_char(&entry.relative_path, _X('\\'), _X('/'));
|
||||
|
||||
m_deps_entries.push_back(entry);
|
||||
|
||||
trace::verbose(_X("Added deps entry [%d] [%s, %s, %s]"), m_deps_entries.size() - 1, entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str());
|
||||
|
||||
static_assert(std::is_same<std::vector<deps_entry_t>, decltype(m_deps_entries)>::value, "decltype(m_deps_entries) not a vector, took index based on size.");
|
||||
if (entry.asset_type == _X("native") &&
|
||||
entry.asset_name == LIBCORECLR_FILENAME)
|
||||
{
|
||||
m_coreclr_index = m_deps_entries.size() - 1;
|
||||
trace::verbose(_X("Found coreclr from deps entry [%d] [%s, %s, %s]"),
|
||||
m_coreclr_index,
|
||||
entry.library_name.c_str(),
|
||||
entry.library_version.c_str(),
|
||||
entry.relative_path.c_str());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Parse the deps file.
|
||||
//
|
||||
// Returns:
|
||||
// True if the file parse is successful or if file doesn't exist. False,
|
||||
// when there is an error parsing the file.
|
||||
//
|
||||
bool deps_resolver_t::parse_deps_file(const arguments_t& args)
|
||||
{
|
||||
m_deps_path = args.deps_path;
|
||||
|
||||
return load();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load local assemblies by priority order of their file extensions and
|
||||
// unique-fied by their simple name.
|
||||
//
|
||||
void deps_resolver_t::get_local_assemblies(const pal::string_t& dir)
|
||||
void deps_resolver_t::get_dir_assemblies(
|
||||
const pal::string_t& dir,
|
||||
const pal::string_t& dir_name,
|
||||
std::unordered_map<pal::string_t, pal::string_t>* dir_assemblies)
|
||||
{
|
||||
trace::verbose(_X("Adding files from dir %s"), dir.c_str());
|
||||
trace::verbose(_X("Adding files from %s dir %s"), dir_name.c_str(), dir.c_str());
|
||||
|
||||
// Managed extensions in priority order, pick DLL over EXE and NI over IL.
|
||||
const pal::string_t managed_ext[] = { _X(".ni.dll"), _X(".dll"), _X(".ni.exe"), _X(".exe") };
|
||||
|
@ -412,18 +104,17 @@ void deps_resolver_t::get_local_assemblies(const pal::string_t& dir)
|
|||
continue;
|
||||
}
|
||||
|
||||
// TODO: Do a case insensitive lookup.
|
||||
// Already added entry for this asset, by priority order skip this ext
|
||||
if (m_local_assemblies.count(file_name))
|
||||
if (dir_assemblies->count(file_name))
|
||||
{
|
||||
trace::verbose(_X("Skipping %s because the %s already exists in local assemblies"), file.c_str(), m_local_assemblies.find(file_name)->second.c_str());
|
||||
trace::verbose(_X("Skipping %s because the %s already exists in %s assemblies"), file.c_str(), dir_assemblies->find(file_name)->second.c_str(), dir_name.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add entry for this asset
|
||||
pal::string_t file_path = dir + DIR_SEPARATOR + file;
|
||||
trace::verbose(_X("Adding %s to local assembly set from %s"), file_name.c_str(), file_path.c_str());
|
||||
m_local_assemblies.emplace(file_name, file_path);
|
||||
trace::verbose(_X("Adding %s to %s assembly set from %s"), file_name.c_str(), dir_name.c_str(), file_path.c_str());
|
||||
dir_assemblies->emplace(file_name, file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -440,43 +131,93 @@ pal::string_t deps_resolver_t::resolve_coreclr_dir(
|
|||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir)
|
||||
{
|
||||
// Runtime servicing
|
||||
trace::verbose(_X("Probing for CoreCLR in servicing dir=[%s]"), m_runtime_svc.c_str());
|
||||
if (!m_runtime_svc.empty())
|
||||
auto process_coreclr = [&]
|
||||
(bool is_portable, const pal::string_t& deps_dir, deps_json_t* deps) -> pal::string_t
|
||||
{
|
||||
pal::string_t svc_clr = m_runtime_svc;
|
||||
append_path(&svc_clr, _X("runtime"));
|
||||
append_path(&svc_clr, _X("coreclr"));
|
||||
pal::string_t candidate;
|
||||
|
||||
if (coreclr_exists_in_dir(svc_clr))
|
||||
// Servicing override.
|
||||
if (deps->has_coreclr_entry())
|
||||
{
|
||||
return svc_clr;
|
||||
const deps_entry_t& entry = deps->get_coreclr_entry();
|
||||
trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in servicing"), entry.library_name.c_str(), entry.library_version.c_str());
|
||||
if (entry.is_serviceable && m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &candidate))
|
||||
{
|
||||
return get_directory(candidate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trace::verbose(_X("Deps has no coreclr entry."));
|
||||
}
|
||||
}
|
||||
|
||||
// Package cache.
|
||||
trace::verbose(_X("Probing for CoreCLR in package cache=[%s] deps index: [%d]"), package_cache_dir.c_str(), m_coreclr_index);
|
||||
pal::string_t coreclr_cache;
|
||||
if (m_coreclr_index >= 0 && !package_cache_dir.empty() &&
|
||||
m_deps_entries[m_coreclr_index].to_hash_matched_path(package_cache_dir, &coreclr_cache))
|
||||
{
|
||||
return get_directory(coreclr_cache);
|
||||
}
|
||||
// Package cache.
|
||||
pal::string_t coreclr_cache;
|
||||
if (!package_cache_dir.empty())
|
||||
{
|
||||
if (deps->has_coreclr_entry())
|
||||
{
|
||||
const deps_entry_t& entry = deps->get_coreclr_entry();
|
||||
trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in package cache=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), package_cache_dir.c_str());
|
||||
if (entry.to_hash_matched_path(package_cache_dir, &coreclr_cache))
|
||||
{
|
||||
return get_directory(coreclr_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// App dir.
|
||||
trace::verbose(_X("Probing for CoreCLR in app directory=[%s]"), app_dir.c_str());
|
||||
if (coreclr_exists_in_dir(app_dir))
|
||||
{
|
||||
return app_dir;
|
||||
}
|
||||
// Deps directory: lookup relative path if portable, else look sxs.
|
||||
if (is_portable)
|
||||
{
|
||||
pal::string_t coreclr_portable;
|
||||
if (deps->has_coreclr_entry())
|
||||
{
|
||||
const deps_entry_t& entry = deps->get_coreclr_entry();
|
||||
trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in portable app dir=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), deps_dir.c_str());
|
||||
if (entry.to_full_path(deps_dir, &coreclr_portable))
|
||||
{
|
||||
return get_directory(coreclr_portable);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// App main dir or standalone app dir.
|
||||
trace::verbose(_X("Probing for CoreCLR in deps directory=[%s]"), deps_dir.c_str());
|
||||
if (coreclr_exists_in_dir(deps_dir))
|
||||
{
|
||||
return deps_dir;
|
||||
}
|
||||
}
|
||||
|
||||
// Packages dir
|
||||
trace::verbose(_X("Probing for CoreCLR in packages=[%s] deps index: [%d]"), package_dir.c_str(), m_coreclr_index);
|
||||
pal::string_t coreclr_package;
|
||||
if (m_coreclr_index >= 0 && !package_dir.empty() &&
|
||||
m_deps_entries[m_coreclr_index].to_full_path(package_dir, &coreclr_package))
|
||||
// Packages dir.
|
||||
pal::string_t coreclr_package;
|
||||
if (!package_dir.empty())
|
||||
{
|
||||
if (deps->has_coreclr_entry())
|
||||
{
|
||||
const deps_entry_t& entry = deps->get_coreclr_entry();
|
||||
trace::verbose(_X("Probing for CoreCLR package=[%s][%s] in packages dir=[%s]"), entry.library_name.c_str(), entry.library_version.c_str(), package_dir.c_str());
|
||||
if (entry.to_full_path(package_dir, &coreclr_package))
|
||||
{
|
||||
return get_directory(coreclr_package);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pal::string_t();
|
||||
};
|
||||
|
||||
trace::info(_X("--- Starting CoreCLR Proble from app deps.json"));
|
||||
pal::string_t clr_dir = process_coreclr(m_portable, app_dir, m_deps.get());
|
||||
if (clr_dir.empty() && m_portable)
|
||||
{
|
||||
return get_directory(coreclr_package);
|
||||
trace::info(_X("--- Starting CoreCLR Proble from FX deps.json"));
|
||||
clr_dir = process_coreclr(false, m_fx_dir, m_fx_deps.get());
|
||||
}
|
||||
if (!clr_dir.empty())
|
||||
{
|
||||
return clr_dir;
|
||||
}
|
||||
|
||||
// Use platform-specific search algorithm
|
||||
|
@ -516,19 +257,33 @@ void deps_resolver_t::resolve_tpa_list(
|
|||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output)
|
||||
{
|
||||
std::vector<deps_entry_t> empty(0);
|
||||
|
||||
pal::string_t ni_package_cache_dir;
|
||||
if (!package_cache_dir.empty())
|
||||
{
|
||||
ni_package_cache_dir = package_cache_dir;
|
||||
append_path(&ni_package_cache_dir, get_arch());
|
||||
}
|
||||
|
||||
// Obtain the local assemblies in the app dir.
|
||||
get_local_assemblies(app_dir);
|
||||
if (m_portable)
|
||||
{
|
||||
get_dir_assemblies(m_fx_dir, _X("fx"), &m_sxs_assemblies);
|
||||
}
|
||||
else
|
||||
{
|
||||
get_dir_assemblies(app_dir, _X("local"), &m_sxs_assemblies);
|
||||
}
|
||||
|
||||
std::set<pal::string_t> items;
|
||||
|
||||
add_mscorlib_to_tpa(clr_dir, &items, output);
|
||||
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
auto process_entry = [&](bool is_portable, deps_json_t* deps, const deps_entry_t& entry)
|
||||
{
|
||||
// Is this asset a "runtime" type?
|
||||
if (entry.asset_type != _X("runtime") || items.count(entry.asset_name))
|
||||
if (items.count(entry.asset_name))
|
||||
{
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
pal::string_t candidate;
|
||||
|
@ -539,45 +294,63 @@ void deps_resolver_t::resolve_tpa_list(
|
|||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
// Is this entry present in the secondary package cache?
|
||||
// Is an NI image for this entry present in the secondary package cache?
|
||||
else if (entry.to_hash_matched_path(ni_package_cache_dir, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
// Is this entry present in the secondary package cache? (note: no .ni extension)
|
||||
else if (entry.to_hash_matched_path(package_cache_dir, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
// Is this entry present locally?
|
||||
else if (m_local_assemblies.count(entry.asset_name))
|
||||
else if (!is_portable && m_sxs_assemblies.count(entry.asset_name))
|
||||
{
|
||||
// TODO: Case insensitive look up?
|
||||
add_tpa_asset(entry.asset_name, m_local_assemblies.find(entry.asset_name)->second, &items, output);
|
||||
add_tpa_asset(entry.asset_name, m_sxs_assemblies.find(entry.asset_name)->second, &items, output);
|
||||
}
|
||||
// Is this entry present in the package restore dir?
|
||||
else if (entry.to_full_path(package_dir, &candidate))
|
||||
// The app is portable so the asset should be picked up from relative subpath.
|
||||
else if (is_portable && deps->try_ni(entry).to_full_path(app_dir, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
}
|
||||
// Is this entry present in the package restore dir?
|
||||
else if (!package_dir.empty() && deps->try_ni(entry).to_full_path(package_dir, &candidate))
|
||||
{
|
||||
add_tpa_asset(entry.asset_name, candidate, &items, output);
|
||||
}
|
||||
};
|
||||
|
||||
const auto& deps_entries = m_deps->get_entries(deps_entry_t::asset_types::runtime);
|
||||
std::for_each(deps_entries.begin(), deps_entries.end(), [&](const deps_entry_t& entry) {
|
||||
process_entry(m_portable, m_deps.get(), entry);
|
||||
});
|
||||
const auto& fx_entries = m_portable ? m_fx_deps->get_entries(deps_entry_t::asset_types::runtime) : empty;
|
||||
std::for_each(fx_entries.begin(), fx_entries.end(), [&](const deps_entry_t& entry) {
|
||||
process_entry(false, m_fx_deps.get(), entry);
|
||||
});
|
||||
|
||||
// Finally, if the deps file wasn't present or has missing entries, then
|
||||
// add the app local assemblies to the TPA.
|
||||
for (const auto& kv : m_local_assemblies)
|
||||
for (const auto& kv : m_sxs_assemblies)
|
||||
{
|
||||
add_tpa_asset(kv.first, kv.second, &items, output);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Resolve the directories order for culture/native lookup
|
||||
// Resolve the directories order for resources/native lookup
|
||||
//
|
||||
// Description:
|
||||
// This general purpose function specifies priority order of directory lookup
|
||||
// for both native images and culture specific resource images. Lookup for
|
||||
// culture assemblies is done by looking up two levels above from the file
|
||||
// for both native images and resources specific resource images. Lookup for
|
||||
// resources assemblies is done by looking up two levels above from the file
|
||||
// path. Lookup for native images is done by looking up one level from the
|
||||
// file path.
|
||||
//
|
||||
// Parameters:
|
||||
// asset_type - The type of the asset that needs lookup, currently
|
||||
// supports "culture" and "native"
|
||||
// supports "resources" and "native"
|
||||
// app_dir - The application local directory
|
||||
// package_dir - The directory path to where packages are restored
|
||||
// package_cache_dir - The directory path to secondary cache for packages
|
||||
|
@ -594,60 +367,89 @@ void deps_resolver_t::resolve_probe_dirs(
|
|||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output)
|
||||
{
|
||||
assert(asset_type == _X("culture") || asset_type == _X("native"));
|
||||
assert(asset_type == _X("resources") || asset_type == _X("native"));
|
||||
|
||||
// For culture assemblies, we need to provide the base directory of the culture path.
|
||||
// For resources assemblies, we need to provide the base directory of the resources path.
|
||||
// For example: .../Foo/en-US/Bar.dll, then, the resolved path is .../Foo
|
||||
std::function<pal::string_t(const pal::string_t&)> culture = [] (const pal::string_t& str) {
|
||||
std::function<pal::string_t(const pal::string_t&)> resources = [] (const pal::string_t& str) {
|
||||
return get_directory(get_directory(str));
|
||||
};
|
||||
// For native assemblies, obtain the directory path from the file path
|
||||
std::function<pal::string_t(const pal::string_t&)> native = [] (const pal::string_t& str) {
|
||||
return get_directory(str);
|
||||
};
|
||||
std::function<pal::string_t(const pal::string_t&)>& action = (asset_type == _X("culture")) ? culture : native;
|
||||
|
||||
std::function<pal::string_t(const pal::string_t&)>& action = (asset_type == _X("resources")) ? resources : native;
|
||||
deps_entry_t::asset_types entry_type = (asset_type == _X("resources")) ? deps_entry_t::asset_types::resources : deps_entry_t::asset_types::native;
|
||||
std::set<pal::string_t> items;
|
||||
|
||||
std::vector<deps_entry_t> empty(0);
|
||||
const auto& entries = m_deps->get_entries(entry_type);
|
||||
const auto& fx_entries = m_portable ? m_fx_deps->get_entries(entry_type) : empty;
|
||||
|
||||
// Fill the "output" with serviced DLL directories if they are serviceable
|
||||
// and have an entry present.
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
auto add_serviced_entry = [&](const deps_entry_t& entry)
|
||||
{
|
||||
pal::string_t redirection_path;
|
||||
if (entry.is_serviceable && entry.asset_type == asset_type && entry.library_type == _X("Package") &&
|
||||
if (entry.is_serviceable && entry.library_type == _X("Package") &&
|
||||
m_svc.find_redirection(entry.library_name, entry.library_version, entry.relative_path, &redirection_path))
|
||||
{
|
||||
add_unique_path(asset_type, action(redirection_path), &items, output);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::for_each(entries.begin(), entries.end(), add_serviced_entry);
|
||||
std::for_each(fx_entries.begin(), fx_entries.end(), add_serviced_entry);
|
||||
|
||||
pal::string_t candidate;
|
||||
|
||||
// Take care of the secondary cache path
|
||||
if (!package_cache_dir.empty())
|
||||
{
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
auto add_package_cache_entry = [&](const deps_entry_t& entry)
|
||||
{
|
||||
if (entry.asset_type == asset_type && entry.to_hash_matched_path(package_cache_dir, &candidate))
|
||||
if (entry.to_hash_matched_path(package_cache_dir, &candidate))
|
||||
{
|
||||
add_unique_path(asset_type, action(candidate), &items, output);
|
||||
}
|
||||
}
|
||||
};
|
||||
std::for_each(entries.begin(), entries.end(), add_package_cache_entry);
|
||||
std::for_each(fx_entries.begin(), fx_entries.end(), add_package_cache_entry);
|
||||
}
|
||||
|
||||
// App local path
|
||||
add_unique_path(asset_type, app_dir, &items, output);
|
||||
|
||||
// Take care of the package restore path
|
||||
if (!package_dir.empty())
|
||||
// For portable path, the app relative directory must be used.
|
||||
if (m_portable)
|
||||
{
|
||||
for (const deps_entry_t& entry : m_deps_entries)
|
||||
std::for_each(entries.begin(), entries.end(), [&](const deps_entry_t& entry)
|
||||
{
|
||||
if (entry.asset_type == asset_type && entry.to_full_path(package_dir, &candidate))
|
||||
{
|
||||
add_unique_path(asset_type, action(candidate), &items, output);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// FX path if present
|
||||
if (!m_fx_dir.empty())
|
||||
{
|
||||
add_unique_path(asset_type, m_fx_dir, &items, output);
|
||||
}
|
||||
|
||||
// Take care of the package restore path
|
||||
if (!package_dir.empty())
|
||||
{
|
||||
auto add_packages_entry = [&](const deps_entry_t& entry)
|
||||
{
|
||||
if (entry.asset_type == asset_type && entry.to_full_path(package_dir, &candidate))
|
||||
{
|
||||
add_unique_path(asset_type, action(candidate), &items, output);
|
||||
}
|
||||
};
|
||||
std::for_each(entries.begin(), entries.end(), add_packages_entry);
|
||||
std::for_each(fx_entries.begin(), fx_entries.end(), add_packages_entry);
|
||||
}
|
||||
|
||||
// CLR path
|
||||
|
@ -656,7 +458,7 @@ void deps_resolver_t::resolve_probe_dirs(
|
|||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Entrypoint to resolve TPA, native and culture path ordering to pass to CoreCLR.
|
||||
// Entrypoint to resolve TPA, native and resources path ordering to pass to CoreCLR.
|
||||
//
|
||||
// Parameters:
|
||||
// app_dir - The application local directory
|
||||
|
@ -676,6 +478,6 @@ bool deps_resolver_t::resolve_probe_paths(
|
|||
{
|
||||
resolve_tpa_list(app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->tpa);
|
||||
resolve_probe_dirs(_X("native"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->native);
|
||||
resolve_probe_dirs(_X("culture"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->culture);
|
||||
resolve_probe_dirs(_X("resources"), app_dir, package_dir, package_cache_dir, clr_dir, &probe_paths->resources);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -8,48 +8,45 @@
|
|||
|
||||
#include "pal.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "deps_format.h"
|
||||
#include "deps_entry.h"
|
||||
#include "servicing_index.h"
|
||||
|
||||
struct deps_entry_t
|
||||
{
|
||||
pal::string_t library_type;
|
||||
pal::string_t library_name;
|
||||
pal::string_t library_version;
|
||||
pal::string_t library_hash;
|
||||
pal::string_t asset_type;
|
||||
pal::string_t asset_name;
|
||||
pal::string_t relative_path;
|
||||
bool is_serviceable;
|
||||
|
||||
// Given a "base" dir, yield the relative path in the package layout.
|
||||
bool to_full_path(const pal::string_t& root, pal::string_t* str) const;
|
||||
|
||||
// Given a "base" dir, yield the relative path in the package layout only if
|
||||
// the hash matches contents of the hash file.
|
||||
bool to_hash_matched_path(const pal::string_t& root, pal::string_t* str) const;
|
||||
};
|
||||
#include "runtime_config.h"
|
||||
|
||||
// Probe paths to be resolved for ordering
|
||||
struct probe_paths_t
|
||||
{
|
||||
pal::string_t tpa;
|
||||
pal::string_t native;
|
||||
pal::string_t culture;
|
||||
pal::string_t resources;
|
||||
};
|
||||
|
||||
class deps_resolver_t
|
||||
{
|
||||
public:
|
||||
deps_resolver_t(const arguments_t& args)
|
||||
deps_resolver_t(const pal::string_t& fx_dir, const runtime_config_t* config, const arguments_t& args)
|
||||
: m_svc(args.dotnet_servicing)
|
||||
, m_runtime_svc(args.dotnet_runtime_servicing)
|
||||
, m_fx_dir(fx_dir)
|
||||
, m_coreclr_index(-1)
|
||||
, m_portable(config->get_portable())
|
||||
, m_deps(nullptr)
|
||||
, m_fx_deps(nullptr)
|
||||
{
|
||||
m_deps_valid = parse_deps_file(args);
|
||||
m_deps_file = args.deps_path;
|
||||
if (m_portable)
|
||||
{
|
||||
m_fx_deps_file = get_fx_deps(fx_dir, config->get_fx_name());
|
||||
m_fx_deps = std::unique_ptr<deps_json_t>(new deps_json_t(false, m_fx_deps_file));
|
||||
m_deps = std::unique_ptr<deps_json_t>(new deps_json_t(true, m_deps_file, m_fx_deps->get_rid_fallback_graph()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_deps = std::unique_ptr<deps_json_t>(new deps_json_t(false, m_deps_file));
|
||||
}
|
||||
}
|
||||
|
||||
bool valid() { return m_deps_valid; }
|
||||
|
||||
bool valid() { return m_deps->is_valid() && (!m_portable || m_fx_deps->is_valid()); }
|
||||
|
||||
bool resolve_probe_paths(
|
||||
const pal::string_t& app_dir,
|
||||
|
@ -63,11 +60,24 @@ public:
|
|||
const pal::string_t& package_dir,
|
||||
const pal::string_t& package_cache_dir);
|
||||
|
||||
const pal::string_t& get_fx_deps_file() const
|
||||
{
|
||||
return m_fx_deps_file;
|
||||
}
|
||||
|
||||
const pal::string_t& get_deps_file() const
|
||||
{
|
||||
return m_deps_file;
|
||||
}
|
||||
private:
|
||||
|
||||
bool load();
|
||||
|
||||
bool parse_deps_file(const arguments_t& args);
|
||||
static pal::string_t get_fx_deps(const pal::string_t& fx_dir, const pal::string_t& fx_name)
|
||||
{
|
||||
pal::string_t fx_deps = fx_dir;
|
||||
pal::string_t fx_deps_name = pal::to_lower(fx_name) + _X(".deps.json");
|
||||
append_path(&fx_deps, fx_deps_name.c_str());
|
||||
return fx_deps;
|
||||
}
|
||||
|
||||
// Resolve order for TPA lookup.
|
||||
void resolve_tpa_list(
|
||||
|
@ -86,30 +96,42 @@ private:
|
|||
const pal::string_t& clr_dir,
|
||||
pal::string_t* output);
|
||||
|
||||
// Populate local assemblies from app_dir listing.
|
||||
void get_local_assemblies(const pal::string_t& dir);
|
||||
// Populate assemblies from the directory.
|
||||
void get_dir_assemblies(
|
||||
const pal::string_t& dir,
|
||||
const pal::string_t& dir_name,
|
||||
std::unordered_map<pal::string_t, pal::string_t>* dir_assemblies);
|
||||
|
||||
// Servicing index to resolve serviced assembly paths.
|
||||
servicing_index_t m_svc;
|
||||
|
||||
// Runtime servicing directory.
|
||||
pal::string_t m_runtime_svc;
|
||||
// Framework deps file.
|
||||
pal::string_t m_fx_dir;
|
||||
|
||||
// Map of simple name -> full path of local assemblies populated in priority
|
||||
// order of their extensions.
|
||||
std::unordered_map<pal::string_t, pal::string_t> m_local_assemblies;
|
||||
|
||||
// Entries in the dep file
|
||||
std::vector<deps_entry_t> m_deps_entries;
|
||||
// Map of simple name -> full path of local/fx assemblies populated
|
||||
// in priority order of their extensions.
|
||||
std::unordered_map<pal::string_t, pal::string_t> m_sxs_assemblies;
|
||||
|
||||
// Special entry for coreclr in the deps entries
|
||||
int m_coreclr_index;
|
||||
|
||||
// The dep file path
|
||||
pal::string_t m_deps_path;
|
||||
// The filepath for the app deps
|
||||
pal::string_t m_deps_file;
|
||||
|
||||
// The filepath for the fx deps
|
||||
pal::string_t m_fx_deps_file;
|
||||
|
||||
// Deps files for the fx
|
||||
std::unique_ptr<deps_json_t> m_fx_deps;
|
||||
|
||||
// Deps files for the app
|
||||
std::unique_ptr<deps_json_t> m_deps;
|
||||
|
||||
// Is the deps file valid
|
||||
bool m_deps_valid;
|
||||
|
||||
// Is the deps file portable app?
|
||||
bool m_portable;
|
||||
};
|
||||
|
||||
#endif // DEPS_RESOLVER_H
|
||||
|
|
|
@ -13,16 +13,24 @@ endif()
|
|||
include(../setup.cmake)
|
||||
|
||||
include_directories(../../common)
|
||||
include_directories(../json/casablanca/include)
|
||||
|
||||
# CMake does not recommend using globbing since it messes with the freshness checks
|
||||
set(SOURCES
|
||||
../../common/trace.cpp
|
||||
../../common/utils.cpp
|
||||
|
||||
../libhost.cpp
|
||||
../runtime_config.cpp
|
||||
../json/casablanca/src/json/json.cpp
|
||||
../json/casablanca/src/json/json_parsing.cpp
|
||||
../json/casablanca/src/json/json_serialization.cpp
|
||||
../json/casablanca/src/utilities/asyncrt_utils.cpp
|
||||
../args.cpp
|
||||
../hostpolicy.cpp
|
||||
../coreclr.cpp
|
||||
../deps_resolver.cpp
|
||||
../deps_format.cpp
|
||||
../deps_entry.cpp
|
||||
../servicing_index.cpp)
|
||||
|
||||
|
||||
|
@ -32,6 +40,8 @@ else()
|
|||
list(APPEND SOURCES ../../common/pal.unix.cpp)
|
||||
endif()
|
||||
|
||||
add_definitions(-D_NO_ASYNCRTIMP)
|
||||
add_definitions(-D_NO_PPLXIMP)
|
||||
add_definitions(-DCOREHOST_MAKE_DLL=1)
|
||||
|
||||
add_library(hostpolicy SHARED ${SOURCES})
|
||||
|
|
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 "trace.h"
|
||||
#include "deps_resolver.h"
|
||||
#include "fx_muxer.h"
|
||||
#include "utils.h"
|
||||
#include "coreclr.h"
|
||||
#include "cpprest/json.h"
|
||||
#include "libhost.h"
|
||||
#include "error_codes.h"
|
||||
|
||||
enum StatusCode
|
||||
{
|
||||
// 0x80 prefix to distinguish from corehost main's error codes.
|
||||
InvalidArgFailure = 0x81,
|
||||
CoreClrResolveFailure = 0x82,
|
||||
CoreClrBindFailure = 0x83,
|
||||
CoreClrInitFailure = 0x84,
|
||||
CoreClrExeFailure = 0x85,
|
||||
ResolverInitFailure = 0x86,
|
||||
ResolverResolveFailure = 0x87,
|
||||
};
|
||||
|
||||
int run(const arguments_t& args)
|
||||
corehost_init_t* g_init = nullptr;
|
||||
|
||||
int run(const corehost_init_t* init, const runtime_config_t& config, const arguments_t& args)
|
||||
{
|
||||
// Load the deps resolver
|
||||
deps_resolver_t resolver(args);
|
||||
deps_resolver_t resolver(init->fx_dir(), &config, args);
|
||||
|
||||
if (!resolver.valid())
|
||||
{
|
||||
trace::error(_X("Invalid .deps file"));
|
||||
|
@ -31,8 +27,8 @@ int run(const arguments_t& args)
|
|||
}
|
||||
|
||||
// Add packages directory
|
||||
pal::string_t packages_dir = args.nuget_packages;
|
||||
if (!pal::directory_exists(packages_dir))
|
||||
pal::string_t packages_dir = init->probe_dir();
|
||||
if (packages_dir.empty() || !pal::directory_exists(packages_dir))
|
||||
{
|
||||
(void)pal::get_default_packages_directory(&packages_dir);
|
||||
}
|
||||
|
@ -55,6 +51,8 @@ int run(const arguments_t& args)
|
|||
return StatusCode::ResolverResolveFailure;
|
||||
}
|
||||
|
||||
// TODO: config.get_runtime_properties();
|
||||
|
||||
// Build CoreCLR properties
|
||||
const char* property_keys[] = {
|
||||
"TRUSTED_PLATFORM_ASSEMBLIES",
|
||||
|
@ -63,20 +61,22 @@ int run(const arguments_t& args)
|
|||
"NATIVE_DLL_SEARCH_DIRECTORIES",
|
||||
"PLATFORM_RESOURCE_ROOTS",
|
||||
"AppDomainCompatSwitch",
|
||||
// TODO: pipe this from corehost.json
|
||||
"SERVER_GC",
|
||||
// Workaround: mscorlib does not resolve symlinks for AppContext.BaseDirectory dotnet/coreclr/issues/2128
|
||||
"APP_CONTEXT_BASE_DIRECTORY",
|
||||
"APP_CONTEXT_DEPS_FILES"
|
||||
};
|
||||
|
||||
auto tpa_paths_cstr = pal::to_stdstring(probe_paths.tpa);
|
||||
auto app_base_cstr = pal::to_stdstring(args.app_dir);
|
||||
auto native_dirs_cstr = pal::to_stdstring(probe_paths.native);
|
||||
auto culture_dirs_cstr = pal::to_stdstring(probe_paths.culture);
|
||||
auto resources_dirs_cstr = pal::to_stdstring(probe_paths.resources);
|
||||
|
||||
// Workaround for dotnet/cli Issue #488 and #652
|
||||
pal::string_t server_gc;
|
||||
std::string server_gc_cstr = (pal::getenv(_X("COREHOST_SERVER_GC"), &server_gc) && !server_gc.empty()) ? pal::to_stdstring(server_gc) : "0";
|
||||
|
||||
std::string deps = pal::to_stdstring(resolver.get_deps_file() + _X(";") + resolver.get_fx_deps_file());
|
||||
|
||||
const char* property_values[] = {
|
||||
// TRUSTED_PLATFORM_ASSEMBLIES
|
||||
|
@ -88,13 +88,15 @@ int run(const arguments_t& args)
|
|||
// NATIVE_DLL_SEARCH_DIRECTORIES
|
||||
native_dirs_cstr.c_str(),
|
||||
// PLATFORM_RESOURCE_ROOTS
|
||||
culture_dirs_cstr.c_str(),
|
||||
resources_dirs_cstr.c_str(),
|
||||
// AppDomainCompatSwitch
|
||||
"UseLatestBehaviorWhenTFMNotSpecified",
|
||||
// SERVER_GC
|
||||
server_gc_cstr.c_str(),
|
||||
// APP_CONTEXT_BASE_DIRECTORY
|
||||
app_base_cstr.c_str()
|
||||
app_base_cstr.c_str(),
|
||||
// APP_CONTEXT_DEPS_FILES,
|
||||
deps.c_str(),
|
||||
};
|
||||
|
||||
size_t property_size = sizeof(property_keys) / sizeof(property_keys[0]);
|
||||
|
@ -188,16 +190,43 @@ int run(const arguments_t& args)
|
|||
return exit_code;
|
||||
}
|
||||
|
||||
SHARED_API int corehost_load(corehost_init_t* init)
|
||||
{
|
||||
g_init = init;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SHARED_API int corehost_main(const int argc, const pal::char_t* argv[])
|
||||
{
|
||||
trace::setup();
|
||||
|
||||
assert(g_init);
|
||||
|
||||
// Take care of arguments
|
||||
arguments_t args;
|
||||
if (!parse_arguments(argc, argv, args))
|
||||
if (!parse_arguments(g_init->deps_file(), g_init->probe_dir(), g_init->host_mode(), argc, argv, &args))
|
||||
{
|
||||
return StatusCode::InvalidArgFailure;
|
||||
return StatusCode::LibHostInvalidArgs;
|
||||
}
|
||||
|
||||
return run(args);
|
||||
if (g_init->runtime_config())
|
||||
{
|
||||
return run(g_init, *g_init->runtime_config(), args);
|
||||
}
|
||||
else
|
||||
{
|
||||
runtime_config_t config(get_runtime_config_json(args.managed_application));
|
||||
if (!config.is_valid())
|
||||
{
|
||||
trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str());
|
||||
return StatusCode::InvalidConfigFile;
|
||||
}
|
||||
return run(g_init, config, args);
|
||||
}
|
||||
}
|
||||
|
||||
SHARED_API int corehost_unload()
|
||||
{
|
||||
g_init = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
|
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.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef __LIBHOST_H__
|
||||
#define __LIBHOST_H__
|
||||
|
||||
#define LIBHOST_NAME MAKE_LIBNAME("hostpolicy")
|
||||
|
||||
enum host_mode_t
|
||||
{
|
||||
invalid = 0,
|
||||
muxer,
|
||||
standalone,
|
||||
split_fx
|
||||
};
|
||||
|
||||
class runtime_config_t;
|
||||
|
||||
class corehost_init_t
|
||||
{
|
||||
const pal::string_t m_probe_path;
|
||||
const pal::string_t m_deps_file;
|
||||
const pal::string_t m_fx_dir;
|
||||
host_mode_t m_host_mode;
|
||||
const runtime_config_t* m_runtime_config;
|
||||
public:
|
||||
corehost_init_t(
|
||||
const pal::string_t& deps_file,
|
||||
const pal::string_t& probe_path,
|
||||
const pal::string_t& fx_dir,
|
||||
const host_mode_t mode,
|
||||
const runtime_config_t* runtime_config)
|
||||
: m_fx_dir(fx_dir)
|
||||
, m_runtime_config(runtime_config)
|
||||
, m_deps_file(deps_file)
|
||||
, m_probe_path(probe_path)
|
||||
, m_host_mode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
const host_mode_t host_mode() const
|
||||
{
|
||||
return m_host_mode;
|
||||
}
|
||||
|
||||
const pal::string_t& deps_file() const
|
||||
{
|
||||
return m_deps_file;
|
||||
}
|
||||
|
||||
const pal::string_t& probe_dir() const
|
||||
{
|
||||
return m_probe_path;
|
||||
}
|
||||
|
||||
const pal::string_t& fx_dir() const
|
||||
{
|
||||
return m_fx_dir;
|
||||
}
|
||||
|
||||
const runtime_config_t* runtime_config() const
|
||||
{
|
||||
return m_runtime_config;
|
||||
}
|
||||
};
|
||||
|
||||
pal::string_t get_runtime_config_json(const pal::string_t& app_path);
|
||||
host_mode_t detect_operating_mode(const int argc, const pal::char_t* argv[], pal::string_t* own_dir = nullptr);
|
||||
|
||||
#endif // __LIBHOST_H__
|
||||
|
|
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());
|
||||
if (iter != m_redirections.end())
|
||||
{
|
||||
pal::string_t full_path = m_patch_root;
|
||||
append_path(&full_path, iter->second.c_str());
|
||||
if (pal::file_exists(full_path))
|
||||
pal::string_t ni_root = m_patch_root;
|
||||
append_path(&ni_root, get_arch());
|
||||
|
||||
// First prefer the architecture specific NI image.
|
||||
pal::string_t paths[2] = { ni_root, m_patch_root };
|
||||
for (pal::string_t& full_path : paths)
|
||||
{
|
||||
*redirection = full_path;
|
||||
trace::verbose(_X("Servicing %s with %s"), stream.str().c_str(), redirection->c_str());
|
||||
return true;
|
||||
append_path(&full_path, iter->second.c_str());
|
||||
if (pal::file_exists(full_path))
|
||||
{
|
||||
*redirection = full_path;
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
pal::string_t stream_str = stream.str();
|
||||
trace::verbose(_X("Servicing %s with %s"), stream_str.c_str(), redirection->c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
trace::verbose(_X("Serviced file %s doesn't exist"), full_path.c_str());
|
||||
}
|
||||
trace::verbose(_X("Serviced file %s doesn't exist"), full_path.c_str());
|
||||
}
|
||||
|
||||
trace::verbose(_X("Entry %s not serviced or file doesn't exist"), stream.str().c_str());
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
auto stream_str = stream.str();
|
||||
trace::verbose(_X("Entry %s not serviced or file doesn't exist"), stream_str.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -112,7 +127,8 @@ void servicing_index_t::ensure_redirections()
|
|||
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
trace::verbose(_X("Adding servicing entry %s => %s"), sstream.str().c_str(), str.substr(from).c_str());
|
||||
auto stream_str = sstream.str();
|
||||
trace::verbose(_X("Adding servicing entry %s => %s"), stream_str.c_str(), str.substr(from).c_str());
|
||||
}
|
||||
|
||||
// Store just the filename.
|
||||
|
|
|
@ -33,4 +33,20 @@ if(WIN32)
|
|||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF")
|
||||
set(CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /DEBUG /OPT:REF /OPT:ICF")
|
||||
else()
|
||||
add_compile_options(-Wno-unused-local-typedef)
|
||||
endif()
|
||||
|
||||
if(CLI_CMAKE_PLATFORM_ARCH_I386)
|
||||
add_definitions(-D_TARGET_X86_=1)
|
||||
elseif(CLI_CMAKE_PLATFORM_ARCH_AMD64)
|
||||
add_definitions(-D_TARGET_AMD64_=1)
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown target architecture")
|
||||
endif()
|
||||
|
||||
if(${CLI_CMAKE_RUNTIME_ID} STREQUAL "")
|
||||
message(FATAL_ERROR "Runtime ID not specified")
|
||||
else()
|
||||
add_definitions(-DTARGET_RUNTIME_ID="${CLI_CMAKE_RUNTIME_ID}")
|
||||
endif()
|
||||
|
|
|
@ -15,10 +15,12 @@
|
|||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <Windows.h>
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
|
||||
#define HOST_EXE_NAME L"corehost.exe"
|
||||
#define xerr std::wcerr
|
||||
|
@ -93,11 +95,17 @@ namespace pal
|
|||
typedef HMODULE dll_t;
|
||||
typedef FARPROC proc_t;
|
||||
|
||||
pal::string_t to_string(int value);
|
||||
|
||||
bool getcwd(pal::string_t* recv);
|
||||
|
||||
inline int strcmp(const char_t* str1, const char_t* str2) { return ::wcscmp(str1, str2); }
|
||||
inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::_wcsicmp(str1, str2); }
|
||||
inline int strncmp(const char_t* str1, const char_t* str2, int len) { return ::wcsncmp(str1, str2, len); }
|
||||
inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::_wcsnicmp(str1, str2, len); }
|
||||
|
||||
pal::string_t to_lower(const pal::string_t& in);
|
||||
|
||||
inline size_t strlen(const char_t* str) { return ::wcslen(str); }
|
||||
inline void err_vprintf(const char_t* format, va_list vl) { ::vfwprintf(stderr, format, vl); ::fputws(_X("\r\n"), stderr); }
|
||||
|
||||
|
@ -126,10 +134,17 @@ namespace pal
|
|||
typedef void* dll_t;
|
||||
typedef void* proc_t;
|
||||
|
||||
pal::string_t to_string(int value);
|
||||
|
||||
bool getcwd(pal::string_t* recv);
|
||||
|
||||
inline int strcmp(const char_t* str1, const char_t* str2) { return ::strcmp(str1, str2); }
|
||||
inline int strcasecmp(const char_t* str1, const char_t* str2) { return ::strcasecmp(str1, str2); }
|
||||
inline int strncmp(const char_t* str1, const char_t* str2, int len) { return ::strncmp(str1, str2, len); }
|
||||
inline int strncasecmp(const char_t* str1, const char_t* str2, int len) { return ::strncasecmp(str1, str2, len); }
|
||||
|
||||
pal::string_t to_lower(const pal::string_t& in);
|
||||
|
||||
inline size_t strlen(const char_t* str) { return ::strlen(str); }
|
||||
inline void err_vprintf(const char_t* format, va_list vl) { ::vfprintf(stderr, format, vl); ::fputc('\n', stderr); }
|
||||
inline pal::string_t to_palstring(const std::string& str) { return str; }
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
@ -20,6 +22,33 @@
|
|||
#define symlinkEntrypointExecutable "/proc/curproc/exe"
|
||||
#endif
|
||||
|
||||
pal::string_t pal::to_string(int value) { return std::to_string(value); }
|
||||
|
||||
pal::string_t pal::to_lower(const pal::string_t& in)
|
||||
{
|
||||
pal::string_t ret = in;
|
||||
std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool pal::getcwd(pal::string_t* recv)
|
||||
{
|
||||
recv->clear();
|
||||
pal::char_t* buf = ::getcwd(nullptr, PATH_MAX + 1);
|
||||
if (buf == nullptr)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
perror("getcwd()");
|
||||
return false;
|
||||
}
|
||||
recv->assign(buf);
|
||||
::free(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pal::find_coreclr(pal::string_t* recv)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
|
@ -133,8 +162,7 @@ bool pal::getenv(const pal::char_t* name, pal::string_t* recv)
|
|||
|
||||
bool pal::realpath(pal::string_t* path)
|
||||
{
|
||||
pal::char_t buf[PATH_MAX];
|
||||
auto resolved = ::realpath(path->c_str(), buf);
|
||||
auto resolved = ::realpath(path->c_str(), nullptr);
|
||||
if (resolved == nullptr)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
|
@ -145,6 +173,7 @@ bool pal::realpath(pal::string_t* path)
|
|||
return false;
|
||||
}
|
||||
path->assign(resolved);
|
||||
::free(resolved);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,18 @@
|
|||
|
||||
static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> g_converter;
|
||||
|
||||
pal::string_t pal::to_lower(const pal::string_t& in)
|
||||
{
|
||||
pal::string_t ret = in;
|
||||
std::transform(ret.begin(), ret.end(), ret.begin(), ::towlower);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pal::string_t pal::to_string(int value)
|
||||
{
|
||||
return std::to_wstring(value);
|
||||
}
|
||||
|
||||
bool pal::find_coreclr(pal::string_t* recv)
|
||||
{
|
||||
pal::string_t candidate;
|
||||
|
@ -31,6 +43,35 @@ bool pal::find_coreclr(pal::string_t* recv)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool pal::getcwd(pal::string_t* recv)
|
||||
{
|
||||
recv->clear();
|
||||
|
||||
pal::char_t buf[MAX_PATH];
|
||||
DWORD result = GetCurrentDirectoryW(MAX_PATH, buf);
|
||||
if (result < MAX_PATH)
|
||||
{
|
||||
recv->assign(buf);
|
||||
return true;
|
||||
}
|
||||
else if (result != 0)
|
||||
{
|
||||
std::vector<pal::char_t> str;
|
||||
str.resize(result);
|
||||
result = GetCurrentDirectoryW(str.size(), str.data());
|
||||
assert(result <= str.size());
|
||||
if (result != 0)
|
||||
{
|
||||
recv->assign(str.data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
assert(result == 0);
|
||||
trace::error(_X("Failed to obtain working directory, HRESULT: 0x%X"), HRESULT_FROM_WIN32(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pal::load_library(const char_t* path, dll_t* dll)
|
||||
{
|
||||
*dll = ::LoadLibraryW(path);
|
||||
|
|
|
@ -55,16 +55,43 @@ pal::string_t get_executable(const pal::string_t& filename)
|
|||
return result;
|
||||
}
|
||||
|
||||
pal::string_t get_filename(const pal::string_t& path)
|
||||
pal::string_t strip_file_ext(const pal::string_t& path)
|
||||
{
|
||||
// Find the last dir separator
|
||||
auto path_sep = path.find_last_of(DIR_SEPARATOR);
|
||||
if (path_sep == pal::string_t::npos)
|
||||
if (path.empty())
|
||||
{
|
||||
return pal::string_t(path);
|
||||
return path;
|
||||
}
|
||||
return path.substr(0, path.rfind(_X('.')));
|
||||
}
|
||||
|
||||
pal::string_t get_filename_without_ext(const pal::string_t& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.substr(path_sep + 1);
|
||||
size_t name_pos = path.find_last_of(_X("/\\"));
|
||||
size_t dot_pos = path.rfind(_X('.'));
|
||||
size_t start_pos = (name_pos == pal::string_t::npos) ? 0 : (name_pos + 1);
|
||||
size_t count = (dot_pos == pal::string_t::npos) ? pal::string_t::npos : (dot_pos - start_pos);
|
||||
return path.substr(start_pos, count);
|
||||
}
|
||||
|
||||
pal::string_t get_filename(const pal::string_t& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
auto name_pos = path.find_last_of(DIR_SEPARATOR);
|
||||
if (name_pos == pal::string_t::npos)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
return path.substr(name_pos + 1);
|
||||
}
|
||||
|
||||
pal::string_t get_directory(const pal::string_t& path)
|
||||
|
@ -87,3 +114,49 @@ void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl)
|
|||
(*path)[pos] = repl;
|
||||
}
|
||||
}
|
||||
|
||||
const pal::char_t* get_arch()
|
||||
{
|
||||
#if _TARGET_AMD64_
|
||||
return _X("x64");
|
||||
#elif _TARGET_X86_
|
||||
return _X("x86");
|
||||
#else
|
||||
#error "Unknown target"
|
||||
#endif
|
||||
}
|
||||
|
||||
bool parse_known_args(
|
||||
const int argc,
|
||||
const pal::char_t* argv[],
|
||||
const std::vector<pal::string_t>& known_opts,
|
||||
std::unordered_map<pal::string_t, pal::string_t>* opts,
|
||||
int* num_args)
|
||||
{
|
||||
int arg_i = *num_args;
|
||||
while (arg_i < argc)
|
||||
{
|
||||
pal::string_t arg = argv[arg_i];
|
||||
if (std::find(known_opts.begin(), known_opts.end(), pal::to_lower(arg)) == known_opts.end())
|
||||
{
|
||||
// Unknown argument.
|
||||
break;
|
||||
}
|
||||
|
||||
// Known argument, so expect one more arg (value) to be present.
|
||||
if (arg_i + 1 >= argc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
(*opts)[arg] = argv[arg_i + 1];
|
||||
|
||||
// Increment for both the option and its value.
|
||||
arg_i += 2;
|
||||
}
|
||||
|
||||
*num_args = arg_i;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,17 @@ bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool mat
|
|||
bool starts_with(const pal::string_t& value, const pal::string_t& prefix, bool match_case);
|
||||
pal::string_t get_executable(const pal::string_t& filename);
|
||||
pal::string_t get_directory(const pal::string_t& path);
|
||||
pal::string_t strip_file_ext(const pal::string_t& path);
|
||||
pal::string_t get_filename(const pal::string_t& path);
|
||||
pal::string_t get_filename_without_ext(const pal::string_t& path);
|
||||
void append_path(pal::string_t* path1, const pal::char_t* path2);
|
||||
bool coreclr_exists_in_dir(const pal::string_t& candidate);
|
||||
void replace_char(pal::string_t* path, pal::char_t match, pal::char_t repl);
|
||||
const pal::char_t* get_arch();
|
||||
bool parse_known_args(
|
||||
const int argc,
|
||||
const pal::char_t* argv[],
|
||||
const std::vector<pal::string_t>& known_opts,
|
||||
std::unordered_map<pal::string_t, pal::string_t>* opts,
|
||||
int* num_args);
|
||||
#endif
|
||||
|
|
|
@ -2,64 +2,140 @@
|
|||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include "trace.h"
|
||||
#include "pal.h"
|
||||
#include "utils.h"
|
||||
#include "libhost.h"
|
||||
#include "corehost.h"
|
||||
#include "fx_ver.h"
|
||||
#include "error_codes.h"
|
||||
#include "policy_load.h"
|
||||
#include <array>
|
||||
|
||||
extern int corehost_main(const int argc, const pal::char_t* argv[]);
|
||||
#define LIBFXR_NAME MAKE_LIBNAME("hostfxr")
|
||||
|
||||
namespace
|
||||
bool corehost_t::hostpolicy_exists_in_svc(pal::string_t* resolved_dir)
|
||||
{
|
||||
enum StatusCode
|
||||
{
|
||||
Success = 0,
|
||||
CoreHostLibLoadFailure = 0x41,
|
||||
CoreHostLibMissingFailure = 0x42,
|
||||
CoreHostEntryPointFailure = 0x43,
|
||||
CoreHostCurExeFindFailure = 0x44,
|
||||
};
|
||||
|
||||
typedef int (*corehost_main_fn) (const int argc, const pal::char_t* argv[]);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Load the corehost library from the path specified
|
||||
//
|
||||
// Parameters:
|
||||
// lib_dir - dir path to the corehost library
|
||||
// h_host - handle to the library which will be kept live
|
||||
// main_fn - Contains the entrypoint "corehost_main" when returns success.
|
||||
//
|
||||
// Returns:
|
||||
// Non-zero exit code on failure. "main_fn" contains "corehost_main"
|
||||
// entrypoint on success.
|
||||
//
|
||||
StatusCode load_host_lib(const pal::string_t& lib_dir, pal::dll_t* h_host, corehost_main_fn* main_fn)
|
||||
{
|
||||
pal::string_t host_path = lib_dir;
|
||||
append_path(&host_path, LIBHOST_NAME);
|
||||
|
||||
// Missing library
|
||||
if (!pal::file_exists(host_path))
|
||||
#ifdef COREHOST_PACKAGE_SERVICING
|
||||
pal::string_t svc_dir;
|
||||
if (!pal::getenv(_X("DOTNET_SERVICING"), &svc_dir))
|
||||
{
|
||||
return StatusCode::CoreHostLibMissingFailure;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load library
|
||||
if (!pal::load_library(host_path.c_str(), h_host))
|
||||
pal::string_t path = svc_dir;
|
||||
append_path(&path, COREHOST_PACKAGE_NAME);
|
||||
append_path(&path, COREHOST_PACKAGE_VERSION);
|
||||
append_path(&path, COREHOST_PACKAGE_COREHOST_RELATIVE_DIR);
|
||||
if (library_exists_in_dir(path, LIBHOST_NAME))
|
||||
{
|
||||
trace::info(_X("Load library of %s failed"), host_path.c_str());
|
||||
resolved_dir->assign(path);
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
pal::string_t corehost_t::resolve_fxr_path(const pal::string_t& own_dir)
|
||||
{
|
||||
pal::string_t fxr_path;
|
||||
|
||||
pal::string_t fxr_dir = own_dir;
|
||||
append_path(&fxr_dir, _X("dotnethost"));
|
||||
append_path(&fxr_dir, _X("fxr"));
|
||||
if (pal::directory_exists(fxr_dir))
|
||||
{
|
||||
trace::info(_X("Reading fx resolver directory=[%s]"), fxr_dir.c_str());
|
||||
|
||||
std::vector<pal::string_t> list;
|
||||
pal::readdir(fxr_dir, &list);
|
||||
|
||||
fx_ver_t max_ver(-1, -1, -1);
|
||||
for (const auto& dir : list)
|
||||
{
|
||||
trace::info(_X("Considering fxr version=[%s]..."), dir.c_str());
|
||||
|
||||
pal::string_t ver = get_filename(dir);
|
||||
|
||||
fx_ver_t fx_ver(-1, -1, -1);
|
||||
if (fx_ver_t::parse(ver, &fx_ver, false))
|
||||
{
|
||||
max_ver = std::max(max_ver, fx_ver);
|
||||
}
|
||||
}
|
||||
|
||||
pal::string_t max_ver_str = max_ver.as_str();
|
||||
append_path(&fxr_dir, max_ver_str.c_str());
|
||||
trace::info(_X("Detected latest fxr version=[%s]..."), fxr_dir.c_str());
|
||||
}
|
||||
|
||||
const pal::string_t* dirs[] = { &fxr_dir, &own_dir };
|
||||
for (const auto& dir : dirs)
|
||||
{
|
||||
trace::info(_X("Considering fxr dir=[%s]..."), fxr_dir.c_str());
|
||||
if (policy_load_t::library_exists_in_dir(*dir, LIBFXR_NAME, &fxr_path))
|
||||
{
|
||||
trace::info(_X("Resolved fxr [%s]..."), fxr_path.c_str());
|
||||
return fxr_path;
|
||||
}
|
||||
}
|
||||
return pal::string_t();
|
||||
}
|
||||
|
||||
int corehost_t::resolve_fx_and_execute_app(const pal::string_t& own_dir, const int argc, const pal::char_t* argv[])
|
||||
{
|
||||
pal::dll_t fxr;
|
||||
|
||||
pal::string_t fxr_path = resolve_fxr_path(own_dir);
|
||||
|
||||
// Load library
|
||||
if (!pal::load_library(fxr_path.c_str(), &fxr))
|
||||
{
|
||||
trace::info(_X("Load library of %s failed"), fxr_path.c_str());
|
||||
return StatusCode::CoreHostLibLoadFailure;
|
||||
}
|
||||
|
||||
// Obtain entrypoint symbol
|
||||
*main_fn = (corehost_main_fn) pal::get_symbol(*h_host, "corehost_main");
|
||||
|
||||
return (*main_fn != nullptr)
|
||||
? StatusCode::Success
|
||||
: StatusCode::CoreHostEntryPointFailure;
|
||||
// Obtain entrypoint symbols
|
||||
hostfxr_main_fn main_fn = (hostfxr_main_fn) pal::get_symbol(fxr, "hostfxr_main");
|
||||
return main_fn(argc, argv);
|
||||
}
|
||||
|
||||
}; // end of anonymous namespace
|
||||
int corehost_t::run(const int argc, const pal::char_t* argv[])
|
||||
{
|
||||
pal::string_t own_dir;
|
||||
auto mode = detect_operating_mode(argc, argv, &own_dir);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case muxer:
|
||||
trace::info(_X("Host operating in Muxer mode"));
|
||||
return resolve_fx_and_execute_app(own_dir, argc, argv);
|
||||
|
||||
case split_fx:
|
||||
{
|
||||
trace::info(_X("Host operating in split mode; own dir=[%s]"), own_dir.c_str());
|
||||
corehost_init_t init(_X(""), _X(""), own_dir, host_mode_t::split_fx, nullptr);
|
||||
return policy_load_t::execute_app(own_dir, &init, argc, argv);
|
||||
}
|
||||
|
||||
case standalone:
|
||||
{
|
||||
trace::info(_X("Host operating from standalone app dir %s"), own_dir.c_str());
|
||||
|
||||
pal::string_t svc_dir;
|
||||
corehost_init_t init(_X(""), _X(""), _X(""), host_mode_t::standalone, nullptr);
|
||||
return policy_load_t::execute_app(
|
||||
hostpolicy_exists_in_svc(&svc_dir) ? svc_dir : own_dir, &init, argc, argv);
|
||||
}
|
||||
return StatusCode::CoreHostLibMissingFailure;
|
||||
|
||||
default:
|
||||
trace::error(_X("Unknown mode detected or could not resolve the mode."));
|
||||
return StatusCode::CoreHostResolveModeFailure;
|
||||
}
|
||||
}
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "deps_format.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
int __cdecl wmain(const int argc, const pal::char_t* argv[])
|
||||
|
@ -69,56 +145,19 @@ int main(const int argc, const pal::char_t* argv[])
|
|||
{
|
||||
trace::setup();
|
||||
|
||||
pal::dll_t corehost;
|
||||
//deps_json_t deps(true, _X("H:\\code\\sharedfx\\PortableApp\\PortableAppWithNative.deps.json"));
|
||||
//deps_json_t deps2(false, _X("H:\\code\\sharedfx\\StandaloneApp\\StandaloneApp.deps.json"));
|
||||
|
||||
#ifdef COREHOST_PACKAGE_SERVICING
|
||||
// No custom host asked, so load the corehost if serviced first.
|
||||
pal::string_t svc_dir;
|
||||
if (pal::getenv(_X("DOTNET_SERVICING"), &svc_dir))
|
||||
if (trace::is_enabled())
|
||||
{
|
||||
pal::string_t path = svc_dir;
|
||||
append_path(&path, COREHOST_PACKAGE_NAME);
|
||||
append_path(&path, COREHOST_PACKAGE_VERSION);
|
||||
append_path(&path, COREHOST_PACKAGE_COREHOST_RELATIVE_DIR);
|
||||
|
||||
corehost_main_fn host_main;
|
||||
StatusCode code = load_host_lib(path, &corehost, &host_main);
|
||||
if (code != StatusCode::Success)
|
||||
trace::info(_X("--- Invoked host main = {"));
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
trace::info(_X("Failed to load host library from servicing dir: %s; Status=%08X"), path.c_str(), code);
|
||||
// Ignore all errors for the servicing case, and proceed to the next step.
|
||||
}
|
||||
else
|
||||
{
|
||||
trace::info(_X("Calling host entrypoint from library at servicing dir %s"), path.c_str());
|
||||
return host_main(argc, argv);
|
||||
trace::info(_X("%s"), argv[i]);
|
||||
}
|
||||
trace::info(_X("}"));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get current path to look for the library app locally.
|
||||
pal::string_t own_path;
|
||||
if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path))
|
||||
{
|
||||
trace::error(_X("Failed to locate current executable"));
|
||||
return StatusCode::CoreHostCurExeFindFailure;
|
||||
}
|
||||
|
||||
// Local load of the corehost library.
|
||||
auto own_dir = get_directory(own_path);
|
||||
|
||||
corehost_main_fn host_main;
|
||||
StatusCode code = load_host_lib(own_dir, &corehost, &host_main);
|
||||
switch (code)
|
||||
{
|
||||
// Success, call the entrypoint.
|
||||
case StatusCode::Success:
|
||||
trace::info(_X("Calling host entrypoint from library at own dir %s"), own_dir.c_str());
|
||||
return host_main(argc, argv);
|
||||
|
||||
// Some other fatal error including StatusCode::CoreHostLibMissingFailure.
|
||||
default:
|
||||
trace::error(_X("Error loading the host library from own dir: %s; Status=%08X"), own_dir.c_str(), code);
|
||||
return code;
|
||||
}
|
||||
corehost_t corehost;
|
||||
return corehost.run(argc, argv);
|
||||
}
|
||||
|
||||
|
|
36
src/corehost/corehost.h
Normal file
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…
Reference in a new issue