36#include <boost/algorithm/string.hpp>
37#include <boost/any.hpp>
38#include <boost/filesystem/operations.hpp>
39#include <boost/filesystem/path.hpp>
40#include <boost/program_options.hpp>
41#include <boost/smart_ptr.hpp>
55using log4cpp::Priority;
71 const string& parent_project_name,
const string& parent_project_vcs_version,
72 const string& parent_module_version,
const string& parent_module_name,
73 const vector<string>& search_dirs,
const Priority::Value& elements_loglevel,
100 Path::Item default_config_file{};
103 Path::Item conf_name(program_name);
104 conf_name.replace_extension(
"conf");
108 if (default_config_file.empty()) {
109 log.warn() <<
"The " << conf_name <<
" default configuration file cannot be found in:";
111 log.warn() <<
" " << loc;
113 if (not module_name.
empty()) {
114 conf_name = Path::Item{module_name} / conf_name;
115 log.warn() <<
"Trying " << conf_name <<
".";
120 if (default_config_file.empty()) {
121 log.debug() <<
"Couldn't find " << conf_name <<
" default configuration file.";
123 log.debug() <<
"Found " << conf_name <<
" default configuration file at " << default_config_file;
126 return default_config_file;
133 return full_path.filename();
140 return full_path.parent_path();
151 using boost::program_options::collect_unrecognized;
152 using boost::program_options::command_line_parser;
153 using boost::program_options::include_positional;
154 using boost::program_options::notify;
155 using boost::program_options::parse_config_file;
156 using boost::program_options::store;
157 using boost::program_options::value;
160 Path::Item config_file;
163 string default_log_level =
"INFO";
167 cmd_only_generic_options.add_options()(
"version",
"Print version string")(
"help",
"Produce help message");
172 cmd_only_generic_options.add_options()(
"config-file", value<Path::Item>()->default_value(default_config_file),
173 "Name of a configuration file");
178 cmd_and_file_generic_options.add_options()(
"log-level", value<string>()->default_value(default_log_level),
179 "Log level: FATAL, ERROR, WARN, INFO (default), DEBUG")(
180 "log-file", value<Path::Item>(),
"Name of a log file");
185 for (
auto o : cmd_only_generic_options.options()) {
186 all_generic_options.add(o);
188 for (
auto o : cmd_and_file_generic_options.options()) {
189 all_generic_options.add(o);
194 auto specific_options =
m_program_ptr->defineSpecificProgramOptions();
195 auto program_arguments =
m_program_ptr->defineProgramArguments();
197 all_specific_options.add(specific_options).add(program_arguments.first);
201 all_cmd_and_file_options.add(cmd_and_file_generic_options).add(all_specific_options);
205 help_options.add(all_generic_options).add(all_specific_options);
208 auto cmd_parsed_options =
209 command_line_parser(argc, argv).options(cmd_only_generic_options).allow_unregistered().run();
213 store(cmd_parsed_options, var_map);
216 if (var_map.count(
"help") > 0) {
217 cout << help_options <<
endl;
222 if (var_map.count(
"version") > 0) {
230 config_file = var_map.at(
"config-file").as<Path::Item>();
235 auto leftover_cmd_options = collect_unrecognized(cmd_parsed_options.options, include_positional);
239 auto parsed_cmdline_options = command_line_parser(leftover_cmd_options)
240 .options(all_cmd_and_file_options)
241 .positional(program_arguments.second)
244 store(parsed_cmdline_options, var_map);
248 if (not config_file.empty() and boost::filesystem::exists(config_file)) {
251 auto parsed_cfgfile_options = parse_config_file(ifs, all_cmd_and_file_options);
252 store(parsed_cfgfile_options, var_map);
258 if (boost::starts_with(e.what(),
"unrecognised option") or
259 boost::starts_with(e.what(),
"too many positional options")) {
274 log.log(
m_elements_loglevel,
"##########################################################");
275 log.log(
m_elements_loglevel,
"##########################################################");
284 log.log(
m_elements_loglevel,
"##########################################################");
288 log.log(
m_elements_loglevel,
"##########################################################");
289 log.log(
m_elements_loglevel,
"##########################################################");
298 log.log(
m_elements_loglevel,
"##########################################################");
310 if (v.second.value().type() ==
typeid(
string)) {
311 log_message << v.first <<
" = " << v.second.as<
string>();
313 }
else if (v.second.value().type() ==
typeid(
double)) {
314 log_message << v.first <<
" = " << v.second.as<
double>();
316 }
else if (v.second.value().type() ==
typeid(
int64_t)) {
317 log_message << v.first <<
" = " << v.second.as<
int64_t>();
319 }
else if (v.second.value().type() ==
typeid(
int)) {
320 log_message << v.first <<
" = " << v.second.as<
int>();
322 }
else if (v.second.value().type() ==
typeid(
bool)) {
323 log_message << v.first <<
" = " << v.second.as<
bool>();
325 }
else if (v.second.value().type() ==
typeid(Path::Item)) {
326 log_message << v.first <<
" = " << v.second.as<Path::Item>();
328 }
else if (v.second.value().type() ==
typeid(
vector<int>)) {
331 for (
const auto& i : intVec) {
332 vecContent <<
" " << i;
334 log_message << v.first <<
" = {" << vecContent.str() <<
" }";
339 for (
const auto& i : intVec) {
340 vecContent <<
" " << i;
342 log_message << v.first <<
" = {" << vecContent.str() <<
" }";
347 for (
const auto& i : intVec) {
348 vecContent <<
" " << i;
350 log_message << v.first <<
" = {" << vecContent.str() <<
" }";
353 log_message <<
"Option " << v.first <<
" of type " << v.second.value().type().name()
354 <<
" not supported in logging !" <<
endl;
366 log.debug() <<
"##########################################################";
368 log.debug() <<
"# Environment of the Run";
369 log.debug() <<
"# ---------------------------";
372 for (
const auto& v : Path::VARIABLE) {
373 log.debug() << v.second <<
": " <<
m_env[v.second];
387 return boost::filesystem::absolute(s);
392 const Path::Item this_parent_path = boost::filesystem::canonical(
m_program_path.parent_path());
393 if (local_search_paths[0] != this_parent_path) {
394 auto b = local_search_paths.
begin();
395 local_search_paths.
insert(b, this_parent_path);
398 using Path::joinPath;
399 using Path::multiPathAppend;
401 for (
const auto& v : Path::VARIABLE) {
402 if (
m_env[v.second].exists()) {
403 m_env[v.second] += Path::PATH_SEP + joinPath(multiPathAppend(local_search_paths, Path::SUFFIXES.at(v.first)));
405 m_env[v.second] = joinPath(multiPathAppend(local_search_paths, Path::SUFFIXES.at(v.first)));
421 auto exit_code = e.exitCode();
422 log.fatal() <<
"# Elements Exception : " << e.what();
427 string logging_level;
433 Path::Item log_file_name;
451 log.debug() <<
"# Exit Code: " << int(c);
483 log.fatal() <<
"Crash detected";
484 log.fatal() <<
"This is the back trace:";
486 log.fatal() << level;
494 log.fatal() <<
"# Elements Exception : " << exc1.
what();
501 log.fatal() <<
"# Standard Exception : " << exc2.
what();
505 log.fatal() <<
"# An exception of unknown type occurred, "
506 <<
"i.e., an exception not deriving from std::exception ";
provide functions to retrieve configuration files
defines the base Elements exception class
define a list of standard exit codes for executables
OS specific details to access at run-time the module configuration of the process.
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
std::string m_parent_project_version
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.
std::string m_parent_module_version
Path::Item m_program_path
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.
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, bool no_config_file=false)
Constructor.
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
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.
ELEMENTS_API Path::Item getExecutablePath()
Get the full executable path.
Program::VariablesMap VariablesMap
Program::OptionsDescription OptionsDescription
T rethrow_exception(T... args)