diff options
Diffstat (limited to 'odb/odb/plugin.cxx')
-rw-r--r-- | odb/odb/plugin.cxx | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/odb/odb/plugin.cxx b/odb/odb/plugin.cxx new file mode 100644 index 0000000..c065a8a --- /dev/null +++ b/odb/odb/plugin.cxx @@ -0,0 +1,458 @@ +// file : odb/plugin.cxx +// license : GNU GPL v3; see accompanying LICENSE file + +#include <odb/gcc.hxx> // Keep it first. + +#include <unistd.h> // stat() +#include <sys/types.h> // stat +#include <sys/stat.h> // stat + +#include <memory> // std::unique_ptr +#include <string> +#include <vector> +#include <cstring> // std::strcpy, std::strstr +#include <cassert> +#include <iostream> + +#include <libcutl/re.hxx> +#include <libcutl/fs/path.hxx> + +#include <odb/pragma.hxx> +#include <odb/parser.hxx> +#include <odb/options.hxx> +#include <odb/option-functions.hxx> +#include <odb/features.hxx> +#include <odb/profile.hxx> +#include <odb/version.hxx> +#include <odb/validator.hxx> +#include <odb/processor.hxx> +#include <odb/generator.hxx> +#include <odb/semantics/unit.hxx> + +using namespace std; +using namespace semantics; + +using cutl::fs::path; +using cutl::fs::invalid_path; + +typedef vector<path> paths; + +#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN) +__declspec(dllexport) +#endif +int plugin_is_GPL_compatible; + +unique_ptr<options const> options_; +paths profile_paths_; +path file_; // File being compiled. +paths inputs_; // List of input files in at-once mode or just file_. + +bool (*cpp_diagnostic_prev) ( + cpp_reader*, +#if BUILDING_GCC_MAJOR >= 9 + cpp_diagnostic_level, + cpp_warning_reason, +#else + int, + int, +#endif +#if BUILDING_GCC_MAJOR >= 6 + rich_location*, +#else + location_t, + unsigned int, +#endif + const char*, + va_list*); + +static bool +cpp_diagnostic_filter (cpp_reader* r, +#if BUILDING_GCC_MAJOR >= 9 + cpp_diagnostic_level level, + cpp_warning_reason reason, +#else + int level, + int reason, +#endif +#if BUILDING_GCC_MAJOR >= 6 + rich_location* l, +#else + location_t l, + unsigned int column_override, +#endif + const char* msg, + va_list* ap) +{ + // #pragma once in the main file. Note that the message that we get is + // potentially translated so we search for the substring (there is + // currently only one warning in libcpp that mentions #pragma once). + // Unfortunately, some translations translate the 'once' word (e.g, + // #pragma uno; I wonder if one can actually specify it like that in + // the source code). Oh, well, there is only so much we can do. + // + if (strstr (msg, "#pragma once") != 0) + return true; + + return cpp_diagnostic_prev ( + r, + level, + reason, +#if BUILDING_GCC_MAJOR >= 6 + l, +#else + l, + column_override, +#endif + msg, + ap); +} + +// A prefix of the _cpp_file struct. This struct is not part of the +// public interface so we have to resort to this technique (based on +// libcpp/files.c). +// +struct cpp_file_prefix +{ + char const* name; + char const* path; + char const* pchname; + char const* dir_name; + _cpp_file* next_file; + const uchar* buffer; + const uchar* buffer_start; + const cpp_hashnode *cmacro; + cpp_dir *dir; + struct stat st; +}; + +extern "C" void +start_unit_callback (void*, void*) +{ + // Set the preprocessor error callback to filter out useless diagnostics. + // + cpp_callbacks* cb (cpp_get_callbacks (parse_in)); + +#if BUILDING_GCC_MAJOR >= 9 + cpp_diagnostic_prev = cb->diagnostic; + cb->diagnostic = &cpp_diagnostic_filter; +#else + cpp_diagnostic_prev = cb->error; + cb->error = &cpp_diagnostic_filter; +#endif + + if (cpp_diagnostic_prev == 0) + { + cerr << "ice: expected cpp diagnostic callback to be set" << endl; + exit (1); + } + + // Set the directory of the main file (stdin) to that of the orginal + // file so that relative inclusion works. Also adjust the path and + // re-stat the file so that #pragma once works. + // + cpp_buffer* b (cpp_get_buffer (parse_in)); + _cpp_file* f (cpp_get_file (b)); + cpp_dir* d (cpp_get_dir (f)); + char const* p (cpp_get_path (f)); + + cpp_file_prefix* fp (reinterpret_cast<cpp_file_prefix*> (f)); + + // Perform some sanity checks. + // + if (p != 0 && *p == '\0' // The path should be empty (stdin). + && cpp_get_prev (b) == 0 // This is the only buffer (main file). + && fp->path == p // Our prefix corresponds to the actual type. + && fp->dir == d // Our prefix corresponds to the actual type. + && fp->dir_name == 0) // The directory part hasn't been initialized. + { + // The dir_name is initialized by libcpp lazily so we can preemptively + // set it to what we need. + // + path d (file_.directory ()); + char* s; + + if (d.empty ()) + { + s = XNEWVEC (char, 1); + *s = '\0'; + } + else + { + size_t n (d.string ().size ()); + s = XNEWVEC (char, n + 2); + strcpy (s, d.string ().c_str ()); + s[n] = path::traits::directory_separator; + s[n + 1] = '\0'; + } + + fp->dir_name = s; + + // Unless we are in the at-once mode (where input files are actually + // #include'ed into the synthesized stdin), pretend that we are the + // actual input file. This is necessary for the #pragma once to work. + // + // All this relies on the way things are implemented in libcpp. In + // particular, the #pragma once code first checks if the mtime and + // size of files match (that's why we need stat() below). If they + // do, then it goes ahead and compares their contents. To re-load + // the contents of the file libcpp uses the path (that's why we + // need to adjust that as well). + // + if (inputs_.size () == 1) + { + string const& f (file_.string ()); + + XDELETEVEC (fp->path); + size_t n (f.size ()); + char* p (XNEWVEC (char, n + 1)); + strcpy (p, f.c_str ()); + p[n] = '\0'; + fp->path = p; + + // This call shouldn't fail since we've just opened it in the driver. + stat (fp->path, &fp->st); + } + } + else + { + cerr << "ice: unable to initialize main file directory" << endl; + exit (1); + } +} + +extern "C" void +gate_callback (void*, void*) +{ + // If there were errors during compilation, let GCC handle the + // exit. + // + if (errorcount || sorrycount) + return; + + int r (0); + + try + { + // Post process pragmas. + // + post_process_pragmas (); + + // Parse the GCC tree to semantic graph. + // + parser p (*options_, loc_pragmas_, ns_loc_pragmas_, decl_pragmas_); + unique_ptr<unit> u (p.parse (global_namespace, file_)); + + features f; + + // Process, pass 1. + // + process (*options_, f, *u, file_, 1); + + // Validate, pass 1. + // + validate (*options_, f, *u, file_, 1); + + // Process, pass 2. + // + process (*options_, f, *u, file_, 2); + + // Validate, pass 2. + // + validate (*options_, f, *u, file_, 2); + + // Generate. + // + generate (*options_, f, *u, file_, inputs_); + } + catch (cutl::re::format const& e) + { + cerr << "error: invalid regex: '" << e.regex () << "': " << + e.description () << endl; + r = 1; + } + catch (pragmas_failed const&) + { + // Diagnostics has aready been issued. + // + r = 1; + } + catch (parser::failed const&) + { + // Diagnostics has aready been issued. + // + r = 1; + } + catch (validator_failed const&) + { + // Diagnostics has aready been issued. + // + r = 1; + } + catch (processor_failed const&) + { + // Diagnostics has aready been issued. + // + r = 1; + } + catch (generator_failed const&) + { + // Diagnostics has aready been issued. + // + r = 1; + } + + exit (r); +} + +static char const* const odb_version = ODB_COMPILER_VERSION_STR; + +typedef vector<string> strings; + +extern "C" +#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN) +__declspec(dllexport) +#endif +int +plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*) +{ + int r (0); + plugin_info->version = odb_version; + + try + { + // Parse options. + // + { + strings argv_str; + argv_str.push_back (plugin_info->base_name); + + for (int i (0); i < plugin_info->argc; ++i) + { + plugin_argument& a (plugin_info->argv[i]); + + // A value cannot contain '=' so it is passed as the backspace + // character. + // + string v (a.value != 0 ? a.value : ""); + for (size_t i (0); i < v.size (); ++i) + if (v[i] == '\b') + v[i] = '='; + + // Handle service options. + // + if (strcmp (a.key, "svc-path") == 0) + { + profile_paths_.push_back (path (v)); + continue; + } + + if (strcmp (a.key, "svc-file") == 0) + { + // First is the main file. Subsequent are inputs in the at-once + // mode. + // + if (file_.empty ()) + file_ = path (v); + else + inputs_.push_back (path (v)); + + continue; + } + + string opt (strlen (a.key) > 1 ? "--" : "-"); + opt += a.key; + + argv_str.push_back (opt); + + if (a.value != 0) + argv_str.push_back (v); + } + + vector<char*> argv; + for (strings::iterator i (argv_str.begin ()); i != argv_str.end (); ++i) + argv.push_back (const_cast<char*> (i->c_str ())); + + int argc (static_cast<int> (argv.size ())); + + if (inputs_.empty ()) + inputs_.push_back (file_); + + // Two-phase options parsing, similar to the driver. + // + cli::argv_file_scanner::option_info oi[3]; + oi[0].option = "--options-file"; + oi[0].search_func = 0; + oi[1].option = "-p"; + oi[2].option = "--profile"; + + database db; + { + oi[1].search_func = &profile_search_ignore; + oi[2].search_func = &profile_search_ignore; + + cli::argv_file_scanner scan (argc, &argv[0], oi, 3); + options ops (scan); + assert (ops.database_specified ()); + db = ops.database ()[0]; + } + + profile_data pd (profile_paths_, db, "odb plugin"); + oi[1].search_func = &profile_search; + oi[2].search_func = &profile_search; + oi[1].arg = &pd; + oi[2].arg = &pd; + + cli::argv_file_scanner scan (argc, &argv[0], oi, 3); + unique_ptr<options> ops ( + new options (scan, cli::unknown_mode::fail, cli::unknown_mode::fail)); + + // Process options. + // + process_options (*ops); + + options_ = move (ops); + pragma_db_ = db; + pragma_multi_ = options_->multi_database (); + } + + if (options_->trace ()) + cerr << "starting plugin " << plugin_info->base_name << endl; + + // Disable assembly output. GCC doesn't define HOST_BIT_BUCKET + // correctly for MinGW (it still used /dev/null which fails to + // open). + // +#ifdef _WIN32 + asm_file_name = "nul"; +#else + asm_file_name = HOST_BIT_BUCKET; +#endif + + // Register callbacks. + // + register_callback (plugin_info->base_name, + PLUGIN_PRAGMAS, + register_odb_pragmas, + 0); + + register_callback (plugin_info->base_name, + PLUGIN_START_UNIT, + start_unit_callback, + 0); + + register_callback (plugin_info->base_name, + PLUGIN_OVERRIDE_GATE, + &gate_callback, + 0); + } + catch (cli::exception const& ex) + { + cerr << ex << endl; + r = 1; + } + + if (r != 0) + exit (r); + + return r; +} |