Skip to content

Commit

Permalink
Plugin framework refactoring
Browse files Browse the repository at this point in the history
This commit adds a new PluginManager that controls loading of all plugins,
regardless of type (kernel vs. stage, static vs. shared). Subsequently, the
StageFactory and KernelFactory classes are greatly simplified as there was
significant redundant code. Macros are provided to simplify creation of each
type of plugin, but are not explicitly required. Each plugin registers it's
name, description, link, type, and will soon include a version.

Dropped program_options in pdal.cpp and added ability to selectively load
plugin types (or none at all).
  • Loading branch information
chambbj committed Feb 11, 2015
1 parent a8240e4 commit 013eba4
Show file tree
Hide file tree
Showing 150 changed files with 2,263 additions and 1,318 deletions.
298 changes: 218 additions & 80 deletions apps/pdal.cpp
@@ -1,6 +1,6 @@
/******************************************************************************
* Copyright (c) 2013, Howard Butler (hobu.inc@gmail.com)
* Copyright (c) 2014, Bradley J Chambers (brad.chambers@gmail.com)
* Copyright (c) 2014-2015, Bradley J Chambers (brad.chambers@gmail.com)
*
* All rights reserved.
*
Expand Down Expand Up @@ -33,16 +33,23 @@
* OF SUCH DAMAGE.
****************************************************************************/

//#include <pdal/Kernels.hpp>
#include <pdal/KernelFactory.hpp>
#include <pdal/StageFactory.hpp>
#include <pdal/pdal_config.hpp>

#include <boost/algorithm/string.hpp>

#include <iomanip>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>

using namespace pdal;
namespace po = boost::program_options;

std::string headline("------------------------------------------------------------------------------------------");
static std::vector<std::string> s_loaded_kernels;

void outputVersion()
{
Expand All @@ -52,26 +59,32 @@ void outputVersion()
std::cout << std::endl;
}

void outputHelp(po::options_description const& options)
void outputHelp(bool no_plugins)
{
std::cerr << "Usage: pdal <command> [--debug] [--drivers] [--help] [--options[=<driver name>]] [--version]" << std::endl;
std::cerr << options << std::endl;
std::cerr << "Usage: pdal <command> [--debug] [--drivers] [--help] [--options[=<driver name>]] [--version]\n";
std::cerr << " --debug Show debug information\n";
std::cerr << " --drivers Show drivers\n";
std::cerr << " -h [ --help ] Print help message\n";
std::cerr << " --options [=arg(=all)] Show driver options\n";
std::cerr << " --version Show version info\n";
std::cerr << "\n";

std::cerr << "The most commonly used pdal commands are:" << std::endl;
std::cerr << "The most commonly used pdal commands are:\n";

KernelFactory f(no_plugins);
std::vector<std::string> loaded_kernels = f.getKernelNames();

KernelFactory f;
std::map<std::string, KernelInfo> const& kernels = f.getKernelInfos();
for (auto i = kernels.begin(); i != kernels.end(); ++i)
std::cout << " - " << i->second.getName() << std::endl;
for (auto name : loaded_kernels)
std::cout << " - " << name << std::endl;

std::cout << "See http://pdal.io/apps.html for more detail";
std::cout << std::endl;
}

