Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
024ec0d
Add support for required plugins at startup
nathanielhourt May 29, 2017
cc851b8
Merge commit '024ec0d'
nathanielhourt May 31, 2017
754b9d1
[IDE Support] Add headers to CMakeLists.txt
nathanielhourt May 31, 2017
22efe13
typo fix and two other small format fixes
Oct 8, 2017
a7e5190
typo fix
Oct 8, 2017
c6c5754
typo fix
Oct 8, 2017
57aff5b
Updated status messages output from CMake to reflect actual project name
mitza-oci Nov 10, 2017
fe10350
Version handling, required to resolve #664
jgiszczak Nov 13, 2017
7acd8fb
Merge pull request #1 from guanqun/patch-1
heifner Nov 14, 2017
d2774f5
Merge pull request #2 from guanqun/patch-2
heifner Nov 14, 2017
11e526a
Merge pull request #3 from guanqun/patch-3
heifner Nov 14, 2017
6471b90
Merge pull request #4 from mitza-oci/patch-1
heifner Nov 14, 2017
f938b9a
Merge pull request #5 from EOSIO/version-info
heifner Nov 14, 2017
bf9a9ff
Add integer version number as well.
jgiszczak Nov 14, 2017
55f2b6b
Merge pull request #6 from EOSIO/version-info
heifner Nov 14, 2017
0865e2c
explicitly destroying io_serv at the end of shutdown
wanderingbort Nov 30, 2017
476f58d
Merge pull request #7 from EOSIO/feature/explicit-io-serv-destruction
heifner Nov 30, 2017
552847c
Add logging configuration path and clean up version variables.
jgiszczak Nov 30, 2017
1ecc477
CLI documentation clarification
jgiszczak Nov 30, 2017
d4746ee
Merge pull request #8 from EOSIO/logging-conf
heifner Nov 30, 2017
9cf6f43
Remove semicolon disabling logconf option.
jgiszczak Nov 30, 2017
770392a
Default logging filename. Command line is parsed very late.
jgiszczak Nov 30, 2017
debdb63
Merge pull request #9 from EOSIO/logging-conf
jgiszczak Nov 30, 2017
3f5d060
Move default location of logging.json to data directory.
jgiszczak Dec 8, 2017
d8e2489
Merge pull request #10 from EOSIO/logging-conf-to-data-dir
jgiszczak Dec 8, 2017
bc8a43b
Remove shadowed variable breaking config filename customization.
jgiszczak Jan 10, 2018
9ce7d3d
Merge pull request #11 from EOSIO/fix-config-file-name
jgiszczak Jan 10, 2018
d9f7184
Error and exit on missing explicit config file. Resolves #1109.
jgiszczak Jan 18, 2018
a0cf75a
Merge pull request #12 from EOSIO/error-on-nonexistent-explicit-confi…
jgiszczak Jan 19, 2018
807cbab
Fix boost library link ordering
spoonincode Feb 14, 2018
ef2b0c8
Merge pull request #13 from EOSIO/boost_lib_ordering
b1bart Feb 14, 2018
93ec299
Add independent config directory and let users set default dirs.
jgiszczak Feb 27, 2018
26477ee
Merge pull request #14 from EOSIO/add-config-directory
heifner Feb 27, 2018
15ecfca
Add plugin per config option in config file descriptions
spoonincode Mar 5, 2018
de1d5e0
Merge pull request #15 from EOSIO/config_file_desc_with_plugin_name
heifner Mar 5, 2018
b28ce4c
Use CMake standard variables to install.
jgiszczak Mar 8, 2018
31a0229
Merge pull request #16 from EOSIO/install-paths-fix
heifner Mar 8, 2018
665e865
When a plugin throws during startup, cleanup
spoonincode Mar 29, 2018
3bfd8b5
Merge pull request #17 from EOSIO/cleanup_on_startup_throw
b1bart Mar 30, 2018
5761e03
Add option to print default configuration template
spoonincode Apr 2, 2018
b5ac92d
Merge pull request #18 from EOSIO/print-default-config
spoonincode Apr 2, 2018
e44c85f
Work around boost defect at intersection of program_options and files…
jgiszczak Apr 10, 2018
7f2b958
Merge pull request #19 from EOSIO/boost-defect-workaround-path-whites…
heifner Apr 10, 2018
039e231
initial support for channels and methods
b1bart Apr 18, 2018
b82f863
remove inheritance from signal, now composed. also fix bad throw
b1bart Apr 26, 2018
24cb742
added forwarding to the methods
b1bart Apr 26, 2018
f969e0f
fix typos
b1bart Apr 26, 2018
dd2245e
wrap handle for unsub
wanderingbort Apr 27, 2018
ca2e56c
delcare and delete constructors so that handles are moved instead of …
wanderingbort Apr 28, 2018
189094f
add documentation
b1bart Apr 30, 2018
5c51b3c
Merge pull request #20 from wanderingbort/feature/add-channels-and-me…
bytemaster Apr 30, 2018
9142e69
explicitly include typeindex headers
b1bart Apr 30, 2018
64da47b
Merge pull request #21 from wanderingbort/bugfix/explicit-typeindex-i…
b1bart Apr 30, 2018
25dc190
add a new dispatch method which calls the best provider by priority ONLY
b1bart May 4, 2018
50dc015
Merge pull request #22 from wanderingbort/feature/void-methods-and-fi…
b1bart May 4, 2018
e2235b0
Added try/catch for initializing plugins
larryk85 May 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,33 @@
project( AppBase )
cmake_minimum_required( VERSION 2.8.12 )

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")

