efl/src/bin/eolian_cxx/eolian_cxx.cc

348 lines
9.2 KiB
C++

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string>
#include <algorithm>
#include <stdexcept>
#include <iosfwd>
#include <type_traits>
#include <cassert>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Eolian.h>
#include <Eina.hh>
#include <Eolian_Cxx.hh>
#include "convert.hh"
#include "type_lookup.hh"
#include "convert.hh"
#include "eolian_wrappers.hh"
#include "safe_strings.hh"
namespace eolian_cxx {
/// Program options.
struct options_type
{
std::vector<std::string> include_dirs;
std::string in_file;
std::string out_file;
std::string out_dir;
std::string classname;
bool recurse;
bool generate_all;
options_type()
: include_dirs()
, in_file()
, out_file()
, out_dir()
, classname()
, recurse(false)
, generate_all(false)
{}
};
efl::eina::log_domain domain("eolian_cxx");
static bool
opts_check(eolian_cxx::options_type const& opts)
{
if (!opts.generate_all && opts.in_file.empty())
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Nothing to generate?" << std::endl;
}
else if (opts.generate_all && !opts.in_file.empty())
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Didn't expect to receive input files (" << opts.in_file
<< ") with parameter -a."
<< std::endl;
}
else if (opts.generate_all && !opts.out_file.empty())
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Can't use -a and -o together." << std::endl;
}
else if (opts.generate_all && opts.include_dirs.empty())
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Option -a requires at least one include directory (-I)."
<< std::endl;
}
else
{
return true; // valid.
}
return false;
}
efl::eolian::eo_generator_options
generator_options(const Eolian_Class& klass)
{
efl::eolian::eo_generator_options gen_opts;
gen_opts.c_headers.push_back(class_base_file(klass) + ".h");
void *cur = NULL;
Eina_Iterator *inheritances = ::eolian_class_inherits_get(&klass);
EINA_ITERATOR_FOREACH(inheritances, cur)
{
const Eolian_Class *ext = ::eolian_class_get_by_name(static_cast<const char*>(cur));
std::string eo_parent_file = class_base_file(*ext);
if (!eo_parent_file.empty())
{
gen_opts.cxx_headers.push_back(eo_parent_file + ".hh");
}
else
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Couldn't find source file for class '" << ext << "'"
<< std::endl;
}
}
eina_iterator_free(inheritances);
return gen_opts;
}
static bool
generate(const Eolian_Class& klass, eolian_cxx::options_type const& opts)
{
efl::eolian::eo_class cls = eolian_cxx::convert_eolian_class(klass);
efl::eolian::eo_generator_options gen_opts = generator_options(klass);
std::string outname = opts.out_file.empty() ? (class_base_file(klass) + ".hh") : opts.out_file;
if (!opts.out_dir.empty())
{
outname = opts.out_dir + "/" + outname;
}
if(opts.out_file == "-")
{
efl::eolian::generate(std::cout, cls, gen_opts);
}
else
{
std::ofstream outfile;
outfile.open(outname);
if (outfile.good())
{
efl::eolian::generate(outfile, cls, gen_opts);
outfile.close();
}
else
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Can't open output file: " << outname << std::endl;
return false;
}
}
return true;
}
static void
run(options_type const& opts)
{
const Eolian_Class *klass = NULL;
if (!opts.classname.empty())
klass = class_from_name(opts.classname);
else if (!opts.in_file.empty())
klass = class_from_file(opts.in_file);
if (klass)
{
if (!generate(*klass, opts))
goto err;
}
else
{
efl::eina::iterator<const Eolian_Class> it(class_list_all());
efl::eina::iterator<const Eolian_Class> end;
while (it != end)
{
Eolian_Class c = (*it);
if (!generate(c, opts))
{
klass = &c;
goto err;
}
++it;
}
}
return;
err:
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Error generating: " << class_name(*klass)
<< std::endl;
assert(false && "error generating class");
}
static void
database_load(options_type const& opts)
{
for (auto src : opts.include_dirs)
{
if (!::eolian_directory_scan(src.c_str()))
{
EINA_CXX_DOM_LOG_WARN(eolian_cxx::domain)
<< "Couldn't load eolian from '" << src << "'.";
}
}
if (!::eolian_all_eot_files_parse())
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Eolian failed parsing eot files";
assert(false && "Error parsing eot files");
}
if (!opts.in_file.empty())
{
if (!::eolian_eo_file_parse(opts.in_file.c_str()))
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Failed parsing: " << opts.in_file << ".";
assert(false && "Error parsing input file");
}
}
else if (!::eolian_all_eo_files_parse())
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Eolian failed parsing input files";
assert(false && "Error parsing input files");
}
if (!::eolian_database_validate())
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain)
<< "Eolian failed validating database.";
assert(false && "Error validating database");
}
}
} // namespace eolian_cxx {
static void
_print_version()
{
std::cerr
<< "Eolian C++ Binding Generator (EFL "
<< PACKAGE_VERSION << ")" << std::endl;
}
static void
_usage(const char *progname)
{
std::cerr
<< progname
<< " [options] [file.eo]" << std::endl
<< " A single input file must be provided (unless -a is specified)." << std::endl
<< "Options:" << std::endl
<< " -a, --all Generate bindings for all Eo classes." << std::endl
<< " -c, --class <name> The Eo class name to generate code for." << std::endl
<< " -D, --out-dir <dir> Output directory where generated code will be written." << std::endl
<< " -I, --in <file/dir> The source containing the .eo descriptions." << std::endl
<< " -o, --out-file <file> The output file name. [default: <classname>.eo.hh]" << std::endl
<< " -n, --namespace <ns> Wrap generated code in a namespace. [Eg: efl::ecore::file]" << std::endl
<< " -r, --recurse Recurse input directories loading .eo files." << std::endl
<< " -v, --version Print the version." << std::endl
<< " -h, --help Print this help." << std::endl;
exit(EXIT_FAILURE);
}
static void
_assert_not_dup(std::string option, std::string value)
{
if (value != "")
{
EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain) <<
"Option -" + option + " already set (" + value + ")";
}
}
static eolian_cxx::options_type
opts_get(int argc, char **argv)
{
eolian_cxx::options_type opts;
const struct option long_options[] =
{
{ "in", required_argument, 0, 'I' },
{ "out-dir", required_argument, 0, 'D' },
{ "out-file", required_argument, 0, 'o' },
{ "class", required_argument, 0, 'c' },
{ "all", no_argument, 0, 'a' },
{ "recurse", no_argument, 0, 'r' },
{ "version", no_argument, 0, 'v' },
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
const char* options = "I:D:o:c:arvh";
int c, idx;
while ( (c = getopt_long(argc, argv, options, long_options, &idx)) != -1)
{
if (c == 'I')
{
opts.include_dirs.push_back(optarg);
}
else if (c == 'D')
{
_assert_not_dup("D", opts.out_dir);
opts.out_dir = optarg;
}
else if (c == 'o')
{
_assert_not_dup("o", opts.out_file);
opts.out_file = optarg;
}
else if (c == 'c')
{
_assert_not_dup("c", opts.classname);
opts.classname = optarg;
}
else if (c == 'a')
{
opts.generate_all = true;
}
else if (c == 'r')
{
opts.recurse = true;
}
else if (c == 'h')
{
_usage(argv[0]);
}
else if (c == 'v')
{
_print_version();
if (argc == 2) exit(EXIT_SUCCESS);
}
}
if (optind == argc-1)
{
opts.in_file = argv[optind];
}
if (!eolian_cxx::opts_check(opts))
{
_usage(argv[0]);
assert(false && "Wrong options passed in command-line");
}
return opts;
}
int main(int argc, char **argv)
{
efl::eina::eina_init eina_init;
efl::eolian::eolian_init eolian_init;
eolian_cxx::options_type opts = opts_get(argc, argv);
eolian_cxx::database_load(opts);
eolian_cxx::run(opts);
return 0;
}