Merge pull request #2191 from schellap/dev-json

Support runtimeconfig.dev.json
This commit is contained in:
Senthil 2016-04-01 20:12:50 -07:00
commit 5b38dd6765
14 changed files with 282 additions and 222 deletions

View file

@ -67,7 +67,7 @@ bool parse_arguments(const pal::string_t& deps_path, const std::vector<pal::stri
args.app_argc = argc - 1;
}
std::unordered_map<pal::string_t, pal::string_t> opts;
std::unordered_map<pal::string_t, std::vector<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))
@ -79,10 +79,13 @@ bool parse_arguments(const pal::string_t& deps_path, const std::vector<pal::stri
args.app_argv += num_args;
pal::string_t opts_deps_file = _X("--depsfile");
pal::string_t opts_probe_path = _X("--additionalprobingpath");
pal::string_t deps_file = opts.count(opts_deps_file) ? opts[opts_deps_file] : deps_path;
pal::string_t deps_file = get_last_known_arg(opts, opts_deps_file, deps_path);
if (opts.count(opts_probe_path))
{
args.probe_paths.push_back(opts[opts_probe_path]);
for (const auto& str : opts[opts_probe_path])
{
args.probe_paths.push_back(str);
}
}
if (!deps_file.empty())

View file

@ -207,11 +207,6 @@ void deps_resolver_t::setup_probe_config(
{
// Additional paths
bool roll_fwd = config.get_fx_roll_fwd();
if (m_package_cache == probe)
{
// FIXME: For now, no roll forward for nuget cache. This should come from config runtimeconfig.dev.json.
roll_fwd = false;
}
m_probes.push_back(probe_config_t::additional(probe, roll_fwd));
}
@ -240,17 +235,6 @@ void deps_resolver_t::setup_additional_probes(const std::vector<pal::string_t>&
iter = m_additional_probes.erase(iter);
}
}
// FIXME: Remove nuget support with runtimeconfig.dev.json
// and setup roll forward to come from the config in setup_probe_config
if (m_additional_probes.empty())
{
// FIXME: Remove this function call entirely including the functiond definition.
(void)pal::get_default_packages_directory(&m_package_cache);
if (pal::directory_exists(m_package_cache))
{
m_additional_probes.push_back(m_package_cache);
}
}
}
bool deps_resolver_t::probe_entry_in_configs(const deps_entry_t& entry, pal::string_t* candidate)

View file

