Commit 9c4b539e authored by Dominik Charousset's avatar Dominik Charousset

Enable alternative paths for locating config files

parent 17316327
......@@ -5,6 +5,23 @@ is based on [Keep a Changelog](https://keepachangelog.com).
## [Unreleased]
### Added
- The `actor_system_config` now has an additional member called
`config_file_path_alternatives`. With this, users can configure fallback paths
for locating a configuration file. For example, an application `my-app` on a
UNIX-like system could set `config_file_path` to `my-app.conf` and then add
`/etc/my-app.conf` to `config_file_path_alternatives` in order to follow the
common practice of looking into the current directory first before looking for
a system-wide configuration file.
### Deprecated
- All `parse` function overloads in `actor_system_config` that took a custom
configuration file path as argument were deprecated in favor of consistently
asking users to use the `config_file_path` and `config_file_path_alternatives`
member variables instead
### Fixed
- For types that offer implicit type conversion, trying to construct a
......
......@@ -94,10 +94,13 @@ public:
/// configuration file.
error parse(string_list args, std::istream& config);
/// Parses `args` as tuple of strings containing CLI options and tries to open
/// `config_file_cstr` as config file. The parsers tries to open
/// `caf-application.conf` if `config_file_cstr` is `nullptr`.
error parse(string_list args, const char* config_file_cstr = nullptr);
/// Parses `args` as CLI options and tries locate a config file via
/// `config_file_path` and `config_file_path_alternative` unless the user
/// provides a config file path on the command line.
error parse(string_list args);
[[deprecated("set the config_file_path member instead")]] error
parse(string_list args, const char* config_file_cstr);
/// Parses the CLI options `{argc, argv}` and `config` as configuration file.
error parse(int argc, char** argv, std::istream& config);
......@@ -105,7 +108,10 @@ public:
/// Parses the CLI options `{argc, argv}` and tries to open `config_file_cstr`
/// as config file. The parsers tries to open `caf-application.conf` if
/// `config_file_cstr` is `nullptr`.
error parse(int argc, char** argv, const char* config_file_cstr = nullptr);
error parse(int argc, char** argv);
[[deprecated("set the config_file_path member instead")]] error
parse(int argc, char** argv, const char* config_file_cstr);
/// Allows other nodes to spawn actors created by `fun`
/// dynamically by using `name` as identifier.
......@@ -235,10 +241,13 @@ public:
// -- parsing parameters -----------------------------------------------------
/// Configures the file path for the config file, `caf-application.conf` per
/// default.
/// Configures a path for the default configuration file.
std::string config_file_path;
/// Configures alternative paths for locating a config file when unable to
/// open the default `config_file_path`.
std::vector<std::string> config_file_path_alternatives;
// -- utility for caf-run ----------------------------------------------------
int (*slave_mode_fun)(actor_system&, const actor_system_config&);
......@@ -321,7 +330,7 @@ private:
actor_system_config& set_impl(string_view name, config_value value);
error extract_config_file_path(string_list& args);
std::pair<error, std::string> extract_config_file_path(string_list& args);
};
/// Returns all user-provided configuration parameters.
......
......@@ -55,8 +55,8 @@ void exec_main_load_module(actor_system_config& cfg) {
}
template <class... Ts, class F = void (*)(actor_system&)>
int exec_main(F fun, int argc, char** argv,
const char* config_file_name = "caf-application.conf") {
[[deprecated("override config_file_path in the config class instead")]] int
exec_main(F fun, int argc, char** argv, const char* config_file_name) {
using trait = typename detail::get_callable_trait<F>::type;
using arg_types = typename trait::arg_types;
static_assert(detail::tl_size<arg_types>::value == 1
......@@ -77,11 +77,13 @@ int exec_main(F fun, int argc, char** argv,
using helper = exec_main_helper<typename trait::arg_types>;
// Pass CLI options to config.
typename helper::config cfg;
CAF_PUSH_DEPRECATED_WARNING
if (auto err = cfg.parse(argc, argv, config_file_name)) {
std::cerr << "error while parsing CLI and file options: " << to_string(err)
<< std::endl;
return EXIT_FAILURE;
}
CAF_POP_WARNINGS
// Return immediately if a help text was printed.
if (cfg.cli_helptext_printed)
return EXIT_SUCCESS;
......@@ -106,6 +108,13 @@ int exec_main(F fun, int argc, char** argv,
}
}
template <class... Ts, class F = void (*)(actor_system&)>
int exec_main(F fun, int argc, char** argv) {
CAF_PUSH_DEPRECATED_WARNING
return exec_main<Ts...>(std::move(fun), argc, argv, nullptr);
CAF_POP_WARNINGS
}
} // namespace caf
#define CAF_MAIN(...) \
......
......@@ -26,7 +26,7 @@ namespace caf {
namespace {
constexpr const char* default_config_file = "$DEFAULT";
constexpr const char* default_config_file = "caf-application.conf";
} // namespace
......@@ -53,8 +53,7 @@ actor_system_config::actor_system_config()
.add<bool>("help,h?", "print help text to STDERR and exit")
.add<bool>("long-help", "print long help text to STDERR and exit")
.add<bool>("dump-config", "print configuration to STDERR and exit")
.add<string>(config_file_path, "config-file",
"set config file (default: caf-application.conf)");
.add<string>("config-file", "sets a path to a configuration file");
opt_group{custom_options_, "caf.stream"}
.add<timespan>(stream_max_batch_delay, "max-batch-delay",
"maximum delay for partial batches")
......@@ -174,15 +173,31 @@ settings actor_system_config::dump_content() const {
return result;
}
error actor_system_config::parse(int argc, char** argv,
const char* config_file_cstr) {
error actor_system_config::parse(int argc, char** argv) {
string_list args;
if (argc > 0) {
program_name = argv[0];
if (argc > 1)
args.assign(argv + 1, argv + argc);
}
return parse(std::move(args), config_file_cstr);
return parse(std::move(args));
}
error actor_system_config::parse(int argc, char** argv,
const char* config_file_cstr) {
if (config_file_cstr == nullptr) {
return parse(argc, argv);
} else {
string_list args;
if (argc > 0) {
program_name = argv[0];
if (argc > 1)
args.assign(argv + 1, argv + argc);
}
CAF_PUSH_DEPRECATED_WARNING
return parse(std::move(args), config_file_cstr);
CAF_POP_WARNINGS
}
}
error actor_system_config::parse(int argc, char** argv, std::istream& conf) {
......@@ -332,20 +347,50 @@ error actor_system_config::parse(string_list args, std::istream& config) {
return none;
}
error actor_system_config::parse(string_list args) {
if (auto&& [err, path] = extract_config_file_path(args); !err) {
std::ifstream conf;
// No error but an empty path means --config-file=ARG was missing.
if (!path.empty()) {
conf.open(path);
} else {
// Try config_file_path and if that fails try the alternative paths.
auto try_open = [this, &conf](const auto& what) {
conf.open(what);
if (conf.is_open()) {
set("global.config-file", what);
return true;
} else {
return false;
}
};
try_open(config_file_path)
|| std::any_of(config_file_path_alternatives.begin(),
config_file_path_alternatives.end(), try_open);
}
return parse(std::move(args), conf);
} else {
return err;
}
}
error actor_system_config::parse(string_list args,
const char* config_file_cstr) {
// Override default config file name if set by user.
if (config_file_cstr != nullptr)
config_file_path = config_file_cstr;
// CLI arguments always win.
if (auto err = extract_config_file_path(args))
return err;
if (config_file_path == "$DEFAULT") {
std::ifstream conf{"caf-application.conf"};
if (config_file_cstr == nullptr) {
return parse(std::move(args));
} else if (auto&& [err, path] = extract_config_file_path(args); !err) {
std::ifstream conf;
if (!path.empty()) {
conf.open(path);
} else {
conf.open(config_file_cstr);
if (conf.is_open())
set("global.config-file", config_file_cstr);
}
return parse(std::move(args), conf);
} else {
return err;
}
std::ifstream conf{config_file_path};
return parse(std::move(args), conf);
}
actor_system_config& actor_system_config::add_actor_factory(std::string name,
......@@ -419,25 +464,28 @@ error actor_system_config::parse_config(std::istream& source,
return none;
}
error actor_system_config::extract_config_file_path(string_list& args) {
std::pair<error, std::string>
actor_system_config::extract_config_file_path(string_list& args) {
auto ptr = custom_options_.qualified_name_lookup("global.config-file");
CAF_ASSERT(ptr != nullptr);
string_list::iterator i;
string_view path;
std::tie(i, path) = find_by_long_name(*ptr, args.begin(), args.end());
if (i == args.end())
return none;
if (path.empty()) {
auto str = std::move(*i);
args.erase(i);
return make_error(pec::missing_argument, std::move(str));
}
config_value val{path};
if (auto err = ptr->sync(val); !err) {
put(content, "config-file", std::move(val));
return none;
if (i == args.end()) {
return {none, std::string{}};
} else if (path.empty()) {
return {make_error(pec::missing_argument, "no argument to --config-file"),
std::string{}};
} else {
return err;
auto path_str = std::string{path.begin(), path.end()};
args.erase(i);
config_value val{path_str};
if (auto err = ptr->sync(val); !err) {
put(content, "config-file", std::move(val));
return {none, std::move(path_str)};
} else {
return {std::move(err), std::string{}};
}
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment