35#include <boost/algorithm/string/predicate.hpp>
36#include <boost/filesystem/operations.hpp>
37#include <boost/program_options.hpp>
52using log4cpp::Priority;
69 const string& parent_project_name,
const string& parent_project_vcs_version,
70 const string& parent_module_version,
const string& parent_module_name,
71 const vector<string>& search_dirs,
const Priority::Value& elements_loglevel)
72 : m_program_ptr(move(program_ptr))
73 , m_parent_project_version(move(parent_project_version))
74 , m_parent_project_name(move(parent_project_name))
75 , m_parent_project_vcs_version(move(parent_project_vcs_version))
76 , m_parent_module_version(move(parent_module_version))
77 , m_parent_module_name(move(parent_module_name))
78 , m_search_dirs(move(search_dirs))
80 , m_elements_loglevel(move(elements_loglevel)) {}
96 Path::Item default_config_file{};
99 Path::Item conf_name(program_name);
100 conf_name.replace_extension(
"conf");
104 if (default_config_file.empty()) {
105 log.warn() <<
"The " << conf_name <<
" default configuration file cannot be found in:";
107 log.warn() <<
" " << loc;
109 if (not module_name.
empty()) {
110 conf_name = Path::Item{module_name} / conf_name;
111 log.warn() <<
"Trying " << conf_name <<
".";
116 if (default_config_file.empty()) {
117 log.debug() <<
"Couldn't find " << conf_name <<
" default configuration file.";
119 log.debug() <<
"Found " << conf_name <<
" default configuration file at " << default_config_file;
122 return default_config_file;
127 Path::Item full_path = getExecutablePath();
129 return full_path.filename();
134 Path::Item full_path = getExecutablePath();
136 return full_path.parent_path();
139template <
class charT>
141 const boost::program_options::basic_parsed_options<charT>& cmd_parsed_options) {
143 for (
const auto& o : cmd_parsed_options.options) {
144 if (o.string_key ==
"config-file") {
145 if (o.value.size() != 1) {
146 cerr <<
"Wrong usage of the --config-file option" << endl;
149 auto conf_file = Path::Item{o.value[0]};
150 if (not boost::filesystem::exists(conf_file)) {
151 cerr <<
"The " << conf_file <<
" configuration file doesn't exist!" << endl;
167 using boost::program_options::collect_unrecognized;
168 using boost::program_options::command_line_parser;
169 using boost::program_options::include_positional;
170 using boost::program_options::notify;
171 using boost::program_options::parse_config_file;
172 using boost::program_options::store;
173 using boost::program_options::value;
178 string default_log_level =
"INFO";
185 cmd_only_generic_options.add_options()(
"version",
"Print version string")(
"help",
"Produce help message")(
186 "config-file", value<Path::Item>()->default_value(default_config_file),
"Name of a configuration file");
190 cmd_and_file_generic_options.add_options()(
"log-level", value<string>()->default_value(default_log_level),
191 "Log level: FATAL, ERROR, WARN, INFO (default), DEBUG")(
192 "log-file", value<Path::Item>(),
"Name of a log file");
197 for (
auto o : cmd_only_generic_options.options()) {
198 all_generic_options.add(o);
200 for (
auto o : cmd_and_file_generic_options.options()) {
201 all_generic_options.add(o);
206 auto specific_options =
m_program_ptr->defineSpecificProgramOptions();
207 auto program_arguments =
m_program_ptr->defineProgramArguments();
209 all_specific_options.add(specific_options).add(program_arguments.first);
213 all_cmd_and_file_options.add(cmd_and_file_generic_options).add(all_specific_options);
217 help_options.add(all_generic_options).add(all_specific_options);
220 auto cmd_parsed_options =
221 command_line_parser(argc, argv).options(cmd_only_generic_options).allow_unregistered().run();
225 store(cmd_parsed_options, var_map);
228 if (var_map.count(
"help") > 0) {
229 cout << help_options << endl;
234 if (var_map.count(
"version") > 0) {
241 auto config_file = var_map.at(
"config-file").as<Path::Item>();
245 auto leftover_cmd_options = collect_unrecognized(cmd_parsed_options.options, include_positional);
249 auto parsed_cmdline_options = command_line_parser(leftover_cmd_options)
250 .options(all_cmd_and_file_options)
251 .positional(program_arguments.second)
254 store(parsed_cmdline_options, var_map);
257 if (not config_file.empty() and boost::filesystem::exists(config_file)) {
260 auto parsed_cfgfile_options = parse_config_file(ifs, all_cmd_and_file_options);
261 store(parsed_cfgfile_options, var_map);
266 if (boost::starts_with(e.what(),
"unrecognised option") or
267 boost::starts_with(e.what(),
"too many positional options")) {
282 log.log(
m_elements_loglevel,
"##########################################################");
283 log.log(
m_elements_loglevel,
"##########################################################");
292 log.log(
m_elements_loglevel,
"##########################################################");
296 log.log(
m_elements_loglevel,
"##########################################################");
297 log.log(
m_elements_loglevel,
"##########################################################");
306 log.log(
m_elements_loglevel,
"##########################################################");
313 stringstream log_message{};
318 if (v.second.value().type() ==
typeid(
string)) {
319 log_message << v.first <<
" = " << v.second.as<
string>();
321 }
else if (v.second.value().type() ==
typeid(double)) {
322 log_message << v.first <<
" = " << v.second.as<
double>();
324 }
else if (v.second.value().type() ==
typeid(int64_t)) {
325 log_message << v.first <<
" = " << v.second.as<int64_t>();
327 }
else if (v.second.value().type() ==
typeid(int)) {
328 log_message << v.first <<
" = " << v.second.as<
int>();
330 }
else if (v.second.value().type() ==
typeid(bool)) {
331 log_message << v.first <<
" = " << v.second.as<
bool>();
333 }
else if (v.second.value().type() ==
typeid(Path::Item)) {
334 log_message << v.first <<
" = " << v.second.as<Path::Item>();
336 }
else if (v.second.value().type() ==
typeid(
vector<int>)) {
338 stringstream vecContent{};
339 for (
const auto& i : intVec) {
340 vecContent <<
" " << i;
342 log_message << v.first <<
" = {" << vecContent.str() <<
" }";
346 stringstream vecContent{};
347 for (
const auto& i : intVec) {
348 vecContent <<
" " << i;
350 log_message << v.first <<
" = {" << vecContent.str() <<
" }";
354 stringstream vecContent{};
355 for (
const auto& i : intVec) {
356 vecContent <<
" " << i;
358 log_message << v.first <<
" = {" << vecContent.str() <<
" }";
361 log_message <<
"Option " << v.first <<
" of type " << v.second.value().type().name()
362 <<
" not supported in logging !" << endl;
374 log.debug() <<
"##########################################################";
376 log.debug() <<
"# Environment of the Run";
377 log.debug() <<
"# ---------------------------";
380 for (
const auto& v : Path::VARIABLE) {
381 log.debug() << v.second <<
": " <<
m_env[v.second];
395 return boost::filesystem::complete(s);
400 const Path::Item this_parent_path = boost::filesystem::canonical(
m_program_path.parent_path());
401 if (local_search_paths[0] != this_parent_path) {
402 auto b = local_search_paths.
begin();
403 local_search_paths.
insert(b, this_parent_path);
406 using Path::joinPath;
407 using Path::multiPathAppend;
409 for (
const auto& v : Path::VARIABLE) {
410 if (
m_env[v.second].exists()) {
411 m_env[v.second] += Path::PATH_SEP + joinPath(multiPathAppend(local_search_paths, Path::SUFFIXES.at(v.first)));
413 m_env[v.second] = joinPath(multiPathAppend(local_search_paths, Path::SUFFIXES.at(v.first)));
429 auto exit_code = e.exitCode();
430 log.fatal() <<
"# Elements Exception : " << e.what();
435 string logging_level;
441 Path::Item log_file_name;
459 log.debug() <<
"# Exit Code: " << int(c);
491 log.fatal() <<
"Crash detected";
492 log.fatal() <<
"This is the back trace:";
494 log.fatal() << level;
502 log.fatal() <<
"# Elements Exception : " << exc1.
what();
509 log.fatal() <<
"# Standard Exception : " << exc2.
what();
513 log.fatal() <<
"# An exception of unknown type occurred, "
514 <<
"i.e., an exception not deriving from std::exception ";
provide functions to retrieve configuration files
OS specific details to access at run-time the module configuration of the process.
defines the base Elements exception class
define a list of standard exit codes for executables
define an exception for unrecognized commandline options and arguments
provide functions to retrieve resources pointed by environment variables
define an abstract class for all Elements program
This file is intended to iron out all the differences between systems (currently Linux and MacOSX)
Macro to silence unused variables warnings from the compiler.
Elements base exception class.
ExitCode exitCode() const noexcept
const char * what() const noexcept override
static Logging getLogger(const std::string &name="")
static void setLogFile(const Path::Item &fileName)
Sets the file to store the log messages.
static void setLevel(std::string level)
Sets the global message level.
void setup(int argc, char *argv[])
Program setup taking care of command line options and logging initialization.
Path::Item m_program_name
virtual ~ProgramManager()
Destructor.
std::unique_ptr< Program > m_program_ptr
std::string m_parent_module_name
static void onTerminate() noexcept
This is the set_terminate handler that is used in the MAIN_FOR macro.
const Path::Item & getProgramName() const
Getter.
ExitCode run(int argc, char *argv[])
This is the public entry point, i.e., the only method called from the main.
void bootstrapEnvironment(char *arg0)
Bootstrap the Environment from the executable location and the install path computed at install time.
static const Path::Item getDefaultConfigFile(const Path::Item &program_name, const std::string &module_name="")
Get a default configuration file name and path, to be used if not provided as a command line option.
void logTheEnvironment() const
Log the program environment.
std::string m_parent_project_name
std::vector< std::string > m_search_dirs
void logHeader(std::string program_name) const
Log Header.
void checkCommandLineOptions(const boost::program_options::basic_parsed_options< charT > &cmd_line_options)
check the explicit command line arguments. For the moment, it only checks if the configuration file b...
const Program::VariablesMap getProgramOptions(int argc, char *argv[])
Get the program options from the command line into thevariables_map.
static const Path::Item setProgramName(char *arg0)
Strip the path from argv[0] to set the program name.
Path::Item m_program_path
ProgramManager(std::unique_ptr< Program > program_ptr, const std::string &parent_project_version="", const std::string &parent_project_name="", const std::string &parent_project_vcs_version="", const std::string &parent_module_version="", const std::string &parent_module_name="", const std::vector< std::string > &search_dirs={}, const log4cpp::Priority::Value &elements_loglevel=log4cpp::Priority::DEBUG)
Constructor.
std::string getVersion() const
This function returns the version of the program computed at compile time. This is the same as the pr...
log4cpp::Priority::Value m_elements_loglevel
std::string m_parent_project_vcs_version
const Path::Item & getProgramPath() const
Getter.
void tearDown(const ExitCode &)
void logFooter(std::string program_name) const
Log Footer.
Program::VariablesMap m_variables_map
static const Path::Item setProgramPath(char *arg0)
Strip the name from argv[0] to set the program path.
void logAllOptions() const
Log all program options.
options_description OptionsDescription
variables_map VariablesMap
T current_exception(T... args)
ExitCode
Strongly typed exit numbers.
@ NOT_OK
Generic unknown failure.
@ CONFIG
configuration error
@ USAGE
command line usage error
ELEMENTS_API Path::Item getConfigurationPath(const T &file_name, bool raise_exception=true)
ELEMENTS_API std::vector< Path::Item > getConfigurationLocations(bool exist_only=false)
ELEMENTS_API int backTrace(ELEMENTS_UNUSED std::shared_ptr< void * > addresses, ELEMENTS_UNUSED const int depth)
ELEMENTS_API Path::Item getExecutablePath()
Get the full executable path.
Program::OptionsDescription OptionsDescription
Program::VariablesMap VariablesMap
T rethrow_exception(T... args)