@ -226,11 +226,151 @@ bool fx_muxer_t::resolve_sdk_dotnet_path(const pal::string_t& own_dir, pal::stri
return !retval.empty();
}
int muxer_usage()
{
trace::error(_X("Usage: dotnet [--help | app.dll]"));
return StatusCode::InvalidArgFailure;
}
int fx_muxer_t::parse_args_and_execute(const pal::string_t& own_dir, int argoff, int argc, const pal::char_t* argv[], bool exec_mode, bool* is_an_app)
{
*is_an_app = true;
std::vector<pal::string_t> known_opts = { _X("--additionalprobingpath") };
if (exec_mode)
{
known_opts.push_back(_X("--depsfile"));
}
// Parse the known muxer arguments if any.
int num_parsed = 0;
std::unordered_map<pal::string_t, std::vector<pal::string_t>> opts;
if (!parse_known_args(argc - argoff, &argv[argoff], known_opts, &opts, &num_parsed))
{
trace::error(_X("Failed to parse supported arguments."));
return InvalidArgFailure;
}
int cur_i = argoff + num_parsed;
if (cur_i >= argc)
{
return muxer_usage();
}
pal::string_t app_candidate = argv[cur_i];
bool is_app_runnable = ends_with(app_candidate, _X(".dll"), false) || ends_with(app_candidate, _X(".exe"), false);
// If exec mode is on, then check we have a dll at this point
if (exec_mode)
{
if (!is_app_runnable)
{
trace::error(_X("dotnet exec needs a dll to execute. Try dotnet [--help]"));
return InvalidArgFailure;
}
}
// For non-exec, there is CLI invocation or app.dll execution after known args.
else
{
// Test if we have a real dll at this point.
if (!is_app_runnable)
{
// No we don't have a dll, this must be routed to the CLI.
*is_an_app = false;
return Success;
}
}
// Transform dotnet [exec] [--additionalprobingpath path] [--depsfile file] dll [args] -> dotnet dll [args]
std::vector<const pal::char_t*> vec_argv;
const pal::char_t** new_argv = argv;
int new_argc = argc;
if (cur_i != 1)
{
vec_argv.resize(argc - cur_i + 1, 0); // +1 for dotnet
memcpy(vec_argv.data() + 1, argv + cur_i, (argc - cur_i) * sizeof(pal::char_t*));
vec_argv[0] = argv[0];
new_argv = vec_argv.data();
new_argc = vec_argv.size();
}
pal::string_t opts_deps_file = _X("--depsfile");
pal::string_t opts_probe_path = _X("--additionalprobingpath");
pal::string_t deps_file = get_last_known_arg(opts, opts_deps_file, _X(""));
std::vector<pal::string_t> probe_paths = opts.count(opts_probe_path) ? opts[opts_probe_path] : std::vector<pal::string_t>();
trace::verbose(_X("Current argv is %s"), app_candidate.c_str());
pal::string_t app_or_deps = deps_file.empty() ? app_candidate : deps_file;
pal::string_t no_json = app_candidate;
pal::string_t dev_config_file;
auto config_file = get_runtime_config_from_file(no_json, &dev_config_file);
runtime_config_t config(config_file, dev_config_file);
for (const auto& path : config.get_probe_paths())
{
probe_paths.push_back(path);
}
if (!config.is_valid())
{
trace::error(_X("Invalid runtimeconfig.json [%s] [%s]"), config.get_path().c_str(), config.get_dev_path().c_str());
return StatusCode::InvalidConfigFile;
}
if (!deps_file.empty() && !pal::file_exists(deps_file))
{
trace::error(_X("Deps file [%s] specified but doesn't exist"), deps_file.c_str());
return StatusCode::InvalidArgFailure;
}
if (config.get_portable())
{
trace::verbose(_X("Executing as a portable app as per config file [%s]"), config_file.c_str());
pal::string_t fx_dir = resolve_fx_dir(own_dir, &config);
corehost_init_t init(deps_file, probe_paths, fx_dir, host_mode_t::muxer, &config);
return execute_app(fx_dir, &init, new_argc, new_argv);
}
else
{
trace::verbose(_X("Executing as a standalone app as per config file [%s]"), config_file.c_str());
pal::string_t impl_dir = get_directory(app_or_deps);
if (!library_exists_in_dir(impl_dir, LIBHOSTPOLICY_NAME, nullptr) && !probe_paths.empty() && !deps_file.empty())
{
bool found = false;
pal::string_t candidate = impl_dir;
deps_json_t deps_json(false, deps_file);
for (const auto& probe_path : probe_paths)
{
trace::verbose(_X("Considering %s for hostpolicy library"), probe_path.c_str());
if (deps_json.is_valid() &&
deps_json.has_hostpolicy_entry() &&
deps_json.get_hostpolicy_entry().to_full_path(probe_path, &candidate))
{
found = true; // candidate contains the right path.
break;
}
}
if (!found)
{
trace::error(_X("Policy library either not found in deps [%s] or not found in %d probe paths."), deps_file.c_str(), probe_paths.size());
return StatusCode::CoreHostLibMissingFailure;
}
impl_dir = get_directory(candidate);
}
corehost_init_t init(deps_file, probe_paths, _X(""), host_mode_t::muxer, &config);
return execute_app(impl_dir, &init, new_argc, new_argv);
}
}
/* static */
int fx_muxer_t::execute(const int argc, const pal::char_t* argv[])
{
trace::verbose(_X("--- Executing in muxer mode..."));
if (argc <= 1)
{
return muxer_usage();
}
pal::string_t own_path;
// Get the full name of the application
@ -239,163 +379,43 @@ int fx_muxer_t::execute(const int argc, const pal::char_t* argv[])
trace::error(_X("Failed to locate current executable"));
return StatusCode::LibHostCurExeFindFailure;
}
auto own_dir = get_directory(own_path);
if (argc <= 1)
bool is_an_app = false;
if (pal::strcasecmp(_X("exec"), argv[1]) == 0)
{
trace::error(_X("Usage: dotnet [--help | app.dll]"));
return StatusCode::InvalidArgFailure;
return parse_args_and_execute(own_dir, 2, argc, argv, true, &is_an_app); // arg offset 2 for dotnet, exec
}
if (ends_with(argv[1], _X(".dll"), false))
int result = parse_args_and_execute(own_dir, 1, argc, argv, false, &is_an_app); // arg offset 1 for dotnet
if (is_an_app)
{
pal::string_t app_path = argv[1];
if (!pal::realpath(&app_path))
{
trace::error(_X("Could not resolve app's full path [%s]"), app_path.c_str());
return StatusCode::LibHostExecModeFailure;
}
auto config_file = get_runtime_config_from_file(app_path);
runtime_config_t config(config_file);
if (!config.is_valid())
{
trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str());
return StatusCode::InvalidConfigFile;
}
if (config.get_portable())
{
trace::verbose(_X("Executing as a portable app as per config file [%s]"), config_file.c_str());
pal::string_t fx_dir = resolve_fx_dir(own_dir, &config);
corehost_init_t init(_X(""), config.get_probe_paths(), fx_dir, host_mode_t::muxer, &config);
return execute_app(fx_dir, &init, argc, argv);
}
else
{
trace::verbose(_X("Executing as a standlone app as per config file [%s]"), config_file.c_str());
corehost_init_t init(_X(""), config.get_probe_paths(), _X(""), host_mode_t::muxer, &config);
return execute_app(get_directory(app_path), &init, argc, argv);
}
return result;
}
else
// Could not execute as an app, try the CLI SDK dotnet.dll
pal::string_t sdk_dotnet;
if (!resolve_sdk_dotnet_path(own_dir, &sdk_dotnet))
{
if (pal::strcasecmp(_X("exec"), argv[1]) == 0)
{
std::vector<pal::string_t> known_opts = { _X("--depsfile"), _X("--additionalprobingpath") };
trace::verbose(_X("Exec mode, parsing known args"));
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))
{
trace::error(_X("Failed to parse known arguments."));
return InvalidArgFailure;
}
int cur_i = 2 + num_args;
if (cur_i >= argc)
{
trace::error(_X("Parsed known args, but need more arguments."));
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 opts_deps_file = _X("--depsfile");
pal::string_t opts_probe_path = _X("--additionalprobingpath");
pal::string_t deps_file = opts.count(opts_deps_file) ? opts[opts_deps_file] : _X("");
pal::string_t probe_path = opts.count(opts_probe_path) ? opts[opts_probe_path] : _X("");
trace::verbose(_X("Current argv is %s"), argv[cur_i]);
pal::string_t app_or_deps = deps_file.empty() ? argv[cur_i] : deps_file;
pal::string_t no_json = argv[cur_i];
auto config_file = get_runtime_config_from_file(no_json);
runtime_config_t config(config_file);
if (!config.is_valid())
{
trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str());
return StatusCode::InvalidConfigFile;
}
if (!deps_file.empty() && !pal::file_exists(deps_file))
{
trace::error(_X("Deps file [%s] specified but doesn't exist"), deps_file.c_str());
return StatusCode::InvalidArgFailure;
}
std::vector<pal::string_t> probe_paths = { probe_path };
if (config.get_portable())
{
trace::verbose(_X("Executing as a portable app as per config file [%s]"), config_file.c_str());
pal::string_t fx_dir = resolve_fx_dir(own_dir, &config);
corehost_init_t init(deps_file, probe_paths, fx_dir, host_mode_t::muxer, &config);
return execute_app(fx_dir, &init, new_argv.size(), new_argv.data());
}
else
{
trace::verbose(_X("Executing as a standalone app as per config file [%s]"), config_file.c_str());
pal::string_t impl_dir = get_directory(app_or_deps);
if (!library_exists_in_dir(impl_dir, LIBHOSTPOLICY_NAME, nullptr) && !probe_path.empty() && !deps_file.empty())
{
deps_json_t deps_json(false, deps_file);
pal::string_t candidate = impl_dir;
if (!deps_json.has_hostpolicy_entry() ||
!deps_json.get_hostpolicy_entry().to_full_path(probe_path, &candidate))
{
trace::error(_X("Policy library either not found in deps [%s] or not found in [%s]"), deps_file.c_str(), probe_path.c_str());
return StatusCode::CoreHostLibMissingFailure;
}
impl_dir = get_directory(candidate);
}
corehost_init_t init(deps_file, probe_paths, _X(""), host_mode_t::muxer, &config);
return 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))
{
trace::error(_X("Could not resolve SDK directory from [%s]"), own_dir.c_str());
return StatusCode::LibHostSdkFindFailure;
}
append_path(&sdk_dotnet, _X("dotnet.dll"));
if (!pal::file_exists(sdk_dotnet))
{
trace::error(_X("Could not find dotnet.dll at [%s]"), sdk_dotnet.c_str());
return StatusCode::LibHostSdkFindFailure;
}
// 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 dotnet SDK dll=[%s]"), sdk_dotnet.c_str());
auto config_file = get_runtime_config_from_file(sdk_dotnet);
runtime_config_t config(config_file);
if (config.get_portable())
{
trace::verbose(_X("Executing dotnet.dll as a portable app as per config file [%s]"), config_file.c_str());
pal::string_t fx_dir = resolve_fx_dir(own_dir, &config);
corehost_init_t init(_X(""), std::vector<pal::string_t>(), fx_dir, host_mode_t::muxer, &config);
return execute_app(fx_dir, &init, new_argv.size(), new_argv.data());
}
else
{
trace::verbose(_X("Executing dotnet.dll as a standalone app as per config file [%s]"), config_file.c_str());
corehost_init_t init(_X(""), std::vector<pal::string_t>(), _X(""), host_mode_t::muxer, &config);
return execute_app(get_directory(sdk_dotnet), &init, new_argv.size(), new_argv.data());
}
}
trace::error(_X("Could not resolve SDK directory from [%s]"), own_dir.c_str());
return StatusCode::LibHostSdkFindFailure;
}
append_path(&sdk_dotnet, _X("dotnet.dll"));
if (!pal::file_exists(sdk_dotnet))
{
trace::error(_X("Could not find dotnet.dll at [%s]"), sdk_dotnet.c_str());
return StatusCode::LibHostSdkFindFailure;
}
// 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 dotnet SDK dll=[%s]"), sdk_dotnet.c_str());
return parse_args_and_execute(own_dir, 1, new_argv.size(), new_argv.data(), false, &is_an_app);
}