void outputDrivers()
void outputDrivers(bool no_plugins)
{
StageFactory f;
std::map<std::string, StageInfo> const& drivers = f.getStageInfos();
StageFactory f(no_plugins);
std::map<std::string, std::string> sm = f.getStageMap();

std::ostringstream strm;

Expand All @@ -88,21 +101,21 @@ void outputDrivers()

strm << std::left;

for (auto i = drivers.begin(); i != drivers.end(); ++i)
for (auto s : sm)
{
std::vector<std::string> lines;
std::string description(i->second.getDescription());
std::string description(s.second);
description = boost::algorithm::erase_all_copy(description, "\n");

Utils::wordWrap(description, lines, description_column-1);
if (lines.size() == 1)
{
strm << std::setw(name_column) << i->second.getName() << " "
strm << std::setw(name_column) << s.first << " "
<< std::setw(description_column) << description << std::endl;
}
else
{
strm << std::setw(name_column) << i->second.getName() << " "
strm << std::setw(name_column) << s.first << " "
<< lines[0] << std::endl;
}

Expand All @@ -118,108 +131,233 @@ void outputDrivers()
std::cout << strm.str() << std::endl;
}

void outputOptions(std::string const& opt)
void outputOptions(std::string const& n, bool no_plugins)
{
StageFactory f;
std::cout << opt << std::endl;
std::cout << f.toRST(opt) << std::endl;
StageFactory f(no_plugins);
std::unique_ptr<Stage> s = f.createStage(n);
std::vector<Option> options = s->getDefaultOptions().getOptions();
if (options.size())
{
std::ostringstream strm;

strm << n << std::endl;
strm << headline << std::endl;

std::string tablehead("================================ =============== =========================================");
std::string headings ("Name Default Description");

strm << std::endl;
strm << tablehead << std::endl;
strm << headings << std::endl;
strm << tablehead << std::endl;

uint32_t default_column(15);
uint32_t name_column(32);
uint32_t description_column(40);

strm << std::left;

for (auto const& opt : options)
{
std::string default_value(opt.getValue<std::string>() );
default_value = boost::algorithm::erase_all_copy(default_value, "\n");
if (default_value.size() > default_column -1 )
{
default_value = default_value.substr(0, default_column-3);
default_value = default_value + "...";
}

std::vector<std::string> lines;
std::string description(opt.getDescription());
description = boost::algorithm::erase_all_copy(description, "\n");

Utils::wordWrap(description, lines, description_column-1);
if (lines.size() == 1)
{

strm << std::setw(name_column) << opt.getName() << " "
<< std::setw(default_column) << default_value << " "
<< std::setw(description_column) << description << std::endl;
} else
strm << std::setw(name_column) << opt.getName() << " "
<< std::setw(default_column) << default_value << " "
<< lines[0] << std::endl;

std::stringstream blank;
size_t blanks(49);
for (size_t i = 0; i < blanks; ++i)
blank << " ";
for (size_t i = 1; i < lines.size(); ++i)
{
strm << blank.str() <<lines[i] << std::endl;
}
}
strm << tablehead << std::endl;
strm << std::endl;

std::cout << strm.str() << std::endl;
}
else
{
std::cerr << n << " has no options\n";
}
}

int main(int argc, char* argv[])
void outputOptions(bool no_plugins)
{
KernelFactory f;

po::options_description options;
po::positional_options_description positional;
po::variables_map variables;
positional.add("command", 1);

options.add_options()
("command", po::value<std::string>(), "command name")
("debug", po::value<bool>()->zero_tokens()->implicit_value(true), "Show debug information")
("drivers", po::value<bool>()->zero_tokens()->implicit_value(true), "Show drivers")
("help,h", po::value<bool>()->zero_tokens()->implicit_value(true), "Print help message")
("options", po::value<std::string>()->implicit_value("all"), "Show driver options")
("version", po::value<bool>()->zero_tokens()->implicit_value(true), "Show version info")
;
StageFactory f(no_plugins);
std::vector<std::string> nv = f.getStageNames();
for (auto const& n : nv)
outputOptions(n, no_plugins);
}

int main(int argc, char* argv[])
{
// No arguments, print basic usage, plugins will be loaded
if (argc < 2)
{
outputHelp(options);
outputHelp(false);
return 1;
}

try
// Test whether or not to load plugins for all subsequent options
bool no_plugins = false;
for (int i = 1; i < argc; ++i)
{
po::store(po::command_line_parser(2, argv).
options(options).positional(positional).run(),
variables);
if (boost::iequals(argv[i], "--no-plugins"))
{
no_plugins = true;
break;
}
}
catch (boost::program_options::unknown_option& e)

// Discover available kernels (with or without plugins), and test to see if
// the positional option 'command' is a valid kernel
KernelFactory f(no_plugins);
s_loaded_kernels = f.getKernelNames();

bool isValidKernel = false;
std::string command(argv[1]);
for (auto name : s_loaded_kernels)
{
#if BOOST_VERSION >= 104200
if (boost::iequals(argv[1], name))
{
isValidKernel = true;
break;
}
}

std::cerr << "Unknown option '" << e.get_option_name() <<"' not recognized" << std::endl << std::endl;
#else
std::cerr << "Unknown option '" << std::string(e.what()) <<"' not recognized" << std::endl << std::endl;
#endif
outputVersion();
return 1;
// Dispatch execution to the kernel, passing all remaining args
if (isValidKernel)
{
int count(argc - 1); // remove the 1st argument
const char** args = const_cast<const char**>(&argv[1]);

std::unique_ptr<Kernel> app = f.createKernel(command);
return app->run(count, args, command);
}

int count(argc - 1); // remove the 1st argument
const char** args = const_cast<const char**>(&argv[1]);
// Otherwise, process the remaining args to see if they are supported
bool debug = false;
bool drivers = false;
bool help = false;
bool options = false;
bool version = false;
std::string optString("all"); // --options will default to displaying information on all available stages
for (int i = 1; i < argc; ++i)
{
if (boost::iequals(argv[i], "--debug"))
{
debug = true;
}
else if (boost::iequals(argv[i], "--drivers"))
{
drivers = true;
}
else if (boost::iequals(argv[i], "--help") || boost::iequals(argv[i], "-h"))
{
help = true;
}
else if (boost::algorithm::istarts_with(argv[i], "--options"))
{
std::vector<std::string> optionsVec;
// we are rather unsophisticated for now, only splitting on '=', no spaces allowed
boost::algorithm::split(optionsVec, argv[i],
boost::algorithm::is_any_of("="), boost::algorithm::token_compress_on);
options = true;
if (optionsVec.size() == 2)
optString = optionsVec[1];
}
else if (boost::iequals(argv[i], "--version"))
{
version = true;
}
else
{
// --no-plugins is valid, but was already parsed
if (no_plugins) continue;

if (variables.count("version"))
if (boost::algorithm::istarts_with(argv[i], "--"))
std::cerr << "Unknown option '" << argv[i] <<"' not recognized" << std::endl << std::endl;
}
}

if (version)
{
outputVersion();
return 0;
}

if (variables.count("drivers"))
if (drivers)
{
outputDrivers();
outputDrivers(no_plugins);
return 0;
}

if (variables.count("options"))
if (options)
{
std::string opt = variables["options"].as<std::string>();
outputOptions(opt);
return 0;
if (boost::iequals(optString, "all"))
{
outputOptions(no_plugins);
}
else
{
StageFactory f(no_plugins);
std::vector<std::string> nv = f.getStageNames();
for (auto const& n : nv)
{
if (boost::iequals(optString, n))
{
outputOptions(optString, no_plugins);
return 0;
}
}
}
std::cerr << "Requested options for unknown driver " << optString << "\n";
return -1;
}

if (variables.count("debug"))
if (debug)
{
std::cerr << getPDALDebugInformation() << std::endl;
return 0;
}

if (variables.count("help") || !variables.count("command"))
if (help)
{
outputHelp(options);
outputHelp(no_plugins);
return 0;
}

std::string command = variables["command"].as<std::string>();

bool isValidKernel = false;
std::map<std::string, KernelInfo> const& kernels = f.getKernelInfos();
for (auto i = kernels.begin(); i != kernels.end(); ++i)
if (no_plugins)
{
if (boost::iequals(command, i->second.getName()))
{
isValidKernel = true;
}
}

if (isValidKernel)
{
std::unique_ptr<Kernel> app(f.createKernel(command));
return app->run(count, args, command);
outputHelp(no_plugins);
return 0;
}

std::cerr << "Command '" << command <<"' not recognized" << std::endl << std::endl;
outputHelp(options);
if (!isValidKernel)
std::cerr << "Command '" << command <<"' not recognized" << std::endl << std::endl;
outputHelp(no_plugins);
return 1;
}

0 comments on commit 013eba4

Please sign in to comment.