include( InstallDirectoryPermissions )

file(GLOB HEADERS "include/appbase/*.hpp")

set(CMAKE_EXPORT_COMPILE_COMMANDS "ON")
SET(BOOST_COMPONENTS)
LIST(APPEND BOOST_COMPONENTS thread
set(BOOST_COMPONENTS)
list(APPEND BOOST_COMPONENTS thread
date_time
system
filesystem
system
chrono
program_options
unit_test_framework
locale)

FIND_PACKAGE(Boost 1.60 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
find_package(Boost 1.60 REQUIRED COMPONENTS ${BOOST_COMPONENTS})
set( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" )

if( APPLE )
# Apple Specific Options Here
message( STATUS "Configuring ChainBase on OS X" )
message( STATUS "Configuring AppBase on OS X" )
set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++14 -stdlib=libc++ -Wall -Wno-conversion -Wno-deprecated-declarations" )
else( APPLE )
# Linux Specific Options Here
message( STATUS "Configuring ChainBase on Linux" )
message( STATUS "Configuring AppBase on Linux" )
set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++14 -Wall" )
set( rt_library rt )
set( pthread_library pthread)
Expand All @@ -40,20 +44,26 @@ endif()

add_library( appbase
application.cpp
${HEADERS}
)

target_link_libraries( appbase ${Boost_LIBRARIES})

target_include_directories( appbase
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ${Boost_INCLUDE_DIR})

INSTALL( TARGETS
set_target_properties( appbase PROPERTIES PUBLIC_HEADER "${HEADERS}" )

set(CPACK_PACKAGING_INSTALL_PREFIX /)

install( TARGETS
appbase

RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/appbase
)
INSTALL( FILES ${HEADERS} DESTINATION "include/appbase" )
install_directory_permissions( DIRECTORY ${CMAKE_INSTALL_FULL_INCLUDEDIR}/appbase )

add_subdirectory( examples )
14 changes: 14 additions & 0 deletions CMakeModules/InstallDirectoryPermissions.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Fix directory permissions after installation of header files (primarily).
macro(install_directory_permissions)
cmake_parse_arguments(ARG "" "DIRECTORY" "" ${ARGN})
set(dir ${ARG_DIRECTORY})
install(DIRECTORY DESTINATION ${dir}
DIRECTORY_PERMISSIONS OWNER_READ
OWNER_WRITE
OWNER_EXECUTE
GROUP_READ
GROUP_EXECUTE
WORLD_READ
WORLD_EXECUTE
)
endmacro(install_directory_permissions)
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins are configured, initialized, started, and shutdown in the proper order.
## Key Features

- Dynamically Specify Plugins to Load
- Automaticly Load Dependent Plugins in Order
- Automatically Load Dependent Plugins in Order
- Plugins can specify commandline arguments and configuration file options
- Program gracefully exits from SIGINT and SIGTERM
- Minimal Dependencies (Boost 1.60, c++14)
Expand Down Expand Up @@ -87,7 +87,7 @@ exited cleanly
### Boost ASIO

AppBase maintains a singleton `application` instance which can be accessed via `appbase::app()`. This
application owns a `boost::asio::io_service` which starts running when appbase::exec() is called. If
application owns a `boost::asio::io_service` which starts running when `appbase::exec()` is called. If
a plugin needs to perform IO or other asynchronous operations then it should dispatch it via
`app().get_io_service().post( lambda )`.

Expand All @@ -105,6 +105,7 @@ To trigger a graceful exit call `appbase::app().quit()` or send SIGTERM or SIGIN

To compile boost with c++14 use:

./b2 ... cxxflags="-std=c++0x -stdlib=libc++" linkflags="-stdlib=libc++" ...

```
./b2 ... cxxflags="-std=c++0x -stdlib=libc++" linkflags="-stdlib=libc++" ...
```

157 changes: 124 additions & 33 deletions application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ class application_impl {
options_description _app_options;
options_description _cfg_options;

bfs::path _data_dir;
bfs::path _data_dir{"data-dir"};
bfs::path _config_dir{"config-dir"};
bfs::path _logging_conf{"logging.json"};

uint64_t _version;
};

application::application()
Expand All @@ -32,6 +36,36 @@ application::application()

application::~application() { }

void application::set_version(uint64_t version) {
my->_version = version;
}

uint64_t application::version() const {
return my->_version;
}

void application::set_default_data_dir(const bfs::path& data_dir) {
my->_data_dir = data_dir;
}

void application::set_default_config_dir(const bfs::path& config_dir) {
my->_config_dir = config_dir;
}

bfs::path application::get_logging_conf() const {
return my->_logging_conf;
}

void application::startup() {
try {
for (auto plugin : initialized_plugins)
plugin->startup();
} catch(...) {
shutdown();
throw;
}
}

application& application::instance() {
static application _app;
return _app;
Expand Down Expand Up @@ -61,44 +95,75 @@ void application::set_program_options()
app_cli_opts.add_options()
("help,h", "Print this help message and exit.")
("version,v", "Print version information.")
("data-dir,d", bpo::value<bfs::path>()->default_value( "data-dir" ), "Directory containing configuration file config.ini")
("config,c", bpo::value<bfs::path>()->default_value( "config.ini" ), "Configuration file name relative to data-dir");
("print-default-config", "Print default configuration template")
("data-dir,d", bpo::value<std::string>(), "Directory containing program runtime data")
("config-dir", bpo::value<std::string>(), "Directory containing configuration files such as config.ini")
("config,c", bpo::value<std::string>()->default_value( "config.ini" ), "Configuration file name relative to config-dir")
("logconf,l", bpo::value<std::string>()->default_value( "logging.json" ), "Logging configuration file name/path for library users");

my->_cfg_options.add(app_cfg_opts);
my->_app_options.add(app_cfg_opts);
my->_app_options.add(app_cli_opts);
}


bool application::initialize( int argc, char** argv )
{
bool application::initialize_impl(int argc, char** argv, vector<abstract_plugin*> autostart_plugins) {
set_program_options();

bpo::variables_map options;
bpo::store(bpo::parse_command_line(argc, argv, my->_app_options), options);

if( options.count( "help" ) ) {
cout << my->_app_options << "\n";
cout << my->_app_options << std::endl;
return false;
}

bfs::path data_dir = "data-dir";
if( options.count("data-dir") )
{
data_dir = options["data-dir"].as<bfs::path>();
if( options.count( "version" ) ) {
cout << my->_version << std::endl;
return false;
}

if( options.count( "print-default-config" ) ) {
print_default_config(cout);
return false;
}

if( options.count( "data-dir" ) ) {
// Workaround for 10+ year old Boost defect
// See https://svn.boost.org/trac10/ticket/8535
// Should be .as<bfs::path>() but paths with escaped spaces break bpo e.g.
// std::exception::what: the argument ('/path/with/white\ space') for option '--data-dir' is invalid
auto workaround = options["data-dir"].as<std::string>();
bfs::path data_dir = workaround;
if( data_dir.is_relative() )
data_dir = bfs::current_path() / data_dir;
my->_data_dir = data_dir;
}
my->_data_dir = data_dir;

bfs::path config_file_name = data_dir / "config.ini";
if( options.count( "config" ) ) {
auto config_file_name = options["config"].as<bfs::path>();
if( config_file_name.is_relative() )
config_file_name = data_dir / config_file_name;
if( options.count( "config-dir" ) ) {
auto workaround = options["config-dir"].as<std::string>();
bfs::path config_dir = workaround;
if( config_dir.is_relative() )
config_dir = bfs::current_path() / config_dir;
my->_config_dir = config_dir;
}

auto workaround = options["logconf"].as<std::string>();
bfs::path logconf = workaround;
if( logconf.is_relative() )
logconf = my->_config_dir / logconf;
my->_logging_conf = logconf;

workaround = options["config"].as<std::string>();
bfs::path config_file_name = workaround;
if( config_file_name.is_relative() )
config_file_name = my->_config_dir / config_file_name;

if(!bfs::exists(config_file_name)) {
if(config_file_name.compare(my->_config_dir / "config.ini") != 0)
{
cout << "Config file " << config_file_name << " missing." << std::endl;
return false;
}
write_default_config(config_file_name);
}

Expand All @@ -116,18 +181,20 @@ bool application::initialize( int argc, char** argv )
get_plugin(name).initialize(options);
}
}

bpo::notify(options);
try {
for (auto plugin : autostart_plugins)
if (plugin != nullptr && plugin->get_state() == abstract_plugin::registered)
plugin->initialize(options);

bpo::notify(options);
} catch (...) {
std::cerr << "Failed to initialize\n";
return false;
}

return true;
}

void application::startup()
{
for(auto plug : initialized_plugins)
plug->startup();
}

void application::shutdown() {
for(auto ritr = running_plugins.rbegin();
ritr != running_plugins.rend(); ++ritr) {
Expand All @@ -140,6 +207,7 @@ void application::shutdown() {
running_plugins.clear();
initialized_plugins.clear();
plugins.clear();
io_serv.reset();
}

void application::quit() {
Expand Down Expand Up @@ -169,28 +237,47 @@ void application::write_default_config(const bfs::path& cfg_file) {
bfs::create_directories(cfg_file.parent_path());

std::ofstream out_cfg( bfs::path(cfg_file).make_preferred().string());
print_default_config(out_cfg);
out_cfg.close();
}

void application::print_default_config(std::ostream& os) {
std::map<std::string, std::string> option_to_plug;
for(auto& plug : plugins) {
boost::program_options::options_description plugin_cli_opts;
boost::program_options::options_description plugin_cfg_opts;
plug.second->set_program_options(plugin_cli_opts, plugin_cfg_opts);

for(const boost::shared_ptr<bpo::option_description>& opt : plugin_cfg_opts.options())
option_to_plug[opt->long_name()] = plug.second->name();
}

for(const boost::shared_ptr<bpo::option_description> od : my->_cfg_options.options())
{
if(!od->description().empty())
out_cfg << "# " << od->description() << "\n";
if(!od->description().empty()) {
os << "# " << od->description();
std::map<std::string, std::string>::iterator it;
if((it = option_to_plug.find(od->long_name())) != option_to_plug.end())
os << " (" << it->second << ")";
os << std::endl;
}
boost::any store;
if(!od->semantic()->apply_default(store))
out_cfg << "# " << od->long_name() << " = \n";
os << "# " << od->long_name() << " = " << std::endl;
else {
auto example = od->format_parameter();
if(example.empty())
// This is a boolean switch
out_cfg << od->long_name() << " = " << "false\n";
os << od->long_name() << " = " << "false" << std::endl;
else {
// The string is formatted "arg (=<interesting part>)"
example.erase(0, 6);
example.erase(example.length()-1);
out_cfg << od->long_name() << " = " << example << "\n";
os << od->long_name() << " = " << example << std::endl;
}
}
out_cfg << "\n";
os << std::endl;
}
out_cfg.close();
}

abstract_plugin* application::find_plugin(const string& name)const
Expand All @@ -209,8 +296,12 @@ abstract_plugin& application::get_plugin(const string& name)const {
return *ptr;
}

bfs::path application::data_dir()const {
bfs::path application::data_dir() const {
return my->_data_dir;
}

bfs::path application::config_dir() const {
return my->_config_dir;
}

} /// namespace appbase
Loading