View file

@ -15,6 +15,7 @@ class fx_muxer_t
public:
static int execute(const int argc, const pal::char_t* argv[]);
private:
static int parse_args_and_execute(const pal::string_t& own_dir, int argoff, int argc, const pal::char_t* argv[], bool exec_mode, bool* can_execute);
static pal::string_t resolve_fx_dir(const pal::string_t& muxer_path, runtime_config_t* runtime);
static pal::string_t resolve_cli_version(const pal::string_t& global);
static bool resolve_sdk_dotnet_path(const pal::string_t& own_dir, pal::string_t* cli_sdk);

View file

@ -240,13 +240,16 @@ SHARED_API int corehost_main(const int argc, const pal::char_t* argv[])
}
else
{
auto config_path = get_runtime_config_from_file(args.managed_application);
runtime_config_t config(config_path);
pal::string_t dev_config_file;
auto config_path = get_runtime_config_from_file(args.managed_application, &dev_config_file);
runtime_config_t config(config_path, dev_config_file);
if (!config.is_valid())
{
trace::error(_X("Invalid runtimeconfig.json [%s]"), config.get_path().c_str());
trace::error(_X("Invalid runtimeconfig.json [%s] [%s]"), config.get_path().c_str(), config.get_dev_path().c_str());
return StatusCode::InvalidConfigFile;
}
// TODO: This is ugly. The whole runtime config/probe paths business should all be resolved by and come from the hostfxr.cpp.
args.probe_paths.insert(args.probe_paths.end(), config.get_probe_paths().begin(), config.get_probe_paths().end());
return run(g_init, config, args);
}
}

