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). ...@@ -5,6 +5,23 @@ is based on [Keep a Changelog](https://keepachangelog.com).
## [Unreleased] ## [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 ### Fixed
- For types that offer implicit type conversion, trying to construct a - For types that offer implicit type conversion, trying to construct a
......
...@@ -94,10 +94,13 @@ public: ...@@ -94,10 +94,13 @@ public:
/// configuration file. /// configuration file.
error parse(string_list args, std::istream& config); error parse(string_list args, std::istream& config);
/// Parses `args` as tuple of strings containing CLI options and tries to open /// Parses `args` as CLI options and tries locate a config file via
/// `config_file_cstr` as config file. The parsers tries to open /// `config_file_path` and `config_file_path_alternative` unless the user
/// `caf-application.conf` if `config_file_cstr` is `nullptr`. /// provides a config file path on the command line.
error parse(string_list args, const char* config_file_cstr = nullptr); 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. /// Parses the CLI options `{argc, argv}` and `config` as configuration file.
error parse(int argc, char** argv, std::istream& config); error parse(int argc, char** argv, std::istream& config);
...@@ -105,7 +108,10 @@ public: ...@@ -105,7 +108,10 @@ public:
/// Parses the CLI options `{argc, argv}` and tries to open `config_file_cstr` /// 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 /// as config file. The parsers tries to open `caf-application.conf` if
/// `config_file_cstr` is `nullptr`. /// `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` /// Allows other nodes to spawn actors created by `fun`
/// dynamically by using `name` as identifier. /// dynamically by using `name` as identifier.
...@@ -235,10 +241,13 @@ public: ...@@ -235,10 +241,13 @@ public:
// -- parsing parameters ----------------------------------------------------- // -- parsing parameters -----------------------------------------------------
/// Configures the file path for the config file, `caf-application.conf` per /// Configures a path for the default configuration file.
/// default.
std::string config_file_path; 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 ---------------------------------------------------- // -- utility for caf-run ----------------------------------------------------
int (*slave_mode_fun)(actor_system&, const actor_system_config&); int (*slave_mode_fun)(actor_system&, const actor_system_config&);
...@@ -321,7 +330,7 @@ private: ...@@ -321,7 +330,7 @@ private:
actor_system_config& set_impl(string_view name, config_value value); 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. /// Returns all user-provided configuration parameters.
......
...@@ -55,8 +55,8 @@ void exec_main_load_module(actor_system_config& cfg) { ...@@ -55,8 +55,8 @@ void exec_main_load_module(actor_system_config& cfg) {
} }
template <class... Ts, class F = void (*)(actor_system&)> template <class... Ts, class F = void (*)(actor_system&)>
int exec_main(F fun, int argc, char** argv, [[deprecated("override config_file_path in the config class instead")]] int
const char* config_file_name = "caf-application.conf") { exec_main(F fun, int argc, char** argv, const char* config_file_name) {
using trait = typename detail::get_callable_trait<F>::type; using trait = typename detail::get_callable_trait<F>::type;
using arg_types = typename trait::arg_types; using arg_types = typename trait::arg_types;
static_assert(detail::tl_size<arg_types>::value == 1 static_assert(detail::tl_size<arg_types>::value == 1
...@@ -77,11 +77,13 @@ int exec_main(F fun, int argc, char** argv, ...@@ -77,11 +77,13 @@ int exec_main(F fun, int argc, char** argv,
using helper = exec_main_helper<typename trait::arg_types>; using helper = exec_main_helper<typename trait::arg_types>;
// Pass CLI options to config. // Pass CLI options to config.
typename helper::config cfg; typename helper::config cfg;
CAF_PUSH_DEPRECATED_WARNING
if (auto err = cfg.parse(argc, argv, config_file_name)) { if (auto err = cfg.parse(argc, argv, config_file_name)) {
std::cerr << "error while parsing CLI and file options: " << to_string(err) std::cerr << "error while parsing CLI and file options: " << to_string(err)
<< std::endl; << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
CAF_POP_WARNINGS
// Return immediately if a help text was printed. // Return immediately if a help text was printed.
if (cfg.cli_helptext_printed) if (cfg.cli_helptext_printed)
return EXIT_SUCCESS; return EXIT_SUCCESS;
...@@ -106,6 +108,13 @@ int exec_main(F fun, int argc, char** argv, ...@@ -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 } // namespace caf
#define CAF_MAIN(...) \ #define CAF_MAIN(...) \
......
...@@ -26,7 +26,7 @@ namespace caf { ...@@ -26,7 +26,7 @@ namespace caf {
namespace { namespace {
constexpr const char* default_config_file = "$DEFAULT"; constexpr const char* default_config_file = "caf-application.conf";
} // namespace } // namespace
...@@ -53,8 +53,7 @@ actor_system_config::actor_system_config() ...@@ -53,8 +53,7 @@ actor_system_config::actor_system_config()
.add<bool>("help,h?", "print help text to STDERR and exit") .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>("long-help", "print long help text to STDERR and exit")
.add<bool>("dump-config", "print configuration to STDERR and exit") .add<bool>("dump-config", "print configuration to STDERR and exit")
.add<string>(config_file_path, "config-file", .add<string>("config-file", "sets a path to a configuration file");
"set config file (default: caf-application.conf)");
opt_group{custom_options_, "caf.stream"} opt_group{custom_options_, "caf.stream"}
.add<timespan>(stream_max_batch_delay, "max-batch-delay", .add<timespan>(stream_max_batch_delay, "max-batch-delay",
"maximum delay for partial batches") "maximum delay for partial batches")
...@@ -174,15 +173,31 @@ settings actor_system_config::dump_content() const { ...@@ -174,15 +173,31 @@ settings actor_system_config::dump_content() const {
return result; return result;
} }
error actor_system_config::parse(int argc, char** argv, error actor_system_config::parse(int argc, char** argv) {
const char* config_file_cstr) {
string_list args; string_list args;
if (argc > 0) { if (argc > 0) {
program_name = argv[0]; program_name = argv[0];
if (argc > 1) if (argc > 1)
args.assign(argv + 1, argv + argc); 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) { 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) { ...@@ -332,20 +347,50 @@ error actor_system_config::parse(string_list args, std::istream& config) {
return none; 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, error actor_system_config::parse(string_list args,
const char* config_file_cstr) { const char* config_file_cstr) {
// Override default config file name if set by user. if (config_file_cstr == nullptr) {
if (config_file_cstr != nullptr) return parse(std::move(args));
config_file_path = config_file_cstr; } else if (auto&& [err, path] = extract_config_file_path(args); !err) {
// CLI arguments always win. std::ifstream conf;
if (auto err = extract_config_file_path(args)) if (!path.empty()) {
return err; conf.open(path);
if (config_file_path == "$DEFAULT") { } else {
std::ifstream conf{"caf-application.conf"}; conf.open(config_file_cstr);
if (conf.is_open())
set("global.config-file", config_file_cstr);
}
return parse(std::move(args), conf); 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, 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, ...@@ -419,25 +464,28 @@ error actor_system_config::parse_config(std::istream& source,
return none; 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"); auto ptr = custom_options_.qualified_name_lookup("global.config-file");
CAF_ASSERT(ptr != nullptr); CAF_ASSERT(ptr != nullptr);
string_list::iterator i; string_list::iterator i;
string_view path; string_view path;
std::tie(i, path) = find_by_long_name(*ptr, args.begin(), args.end()); std::tie(i, path) = find_by_long_name(*ptr, args.begin(), args.end());
if (i == args.end()) if (i == args.end()) {
return none; return {none, std::string{}};
if (path.empty()) { } else if (path.empty()) {
auto str = std::move(*i); return {make_error(pec::missing_argument, "no argument to --config-file"),
args.erase(i); std::string{}};
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;
} else { } 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