View file

@ -6,14 +6,19 @@
#include "trace.h"
#include "libhost.h"
pal::string_t get_runtime_config_from_file(const pal::string_t& file)
pal::string_t get_runtime_config_from_file(const pal::string_t& file, pal::string_t* dev_cfg)
{
auto name = get_filename_without_ext(file);
auto json_name = name + _X(".runtimeconfig.json");
auto dev_json_name = name + _X(".runtimeconfig.dev.json");
auto json_path = get_directory(file);
auto dev_json_path = json_path;
append_path(&json_path, json_name.c_str());
trace::verbose(_X("Runtime config is %s"), json_path.c_str());
append_path(&dev_json_path, dev_json_name.c_str());
trace::verbose(_X("Runtime config is cfg=%s dev=%s"), json_path.c_str(), dev_json_path.c_str());
dev_cfg->assign(dev_json_path);
return json_path;
}

View file

@ -80,7 +80,7 @@ public:
}
};
pal::string_t get_runtime_config_from_file(const pal::string_t& file);
pal::string_t get_runtime_config_from_file(const pal::string_t& file, pal::string_t* dev_config_file);
host_mode_t detect_operating_mode(const int argc, const pal::char_t* argv[], pal::string_t* own_dir = nullptr);
void try_patch_roll_forward_in_dir(const pal::string_t& cur_dir, const fx_ver_t& start_ver, pal::string_t* max_str, bool only_production);

View file

@ -8,9 +8,10 @@
#include "runtime_config.h"
#include <cassert>
runtime_config_t::runtime_config_t(const pal::string_t& path)
runtime_config_t::runtime_config_t(const pal::string_t& path, const pal::string_t& dev_path)
: m_fx_roll_fwd(true)
, m_path(path)
, m_dev_path(dev_path)
, m_portable(false)
{
m_valid = ensure_parsed();
@ -19,6 +20,8 @@ runtime_config_t::runtime_config_t(const pal::string_t& path)
bool runtime_config_t::parse_opts(const json_value& opts)
{
// Note: both runtime_config and dev_runtime_config call into the function.
// runtime_config will override whatever dev_runtime_config populated.
if (opts.is_null())
{
return true;
@ -43,14 +46,14 @@ bool runtime_config_t::parse_opts(const json_value& opts)
{
if (probe_paths->second.is_string())
{
m_probe_paths.push_back(probe_paths->second.as_string());
m_probe_paths.insert(m_probe_paths.begin(), probe_paths->second.as_string());
}
else
{
const auto& arr = probe_paths->second.as_array();
for (const auto& str : arr)
for (auto iter = arr.rbegin(); iter != arr.rend(); iter++)
{
m_probe_paths.push_back(str.as_string());
m_probe_paths.push_front(iter->as_string());
}
}
}
@ -75,8 +78,59 @@ bool runtime_config_t::parse_opts(const json_value& opts)
return true;
}
bool runtime_config_t::ensure_dev_config_parsed()
{
trace::verbose(_X("Attempting to read dev runtime config: %s"), m_dev_path.c_str());
pal::string_t retval;
if (!pal::file_exists(m_dev_path))
{
// Not existing is not an error.
return true;
}
// Set dev mode default values, if the file exists.
m_fx_roll_fwd = false;
pal::ifstream_t file(m_dev_path);
if (!file.good())
{
trace::verbose(_X("File stream not good %s"), m_dev_path.c_str());
return false;
}
if (skip_utf8_bom(&file))
{
trace::verbose(_X("UTF-8 BOM skipped while reading [%s]"), m_dev_path.c_str());
}
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_opts(iter->second);
}
}
catch (const web::json::json_exception& je)
{
pal::string_t jes = pal::to_palstring(je.what());
trace::error(_X("A JSON parsing exception occurred in [%s]: %s"), m_dev_path.c_str(), jes.c_str());
return false;
}
return true;
}
bool runtime_config_t::ensure_parsed()
{
trace::verbose(_X("Attempting to read runtime config: %s"), m_path.c_str());
if (!ensure_dev_config_parsed())
{
trace::verbose(_X("Did not successfully parse the runtimeconfig.dev.json"));
}
pal::string_t retval;
if (!pal::file_exists(m_path))
{
@ -138,7 +192,7 @@ bool runtime_config_t::get_portable() const
return m_portable;
}
const std::vector<pal::string_t>& runtime_config_t::get_probe_paths() const
const std::list<pal::string_t>& runtime_config_t::get_probe_paths() const
{
return m_probe_paths;
}

View file

@ -1,6 +1,8 @@
// 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 <list>
#include "pal.h"
#include "cpprest/json.h"
@ -9,13 +11,14 @@ typedef web::json::value json_value;
class runtime_config_t
{
public:
runtime_config_t(const pal::string_t& path);
runtime_config_t(const pal::string_t& path, const pal::string_t& dev_path);
bool is_valid() { return m_valid; }
const pal::string_t& get_path() { return m_path; }
const pal::string_t& get_dev_path() { return m_dev_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;
const std::vector<pal::string_t>& get_probe_paths() const;
const std::list<pal::string_t>& get_probe_paths() const;
bool get_fx_roll_fwd() const;
bool get_portable() const;
bool parse_opts(const json_value& opts);
@ -23,15 +26,17 @@ public:
private:
bool ensure_parsed();
bool ensure_dev_config_parsed();
std::unordered_map<pal::string_t, pal::string_t> m_properties;
std::vector<std::string> m_prop_keys;
std::vector<std::string> m_prop_values;
std::vector<pal::string_t> m_probe_paths;
std::list<pal::string_t> m_probe_paths;
pal::string_t m_fx_name;
pal::string_t m_fx_ver;
bool m_fx_roll_fwd;
pal::string_t m_dev_path;
pal::string_t m_path;
bool m_portable;
bool m_valid;

View file

@ -168,7 +168,6 @@ namespace pal
bool get_own_executable_path(string_t* recv);
bool getenv(const char_t* name, string_t* recv);
bool get_default_packages_directory(string_t* recv);
bool get_default_extensions_directory(string_t* recv);
bool is_path_rooted(const string_t& path);

View file

@ -112,28 +112,6 @@ bool pal::is_path_rooted(const pal::string_t& path)
return path.front() == '/';
}
bool pal::get_default_packages_directory(pal::string_t* recv)
{
recv->clear();
pal::string_t dir;
if (!pal::getenv("HOME", &dir))
{
struct passwd* pw = getpwuid(getuid());
if (pw && pw->pw_dir)
{
dir.assign(pw->pw_dir);
}
}
if (dir.empty())
{
return false;
}
append_path(&dir, _X(".nuget"));
append_path(&dir, _X("packages"));
recv->assign(dir);
return true;
}
bool pal::get_default_extensions_directory(string_t* recv)
{
recv->clear();

View file

@ -123,18 +123,6 @@ void pal::unload_library(dll_t library)
// No-op. On windows, we pin the library, so it can't be unloaded.
}
bool pal::get_default_packages_directory(string_t* recv)
{
recv->clear();
if (!pal::getenv(_X("USERPROFILE"), recv))
{
return false;
}
append_path(recv, _X(".nuget"));
append_path(recv, _X("packages"));
return true;
}
bool pal::get_default_extensions_directory(string_t* recv)
{
recv->clear();

View file

@ -156,18 +156,34 @@ const pal::char_t* get_arch()
#endif
}
pal::string_t get_last_known_arg(
const std::unordered_map<pal::string_t, std::vector<pal::string_t>>& opts,
const pal::string_t& opt_key,
const pal::string_t& de_fault)
{
if (opts.count(opt_key))
{
const auto& val = opts.find(opt_key)->second;
return val[val.size() - 1];
}
return de_fault;
}
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,
// Although multimap would provide this functionality the order of kv, values are
// not preserved in C++ < C++0x
std::unordered_map<pal::string_t, std::vector<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())
pal::string_t arg_lower = pal::to_lower(arg);
if (std::find(known_opts.begin(), known_opts.end(), arg_lower) == known_opts.end())
{
// Unknown argument.
break;
@ -180,7 +196,7 @@ bool parse_known_args(
}
trace::verbose(_X("Parsed known arg %s = %s"), arg.c_str(), argv[arg_i + 1]);
(*opts)[arg] = argv[arg_i + 1];
(*opts)[arg_lower].push_back(argv[arg_i + 1]);
// Increment for both the option and its value.
arg_i += 2;

View file

@ -20,11 +20,15 @@ bool library_exists_in_dir(const pal::string_t& lib_dir, const pal::string_t& li
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();
pal::string_t get_last_known_arg(
const std::unordered_map<pal::string_t, std::vector<pal::string_t>>& opts,
const pal::string_t& opt_key,
const pal::string_t& de_fault);
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,
std::unordered_map<pal::string_t, std::vector<pal::string_t>>* opts,
int* num_args);
bool skip_utf8_bom(pal::ifstream_t* stream);
#endif