diff options
Diffstat (limited to 'odb/odb.cxx')
-rw-r--r-- | odb/odb.cxx | 2032 |
1 files changed, 0 insertions, 2032 deletions
diff --git a/odb/odb.cxx b/odb/odb.cxx deleted file mode 100644 index 225cc21..0000000 --- a/odb/odb.cxx +++ /dev/null @@ -1,2032 +0,0 @@ -// file : odb/odb.cxx -// license : GNU GPL v3; see accompanying LICENSE file - -#include <errno.h> -#include <stdlib.h> // getenv, setenv -#include <string.h> // strerror, memset -#include <unistd.h> // stat, close -#include <sys/types.h> // stat -#include <sys/stat.h> // stat - -// Process. -// -#ifndef _WIN32 -# include <unistd.h> // execvp, fork, dup2, pipe, {STDIN,STDERR}_FILENO -# include <sys/types.h> // waitpid -# include <sys/wait.h> // waitpid -#else -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include <windows.h> // CreatePipe, CreateProcess -# include <io.h> // _open_osfhandle -# include <fcntl.h> // _O_TEXT -#endif - -#include <string> -#include <vector> -#include <cstddef> // size_t -#include <sstream> -#include <fstream> -#include <iostream> -#include <ext/stdio_filebuf.h> - -#include <cutl/fs/path.hxx> - -#include <odb/version.hxx> -#include <odb/options.hxx> -#include <odb/profile.hxx> - -#ifdef HAVE_CONFIG_H -# include <odb/config.h> -#endif - -using namespace std; -using cutl::fs::path; -using cutl::fs::invalid_path; - -typedef vector<string> strings; -typedef vector<path> paths; - -// -// Path manipulation. -// - -// Escape backslashes in the path. -// -static string -escape_path (string const&); - -// Search the PATH environment variable for the file. -// -static path -path_search (path const&); - -// Driver path with the directory part (search PATH). -// -static path -driver_path (path const& driver); - -#ifndef ODB_STATIC_PLUGIN -static path -plugin_path (path const& driver, string const& gxx); -#endif - -// -// Process manipulation. -// -struct process_info -{ -#ifndef _WIN32 - pid_t id; -#else - HANDLE id; -#endif - - int out_fd; // Write to this fd to send to the new process' stdin. - int in_efd; // Read from this fd to receive from the new process' stderr. - int in_ofd; // Read from this fd to receive from the new process' stdout. -}; - -struct process_failure {}; - -// Start another process using the specified command line. Connect the -// newly created process' stdin to out_fd. Also if connect_* are true, -// connect the created process' stdout and stderr to in_*fd. Issue -// diagnostics and throw process_failure if anything goes wrong. The -// name argument is the name of the current process for diagnostics. -// -static process_info -start_process (char const* args[], - char const* name, - bool connect_stderr = false, - bool connect_stdout = false); - -// Wait for the process to terminate. Return true if the process terminated -// normally and with the zero exit status. Issue diagnostics and throw -// process_failure if anything goes wrong. The name argument is the name -// of the current process for diagnostics. -// -static bool -wait_process (process_info, char const* name); - -// -// -static string -encode_plugin_flag (string const& k); - -static string -encode_plugin_option (string const& k, string const& v); - -// Extract header search paths from GCC's -v output. May throw the -// profile_failure, process_failure and invalid_path exceptions. Name -// is the program name (argv[0]) for diagnostics. -// -static paths -profile_paths (strings const& args, char const* name); - -static char const* const db_macro[] = -{ - "-DODB_DATABASE_COMMON", - "-DODB_DATABASE_MSSQL", - "-DODB_DATABASE_MYSQL", - "-DODB_DATABASE_ORACLE", - "-DODB_DATABASE_PGSQL", - "-DODB_DATABASE_SQLITE" -}; - -int -main (int argc, char* argv[]) -{ - ostream& e (cerr); - - try - { - strings args, plugin_args; - bool v (false); - - // The first argument points to the program name, which is - // g++ by default. - // -#ifdef ODB_GXX_NAME - path gxx (ODB_GXX_NAME); - - if (gxx.empty ()) - { - e << argv[0] << ": error: embedded g++ compile name is empty" << endl; - return 1; - } - - // If the g++ name is a relative path (starts with '.'), then use - // our own path as base. - // - if (gxx.string ()[0] == '.') - { - path dp (driver_path (path (argv[0]))); - path d (dp.directory ()); - - if (!d.empty ()) - gxx = d / gxx; - } - - args.push_back (gxx.string ()); - - // Also modify LD_LIBRARY_PATH to include the lib path. - // -#ifndef _WIN32 - { -#ifdef __APPLE__ - char const name[] = "DYLD_LIBRARY_PATH"; -#else - char const name[] = "LD_LIBRARY_PATH"; -#endif - - string ld_paths; - - if (char const* s = getenv (name)) - ld_paths = s; - - path d (gxx.directory ()); - - if (!d.empty ()) - { - d.complete (); - d /= path ("..") / path ("lib"); - - if (ld_paths.empty ()) - ld_paths = d.string (); - else - ld_paths = d.string () + path::traits::path_separator + ld_paths; - - if (setenv (name, ld_paths.c_str (), 1) != 0) - { - e << argv[0] << ": error: unable to update environment" << endl; - return 1; - } - } - } -#endif // _WIN32 - -#else - args.push_back ("g++"); -#endif // ODB_GXX_NAME - - // Default options. - // - args.push_back ("-x"); - args.push_back ("c++"); - args.push_back (""); // Reserve space for -std=c++XX. - args.push_back ("-S"); - args.push_back ("-Wunknown-pragmas"); - args.push_back ("-Wno-deprecated"); - args.push_back (""); // Reserve space for -fplugin=path. - - // Parse the default options file if we have one. - // - strings def_inc_dirs; - strings def_defines; -#ifdef ODB_DEFAULT_OPTIONS_FILE - { - path file (ODB_DEFAULT_OPTIONS_FILE); - - // If the path is relative, then use the driver's path as a base. If - // the file is not found in that directory, then also try outer - // directory (so that we can find /etc if driver is in /usr/bin). - // - if (file.relative ()) - { - bool found (false); - path dd (driver_path (path (argv[0])).directory ()); - - for (path d (dd);; d = d.directory ()) - { - path f (d / file); - // Check that the file exist without checking for permissions, etc. - // - struct stat s; - if (stat (f.string ().c_str (), &s) == 0 && S_ISREG (s.st_mode)) - { - file = f; - found = true; - break; - } - - if (d.root ()) - break; - } - - if (!found) - file = dd / file; // For diagnostics. - } - - int ac (3); - const char* av[4] = {argv[0], "--file", file.string ().c_str (), 0}; - cli::argv_file_scanner s (ac, const_cast<char**> (av), "--file"); - - bool first_x (true); - - while (s.more ()) - { - string a (s.next ()); - size_t n (a.size ()); - - // -x - // - if (a == "-x") - { - if (!s.more () || (a = s.next ()).empty ()) - { - e << file << ": error: expected argument for the " << a - << " option" << endl; - return 1; - } - - if (first_x) - { - first_x = false; - - // If it doesn't start with '-', then it must be the g++ - // executable name. Update the first argument with it. - // - if (a[0] != '-') - args[0] = a; - else - args.push_back (a); - } - else - args.push_back (a); - } - // -I - // - else if (n > 1 && a[0] == '-' && a[1] == 'I') - { - def_inc_dirs.push_back (a); - - if (n == 2) // -I /path - { - if (!s.more () || (a = s.next ()).empty ()) - { - e << file << ": error: expected argument for the -I option" - << endl; - return 1; - } - - def_inc_dirs.push_back (a); - } - } - // -isystem, -iquote, -idirafter, and -framework (Mac OS X) - // - else if (a == "-isystem" || - a == "-iquote" || - a == "-idirafter" || - a == "-framework") - { - def_inc_dirs.push_back (a); - - if (!s.more () || (a = s.next ()).empty ()) - { - e << file << ": error: expected argument for the " << a - << " option" << endl; - return 1; - } - - def_inc_dirs.push_back (a); - } - // -D - // - else if (n > 1 && a[0] == '-' && a[1] == 'D') - { - def_defines.push_back (a); - - if (n == 2) // -D macro - { - if (!s.more () || (a = s.next ()).empty ()) - { - e << file << ": error: expected argument for the -D option" - << endl; - return 1; - } - - def_defines.push_back (a); - } - } - // -U - // - else if (n > 1 && a[0] == '-' && a[1] == 'U') - { - def_defines.push_back (a); - - if (n == 2) // -U macro - { - if (!s.more () || (a = s.next ()).empty ()) - { - e << file << ": error: expected argument for the -U option" - << endl; - return 1; - } - - def_defines.push_back (a); - } - } - else - plugin_args.push_back (a); - } - } -#endif - - // Add the default preprocessor defines (-D/-U) before the user-supplied - // ones. - // - args.insert (args.end (), def_defines.begin (), def_defines.end ()); - - // Parse driver options. - // - bool first_x (true); - - for (int i = 1; i < argc; ++i) - { - string a (argv[i]); - size_t n (a.size ()); - - // -v - // - if (a == "-v") - { - v = true; - args.push_back (a); - } - // -x - // - else if (a == "-x") - { - if (++i == argc || argv[i][0] == '\0') - { - e << argv[0] << ": error: expected argument for the -x option" << endl; - return 1; - } - - a = argv[i]; - - if (first_x) - { - first_x = false; - - // If it doesn't start with '-', then it must be the g++ - // executable name. Update the first argument with it. - // - if (a[0] != '-') - args[0] = a; - else - args.push_back (a); - } - else - args.push_back (a); - } - // -I - // - else if (n > 1 && a[0] == '-' && a[1] == 'I') - { - args.push_back (a); - - if (n == 2) // -I /path - { - if (++i == argc || argv[i][0] == '\0') - { - e << argv[0] << ": error: expected argument for the -I option" - << endl; - return 1; - } - - args.push_back (argv[i]); - } - } - // -isystem, -iquote, -idirafter, and -framework (Mac OS X) - // - else if (a == "-isystem" || - a == "-iquote" || - a == "-idirafter" || - a == "-framework") - { - args.push_back (a); - - if (++i == argc || argv[i][0] == '\0') - { - e << argv[0] << ": error: expected argument for the " << a - << " option" << endl; - return 1; - } - - args.push_back (argv[i]); - } - // -D - // - else if (n > 1 && a[0] == '-' && a[1] == 'D') - { - args.push_back (a); - - if (n == 2) // -D macro - { - if (++i == argc || argv[i][0] == '\0') - { - e << argv[0] << ": error: expected argument for the -D option" - << endl; - return 1; - } - - args.push_back (argv[i]); - } - } - // -U - // - else if (n > 1 && a[0] == '-' && a[1] == 'U') - { - args.push_back (a); - - if (n == 2) // -U macro - { - if (++i == argc || argv[i][0] == '\0') - { - e << argv[0] << ": error: expected argument for the -U option" - << endl; - return 1; - } - - args.push_back (argv[i]); - } - } - // Store everything else in a list so that we can parse it with the - // cli parser. This is the only reliable way to find out where the - // options end. - // - else - plugin_args.push_back (a); - } - - // Add the default include directories (-I) after the user-supplied - // ones. - // - args.insert (args.end (), def_inc_dirs.begin (), def_inc_dirs.end ()); - - // Find the plugin. - // - { -#ifndef ODB_STATIC_PLUGIN - path plugin (plugin_path (path (argv[0]), args[0])); -#else - // Use a dummy name if the plugin is linked into the compiler. - // - path plugin ("odb"); -#endif - - if (plugin.empty ()) - return 1; // Diagnostics has already been issued. - -#ifdef ODB_BUILD2 -#ifdef _WIN32 - // Here is the problem: since the plugin is loaded by GCC (cc1plus.exe - // to be precise), the DLL assembly magic we have for executables won't - // help here. - // - // To allow executing the ODB compiler in-place we add the odb.exe.dlls/ - // directory to PATH. It is a bit of hack but then DLL assemblies for - // DLLs is whole new level of insanity that we are unlikely to ever - // touch. - // - // And it turns out we have the same problem in the installed case: if - // the installation directory is not in PATH, then GCC won't find the - // DLLs the plugin needs. So we handle both here. - // - { - path d (plugin.directory ()); - d.complete (); - d.normalize (); - d /= path ("odb.exe.dlls"); - - struct stat st; - if (stat (d.string ().c_str (), &st) != 0 || !S_ISDIR (st.st_mode)) - d = d.directory (); - - string v ("PATH=" + d.string ()); - - if (char const* p = getenv ("PATH")) - { - v += ';'; - v += p; - } - - _putenv (v.c_str ()); - } -#endif -#endif - - args[7] = "-fplugin=" + plugin.string (); - } - - // Parse plugin options. We have to do it twice to get the target - // database which is needed while loading profiles. - // - vector<char*> av; - av.push_back (argv[0]); - - for (strings::iterator i (plugin_args.begin ()), end (plugin_args.end ()); - i != end; ++i) - { - av.push_back (const_cast<char*> (i->c_str ())); - } - - int ac (static_cast<int> (av.size ())); - - 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"; - - vector<database> dbs; - bool show_sloc; - size_t sloc_limit; - { - oi[1].search_func = &profile_search_ignore; - oi[2].search_func = &profile_search_ignore; - - cli::argv_file_scanner scan (ac, &av[0], oi, 3); - options ops (scan); - - // Handle --build2-metadata (see also buildfile). - // - if (ops.build2_metadata_specified ()) - { - ostream& o (cout); - - // Note that the export.metadata variable should be the first non- - // blank/comment line. - // - o << "# build2 buildfile odb" << endl - << "export.metadata = 1 odb" << endl - << "odb.name = [string] odb" << endl - << "odb.version = [string] '" << ODB_COMPILER_VERSION_STR << '\'' << endl - << "odb.checksum = [string] '" << ODB_COMPILER_VERSION_STR << '\'' << endl; - - return 0; - } - - // Handle --version. - // - if (ops.version ()) - { - ostream& o (cout); - - o << "ODB object-relational mapping (ORM) compiler for C++ " - ODB_COMPILER_VERSION_STR << endl; - -#ifdef ODB_BUILD2 - o << "Copyright (c) " << ODB_COPYRIGHT << "." << endl; -#endif - - o << "This is free software; see the source for copying conditions. " - << "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS " - << "FOR A PARTICULAR PURPOSE." << endl; - - return 0; - } - - // Handle --help. - // - if (ops.help ()) - { - ostream& o (cout); - - o << "Usage: " << argv[0] << " [options] file [file ...]" << endl - << "Options:" << endl; - - options::print_usage (cout); - return 0; - } - - // Check that required options were specifed. - // - dbs = ops.database (); - - if (dbs.empty ()) - { - e << argv[0] << ": error: no database specified with the --database " - << "option" << endl; - return 1; - } - - if (dbs.size () > 1 && !ops.multi_database_specified ()) - { - e << argv[0] << ": error: --multi-database option required when " << - "multiple databases are specified"<< endl; - return 1; - } - - show_sloc = ops.show_sloc (); - sloc_limit = ops.sloc_limit_specified () ? ops.sloc_limit () : 0; - - // Translate some ODB options to GCC options. - // - switch (ops.std ()) - { - case cxx_version::cxx98: - { - args[3] = "-std=c++98"; - break; - } - case cxx_version::cxx11: - { - args[3] = "-std=c++0x"; // c++11 was only added in GCC 4.7.0. - break; - } - case cxx_version::cxx14: - { - args[3] = "-std=c++1y"; - break; - } - case cxx_version::cxx17: - { - args[3] = "-std=c++1z"; - break; - } - } - } - - // Obtain profile (-I) search paths. - // - paths prof_paths (profile_paths (args, argv[0])); - - if (v) - { - e << "Profile search paths:" << endl; - - for (paths::const_iterator i (prof_paths.begin ()); - i != prof_paths.end (); ++i) - e << " " << *i << endl; - } - - // Pass profile search paths (svc-path option). - // - for (paths::const_iterator i (prof_paths.begin ()); - i != prof_paths.end (); ++i) - { - args.push_back (encode_plugin_option ("svc-path", i->string ())); - } - - // Add common ODB macros. - // - args.push_back ("-DODB_COMPILER"); - - { - ostringstream ostr; - ostr << ODB_COMPILER_VERSION; - args.push_back ("-DODB_COMPILER_VERSION=" + ostr.str ()); - } - - // Compile for each database. - // - size_t sloc_total (0); - - for (vector<database>::iterator i (dbs.begin ()); i != dbs.end (); ++i) - { - database db (*i); - strings db_args (args); - - // Add database-specific ODB macro. - // - db_args.push_back (db_macro[db]); - - // Second parse. - // - profile_data pd (prof_paths, db, argv[0]); - 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 (ac, &av[0], oi, 3); - options ops (scan); - - size_t end (scan.end () - 1); // We have one less in plugin_args. - - if (end == plugin_args.size ()) - { - e << argv[0] << ": error: input file expected" << endl; - return 1; - } - - // Encode plugin options. - // - // Add the database we are compiling for first. More databases - // could be specified in options files but they will be ignored - // by the plugin (it only cares about the first). - // - db_args.push_back (encode_plugin_option ("database", db.string ())); - - cli::options const& desc (options::description ()); - for (size_t i (0); i < end; ++i) - { - string k, a (plugin_args[i]); - - // Ignore certain options. - // - if (a == "--") - { - // Ignore the option seperator since GCC doesn't understand it. - // - continue; - } - else if (a == "-d" || a == "--database") - { - // Ignore all other databases. - // - i++; // Skip the value. - continue; - } - - cli::options::const_iterator it (desc.find (a)); - - if (it == desc.end ()) - { - e << argv[0] << ": ice: unexpected option '" << a << "'" << endl; - return 1; - } - - if (a.size () > 2 && a[0] == '-' && a[1] == '-') - k = string (a, 2); // long format - else - k = string (a, 1); // short format - - if (it->flag ()) - db_args.push_back (encode_plugin_flag (k)); - else - { - // If there are more arguments then we may have a value. - // - if (i + 1 == end) - { - e << argv[0] << ": ice: expected argument for '" << a << "'" - << endl; - return 1; - } - - db_args.push_back (encode_plugin_option (k, plugin_args[++i])); - } - } - - // Reserve space for and remember the position of the svc-file - // option. - // - size_t svc_file_pos (db_args.size ()); - db_args.push_back (""); - - // If compiling multiple input files at once, pass them also with - // the --svc-file option. - // - bool at_once (ops.at_once () && plugin_args.size () - end > 1); - if (at_once) - { - if (ops.input_name ().empty ()) - { - e << "error: --input-name required when compiling multiple " << - "input files at once (--at-once)" << endl; - return 1; - } - - for (size_t i (end); i < plugin_args.size (); ++i) - db_args.push_back ( - encode_plugin_option ("svc-file", plugin_args[i])); - } - - // Create an execvp-compatible argument array. - // - typedef vector<char const*> cstrings; - cstrings exec_args; - - for (strings::const_iterator i (db_args.begin ()), end (db_args.end ()); - i != end; ++i) - { - exec_args.push_back (i->c_str ()); - } - - exec_args.push_back ("-"); // Compile stdin. - exec_args.push_back (0); - - // Iterate over the input files and compile each of them. - // - for (; end < plugin_args.size (); ++end) - { - string name (at_once ? ops.input_name () : plugin_args[end]); - - // Set the --svc-file option. - // - db_args[svc_file_pos] = encode_plugin_option ("svc-file", name); - exec_args[svc_file_pos] = db_args[svc_file_pos].c_str (); - - // - // - ifstream ifs; - - if (!at_once) - { - ifs.open (name.c_str (), ios_base::in | ios_base::binary); - - if (!ifs.is_open ()) - { - e << name << ": error: unable to open in read mode" << endl; - return 1; - } - } - - if (v) - { - e << "Compiling " << name << endl; - for (cstrings::const_iterator i (exec_args.begin ()); - i != exec_args.end (); ++i) - { - if (*i != 0) - e << *i << (*(i + 1) != 0 ? ' ' : '\n'); - } - } - - process_info pi (start_process (&exec_args[0], argv[0], false, true)); - - { - __gnu_cxx::stdio_filebuf<char> fb ( - pi.out_fd, ios_base::out | ios_base::binary); - ostream os (&fb); - - if (!at_once) - { - // See if we there is a UTF-8 BOM in the input file. If so, - // then we need to write it before prologues. - // - if (ifs.peek () == 0xEF) - { - ifs.get (); - if (ifs.get () != 0xBB || ifs.get () != 0xBF) - { - e << name << ": error: invalid UTF-8 BOM sequence" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; - } - - os.put (0xEF); - os.put (0xBB); - os.put (0xBF); - } - } - - if (!ops.trace ()) - { - // Add the standard prologue. - // - os << "#line 1 \"<standard-odb-prologue>\"" << endl; - - // Make sure ODB compiler and libodb versions are compatible. - // - os << "#include <odb/version.hxx>" << endl - << endl - << "#if ODB_VERSION != " << ODB_VERSION << endl - << "# error incompatible ODB compiler and runtime " << - "versions" << endl - << "#endif" << endl - << endl; - - // Include std::string. It is used as a default type for - // the implicit discriminator member in polymorphism - // support. - // - os << "#include <string>" << endl - << endl; - - // Add ODB compiler metaprogramming tests. - // - os << "namespace odb" << endl - << "{" << endl - << "namespace compiler" << endl - << "{" << endl; - - // operator< test, used in validator. - // - os << "template <typename T>" << endl - << "bool" << endl - << "has_lt_operator (const T& x, const T& y)" << endl - << "{" << endl - << "bool r (x < y);" << endl - << "return r;" << endl - << "}" << endl; - - os << "}" << endl - << "}" << endl; - } - - // Add custom prologue if any. - // - // NOTE: if you change the format, you also need to update code - // in include.cxx - // - size_t pro_count (1); - if (ops.odb_prologue ().count (db) != 0) - { - strings const& pro (ops.odb_prologue ()[db]); - for (size_t i (0); i < pro.size (); ++i, ++pro_count) - { - os << "#line 1 \"<odb-prologue-" << pro_count << ">\"" << endl - << pro[i] << endl; - } - } - - if (ops.odb_prologue_file ().count (db) != 0) - { - strings const& prof (ops.odb_prologue_file ()[db]); - for (size_t i (0); i < prof.size (); ++i, ++pro_count) - { - os << "#line 1 \"<odb-prologue-" << pro_count << ">\"" - << endl; - - ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary); - - if (!ifs.is_open ()) - { - e << prof[i] << ": error: unable to open in read mode" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; - } - - if (!(os << ifs.rdbuf ())) - { - e << prof[i] << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; - } - - os << endl; - } - } - - if (at_once) - { - // Include all the input files (no need to escape). - // - os << "#line 1 \"<command-line>\"" << endl; - - bool b (ops.include_with_brackets ()); - char op (b ? '<' : '"'), cl (b ? '>' : '"'); - - for (; end < plugin_args.size (); ++end) - os << "#include " << op << plugin_args[end] << cl << endl; - } - else - { - // Write the synthesized translation unit to stdout. - // - os << "#line 1 \"" << escape_path (name) << "\"" << endl; - - if (!(os << ifs.rdbuf ())) - { - e << name << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; - } - - // Add a new line in case the input file doesn't end with one. - // - os << endl; - } - - // Add custom epilogue if any. - // - // NOTE: if you change the format, you also need to update code - // in include.cxx - // - size_t epi_count (1); - if (ops.odb_epilogue ().count (db) != 0) - { - strings const& epi (ops.odb_epilogue ()[db]); - for (size_t i (0); i < epi.size (); ++i, ++epi_count) - { - os << "#line 1 \"<odb-epilogue-" << epi_count << ">\"" << endl - << epi[i] << endl; - } - } - - if (ops.odb_epilogue_file ().count (db) != 0) - { - strings const& epif (ops.odb_epilogue_file ()[db]); - for (size_t i (0); i < epif.size (); ++i, ++epi_count) - { - os << "#line 1 \"<odb-epilogue-" << epi_count << ">\"" - << endl; - - ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary); - - if (!ifs.is_open ()) - { - e << epif[i] << ": error: unable to open in read mode" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; - } - - if (!(os << ifs.rdbuf ())) - { - e << epif[i] << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; - } - - os << endl; - } - } - - if (!ops.trace ()) - { - // Add the standard epilogue at the end so that we see all - // the declarations. - // - os << "#line 1 \"<standard-odb-epilogue>\"" << endl; - - // Includes for standard smart pointers. The Boost TR1 header - // may or may not delegate to the GCC implementation. In either - // case, the necessary declarations will be provided so we don't - // need to do anything. - // - os << "#include <memory>" << endl; - - // Standard wrapper traits. - // - os << "#include <odb/wrapper-traits.hxx>" << endl; - - // Standard pointer traits. - // - os << "#include <odb/pointer-traits.hxx>" << endl; - - // Standard container traits. - // - os << "#include <odb/container-traits.hxx>" << endl; - - // TR1 wrapper/pointer traits. - // -#ifndef ODB_BUILD2 - if (ops.std () == cxx_version::cxx98) - os << endl - << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl - << "# include <tr1/memory>" << endl - << "#endif" << endl - << "#include <odb/tr1/wrapper-traits.hxx>" << endl - << "#include <odb/tr1/pointer-traits.hxx>" << endl; -#endif - } - } - - // Filter the output stream looking for communication from the - // plugin. - // - { - __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in); - istream is (&fb); - - for (bool first (true); !is.eof (); ) - { - string line; - getline (is, line); - - if (is.fail () && !is.eof ()) - { - e << argv[0] << ": error: io failure while parsing output" - << endl; - wait_process (pi, argv[0]); - return 1; - } - - if (line.compare (0, 9, "odb:sloc:") == 0) - { - if (show_sloc || sloc_limit != 0) - { - size_t n; - istringstream is (string (line, 9, string::npos)); - - if (!(is >> n && is.eof ())) - { - e << argv[0] << ": error: invalid odb:sloc value" << endl; - wait_process (pi, argv[0]); - return 1; - } - - sloc_total += n; - } - - continue; - } - - if (first) - first = false; - else - cout << endl; - - cout << line; - } - } - - if (!wait_process (pi, argv[0])) - return 1; - } // End input file loop. - } // End database loop. - - // Handle SLOC. - // - if (show_sloc) - e << "total: " << sloc_total << endl; - - if (sloc_limit != 0 && sloc_limit < sloc_total) - { - e << argv[0] << ": error: SLOC limit of " << sloc_limit << " lines " << - "has been exceeded" << endl; - - if (!show_sloc) - e << argv[0] << ": info: use the --show-sloc option to see the " - << "current total" << endl; - - return 1; - } - } - catch (profile_failure const&) - { - // Diagnostics has already been issued. - // - return 1; - } - catch (process_failure const&) - { - // Diagnostics has already been issued. - // - return 1; - } - catch (invalid_path const& ex) - { - e << argv[0] << ": error: invalid path '" << ex.path () << "'" << endl; - return 1; - } - catch (cli::exception const& ex) - { - e << ex << endl; - return 1; - } -} - -static inline string -encode_plugin_flag (string const& k) -{ - return "-fplugin-arg-odb-" + k; -} - -static string -encode_plugin_option (string const& k, string const& cv) -{ - string o ("-fplugin-arg-odb-"); - o += k; - o += '='; - - if (!cv.empty ()) - { - // A value cannot contain '='. Encode it as the backspace - // character. - // - string v (cv); - for (size_t i (0); i < v.size (); ++i) - if (v[i] == '=') - v[i] = '\b'; - - o += v; - } - - return o; -} - -static paths -profile_paths (strings const& sargs, char const* name) -{ - // Copy some of the arguments from the passed list. We also need - // the g++ executable. - // - strings args; - - args.push_back (sargs[0]); - args.push_back ("-v"); - args.push_back ("-x"); - args.push_back ("c++"); - args.push_back ("-E"); - args.push_back ("-P"); - - for (strings::const_iterator i (++sargs.begin ()), end (sargs.end ()); - i != end; ++i) - { - string const& a (*i); - - // -I - // - if (a.size () > 1 && a[0] == '-' && a[1] == 'I') - { - args.push_back (a); - - if (a.size () == 2) // -I /path - { - args.push_back (*(++i)); - } - } - // -framework - // - else if (a == "-isystem" || - a == "-iquote" || - a == "-idirafter" || - a == "-isysroot" || - a == "-framework") - { - args.push_back (a); - - if (++i == end) - { - cerr << name << ": error: expected argument for the " << a - << " option" << endl; - throw profile_failure (); - } - - args.push_back (*i); - } - // --sysroot - // - else if (a.compare (0, 10, "--sysroot=") == 0) - args.push_back (a); - // -std - // - else if (a.compare (0, 5, "-std=") == 0) - args.push_back (a); - } - - // Create an execvp-compatible argument array. - // - vector<char const*> exec_args; - - for (strings::const_iterator i (args.begin ()), end (args.end ()); - i != end; ++i) - { - exec_args.push_back (i->c_str ()); - } - - exec_args.push_back ("-"); // Compile stdin. - exec_args.push_back (0); - - process_info pi (start_process (&exec_args[0], name, true)); - close (pi.out_fd); // Preprocess empty file. - - // Read the output into a temporary string stream. We don't parse - // it on the fly because we don't know whether it is the data or - // diagnostics until after the process is terminated and we get - // the exit code. We also cannot first wait for the exist code - // and then read the output because the process might get blocked. - // - stringstream ss; - { - __gnu_cxx::stdio_filebuf<char> fb (pi.in_efd, ios_base::in); - istream is (&fb); - - for (bool first (true); !is.eof (); ) - { - string line; - getline (is, line); - - if (is.fail () && !is.eof ()) - { - cerr << name << ": error: " - << "io failure while parsing profile paths" << endl; - - wait_process (pi, name); - throw profile_failure (); - } - - if (first) - first = false; - else - ss << endl; - - ss << line; - } - } - - if (!wait_process (pi, name)) - { - // Things didn't go well and ss should contain the diagnostics. - // In case it is empty, issue our own. - // - if (!ss.str ().empty ()) - cerr << ss.rdbuf (); - else - cerr << name << ": error: unable to extract profile paths" << endl; - - throw profile_failure (); - } - - // Parse the cached output. - // - paths r; - { - enum - { - read_prefix, - read_path, - read_suffix - } state = read_prefix; - - while (!ss.eof () && state != read_suffix) - { - string line; - getline (ss, line); - - if (ss.fail () && !ss.eof ()) - { - cerr << name << ": error: " - << "io failure while parsing profile paths" << endl; - throw profile_failure (); - } - - switch (state) - { - case read_prefix: - { - // The English string that we are looking for is "#include <...> - // search starts here:" but it can be translated. However, all - // the translations seems to have the "#include" and "<...>" - // parts, so we can search for those. - // - if (line.find ("#include") != string::npos && - line.find ("<...>") != string::npos) - state = read_path; - break; - } - case read_path: - { - // The end of the list is terminated with the "End of search - // list." line, which, again, can be translated. Here we don't - // have any invariable parts that we can use. Instead, we will - // rely on the fact that all the paths are space-indented. - // - if (!line.empty () && line[0] != ' ') - state = read_suffix; - else - // Paths are indented with a space. - // - r.push_back (path (string (line, 1))); - - break; - } - case read_suffix: - { - // We shouldn't get here. - break; - } - } - } - - if (state != read_suffix) - { - cerr << name << ": error: unable to parse profile paths" << endl; - throw profile_failure (); - } - } - - return r; -} - -// -// Path manipulation. -// - -static string -escape_path (string const& p) -{ - string r; - - for (size_t i (0); i < p.size (); ++i) - { - if (p[i] == '\\') - r += "\\\\"; - else - r += p[i]; - } - - return r; -} - -static path -path_search (path const& f) -{ - typedef path::traits traits; - - // If there is a directory component in the file, then the PATH - // search does not apply. - // - if (!f.directory ().empty ()) - return f; - - string paths; - - // If there is no PATH in environment then the default search - // path is the current directory. - // - if (char const* s = getenv ("PATH")) - paths = s; - else - paths = traits::path_separator; - - // On Windows also check the current directory. - // -#ifdef _WIN32 - paths += traits::path_separator; -#endif - - struct stat info; - - for (size_t b (0), e (paths.find (traits::path_separator)); - b != string::npos;) - { - path p (string (paths, b, e != string::npos ? e - b : e)); - - // Empty path (i.e., a double colon or a colon at the beginning - // or end of PATH) means search in the current dirrectory. - // - if (p.empty ()) - p = path ("."); - - path dp (p / f); - - // Just check that the file exist without checking for permissions, etc. - // - if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode)) - return dp; - - // On Windows also try the path with the .exe extension. - // -#ifdef _WIN32 - dp += ".exe"; - - if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode)) - return dp; -#endif - - if (e == string::npos) - b = e; - else - { - b = e + 1; - e = paths.find (traits::path_separator, b); - } - } - - return path (); -} - -static path -driver_path (path const& drv) -{ - return drv.directory ().empty () ? path_search (drv) : drv; -} - -#ifndef ODB_STATIC_PLUGIN -static path -plugin_path (path const& drv, -#ifdef ODB_GCC_PLUGIN_DIR - string const& gxx) -#else - string const&) -#endif -{ -#ifdef _WIN32 - char const plugin_ext[] = ".dll"; - -// While GCC 8 switched to using .dylib as the plugin extension, there is a -// bug in the extension stripping code. So for now we use the .so extension -// everywhere (see also buildfile if changing this). -// -//#elif defined(__APPLE__) && defined(ODB_BUILD2) -// char const plugin_ext[] = ".dylib"; -#else - char const plugin_ext[] = ".so"; -#endif - - // Figure out the plugin base name which is just the driver name (but - // without the .exe extension on Windows). If the driver name starts with - // 'lt-', then we are running through the libtool script. Strip this prefix - // -- the shared object should be in the same directory. - // -#ifdef _WIN32 - string b (drv.leaf ().base ().string ()); -#else - string b (drv.leaf ().string ()); -#endif - - bool lt (b.size () > 3 && b[0] == 'l' && b[1] == 't' && b[2] == '-'); - if (lt) - b = string (b, 3, string::npos); - - path dp (driver_path (drv)); - - if (dp.empty ()) - { - cerr << drv << ": error: unable to resolve ODB driver path" << endl; - return path (); - } - - dp = dp.directory (); - struct stat info; - - // Regardless of whether we were given a plugin path, first try - // the current directory for the .la file. This will make sure - // running ODB from the build directory works as expected. - // - // @@ BUILD2: not going to work for build2 build. - // - path pp (dp / path (b + ".la")); - if (stat (pp.string ().c_str (), &info) == 0) - { - pp = dp / path (".libs") / path (b + ".so"); - if (stat (pp.string ().c_str (), &info) == 0) - return pp; - } - -#ifdef ODB_GCC_PLUGIN_DIR - // Plugin should be installed into the GCC default plugin directory. - // Ideally, in this situation, we would simply pass the plugin name and - // let GCC append the correct directory. Unfortunately, this mechanism - // was only added in GCC 4.6 so in order to support 4.5 we will have to - // emulate it ourselves. - // - if (!lt) - { - //@@ BUILD2: if/when dropping old GCC should just get rid of this. -#if 1 - // First get the default GCC plugin directory. - // - path d; - vector<char const*> exec_args; - exec_args.push_back (gxx.c_str ()); - exec_args.push_back ("-print-file-name=plugin"); - exec_args.push_back (0); - - process_info pi ( - start_process ( - &exec_args[0], drv.string ().c_str (), false, true)); - close (pi.out_fd); - - // Read the path from stdout. - // - { - __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in); - istream is (&fb); - string line; - getline (is, line); - d = path (line); - } - - if (!wait_process (pi, drv.string ().c_str ())) - return path (); // Assume GCC issued some diagnostics. - - if (d.string () == "plugin") - { - cerr << drv << ": error: unable to obtain GCC plugin directory" << endl; - return path (); - } - - // See if the plugin is there. - // - pp = d / path (b + plugin_ext); - if (stat (pp.string ().c_str (), &info) != 0) - { - cerr << drv << ": error: no ODB plugin in GCC plugin directory '" << - d << "'" << endl; - return path (); - } - - return pp; -#else - return path (b); -#endif - } -#elif defined (ODB_PLUGIN_PATH) - // If we were given a plugin path, use that unless we are running - // via libtool. - // - if (!lt) - { - string rp (ODB_PLUGIN_PATH); - if (!rp.empty ()) - dp /= path (rp); - - pp = dp / path (b + plugin_ext); - - if (stat (pp.string ().c_str (), &info) != 0) - { - cerr << drv << ": error: no ODB plugin in '" << dp << "'" << endl; - return path (); - } - - return pp; - } -#endif - - // Try in the current directory. - // - pp = dp / path (b + plugin_ext); - if (stat (pp.string ().c_str (), &info) != 0) - { - cerr << drv << ": error: unable to locate ODB plugin" << endl; - return path (); - } - - return pp; -} -#endif - -// -// Process manipulation. -// - -#ifndef _WIN32 - -static process_info -start_process (char const* args[], char const* name, bool err, bool out) -{ - int out_fd[2]; - int in_efd[2]; - int in_ofd[2]; - - if (pipe (out_fd) == -1 || - (err && pipe (in_efd) == -1) || - (out && pipe (in_ofd) == -1)) - { - char const* err (strerror (errno)); - cerr << name << ": error: " << err << endl; - throw process_failure (); - } - - pid_t pid (fork ()); - - if (pid == -1) - { - char const* err (strerror (errno)); - cerr << name << ": error: " << err << endl; - throw process_failure (); - } - - if (pid == 0) - { - // Child. Close the write end of the pipe and duplicate the read end - // to stdin. Then close the original read end descriptors. - // - if (close (out_fd[1]) == -1 || - dup2 (out_fd[0], STDIN_FILENO) == -1 || - close (out_fd[0]) == -1) - { - char const* err (strerror (errno)); - cerr << name << ": error: " << err << endl; - throw process_failure (); - } - - // Do the same for the stderr if requested. - // - if (err) - { - if (close (in_efd[0]) == -1 || - dup2 (in_efd[1], STDERR_FILENO) == -1 || - close (in_efd[1]) == -1) - { - char const* err (strerror (errno)); - cerr << name << ": error: " << err << endl; - throw process_failure (); - } - } - - // Do the same for the stdout if requested. - // - if (out) - { - if (close (in_ofd[0]) == -1 || - dup2 (in_ofd[1], STDOUT_FILENO) == -1 || - close (in_ofd[1]) == -1) - { - char const* err (strerror (errno)); - cerr << name << ": error: " << err << endl; - throw process_failure (); - } - } - - if (execvp (args[0], const_cast<char**> (&args[0])) == -1) - { - char const* err (strerror (errno)); - cerr << args[0] << ": error: " << err << endl; - throw process_failure (); - } - } - else - { - // Parent. Close the other ends of the pipes. - // - if (close (out_fd[0]) == -1 || - (err && close (in_efd[1]) == -1) || - (out && close (in_ofd[1]) == -1)) - { - char const* err (strerror (errno)); - cerr << name << ": error: " << err << endl; - throw process_failure (); - } - } - - process_info r; - r.id = pid; - r.out_fd = out_fd[1]; - r.in_efd = err ? in_efd[0] : 0; - r.in_ofd = out ? in_ofd[0] : 0; - return r; -} - -static bool -wait_process (process_info pi, char const* name) -{ - int status; - - if (waitpid (pi.id, &status, 0) == -1) - { - char const* err (strerror (errno)); - cerr << name << ": error: " << err << endl; - throw process_failure (); - } - - return WIFEXITED (status) && WEXITSTATUS (status) == 0; -} - -#else // _WIN32 - -static void -print_error (char const* name) -{ - LPTSTR msg; - DWORD e (GetLastError()); - - if (!FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - 0, - e, - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &msg, - 0, - 0)) - { - cerr << name << ": error: unknown error code " << e << endl; - return; - } - - cerr << name << ": error: " << msg << endl; - LocalFree (msg); -} - -static process_info -start_process (char const* args[], char const* name, bool err, bool out) -{ - HANDLE out_h[2]; - HANDLE in_eh[2]; - HANDLE in_oh[2]; - SECURITY_ATTRIBUTES sa; - - sa.nLength = sizeof (SECURITY_ATTRIBUTES); - sa.bInheritHandle = true; - sa.lpSecurityDescriptor = 0; - - if (!CreatePipe (&out_h[0], &out_h[1], &sa, 0) || - !SetHandleInformation (out_h[1], HANDLE_FLAG_INHERIT, 0)) - { - print_error (name); - throw process_failure (); - } - - if (err) - { - if (!CreatePipe (&in_eh[0], &in_eh[1], &sa, 0) || - !SetHandleInformation (in_eh[0], HANDLE_FLAG_INHERIT, 0)) - { - print_error (name); - throw process_failure (); - } - } - - if (out) - { - if (!CreatePipe (&in_oh[0], &in_oh[1], &sa, 0) || - !SetHandleInformation (in_oh[0], HANDLE_FLAG_INHERIT, 0)) - { - print_error (name); - throw process_failure (); - } - } - - // Create the process. - // - path file (args[0]); - - // Do PATH search. - // - if (file.directory ().empty ()) - file = path_search (file); - else if (file.base () == file) // No extension - file += ".exe"; // Assume .exe. - - if (file.empty ()) - { - cerr << args[0] << ": error: file not found" << endl; - throw process_failure (); - } - - // Serialize the arguments to string. - // - string cmd_line; - - for (char const** p (args); *p != 0; ++p) - { - if (p != args) - cmd_line += ' '; - - // On Windows we need to protect values with spaces using quotes. - // Since there could be actual quotes in the value, we need to - // escape them. - // - string a (*p); - bool quote (a.find (' ') != string::npos); - - if (quote) - cmd_line += '"'; - - for (size_t i (0); i < a.size (); ++i) - { - if (a[i] == '"') - cmd_line += "\\\""; - else - cmd_line += a[i]; - } - - if (quote) - cmd_line += '"'; - } - - // Prepare other info. - // - STARTUPINFO si; - PROCESS_INFORMATION pi; - - memset (&si, 0, sizeof (STARTUPINFO)); - memset (&pi, 0, sizeof (PROCESS_INFORMATION)); - - si.cb = sizeof(STARTUPINFO); - - if (err) - si.hStdError = in_eh[1]; - else - si.hStdError = GetStdHandle (STD_ERROR_HANDLE); - - if (out) - si.hStdOutput = in_oh[1]; - else - si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); - - si.hStdInput = out_h[0]; - si.dwFlags |= STARTF_USESTDHANDLES; - - if (!CreateProcess ( - file.string ().c_str (), - const_cast<char*> (cmd_line.c_str ()), - 0, // Process security attributes. - 0, // Primary thread security attributes. - true, // Inherit handles. - 0, // Creation flags. - 0, // Use our environment. - 0, // Use our current directory. - &si, - &pi)) - { - print_error (name); - throw process_failure (); - } - - CloseHandle (pi.hThread); - CloseHandle (out_h[0]); - - if (err) - CloseHandle (in_eh[1]); - - if (out) - CloseHandle (in_oh[1]); - - process_info r; - r.id = pi.hProcess; - r.out_fd = _open_osfhandle ((intptr_t) (out_h[1]), 0); - - if (r.out_fd == -1) - { - cerr << name << ": error: unable to obtain C file handle" << endl; - throw process_failure (); - } - - if (err) - { - // Pass _O_TEXT to get newline translation. - // - r.in_efd = _open_osfhandle ((intptr_t) (in_eh[0]), _O_TEXT); - - if (r.in_efd == -1) - { - cerr << name << ": error: unable to obtain C file handle" << endl; - throw process_failure (); - } - } - else - r.in_efd = 0; - - if (out) - { - // Pass _O_TEXT to get newline translation. - // - r.in_ofd = _open_osfhandle ((intptr_t) (in_oh[0]), _O_TEXT); - - if (r.in_ofd == -1) - { - cerr << name << ": error: unable to obtain C file handle" << endl; - throw process_failure (); - } - } - else - r.in_ofd = 0; - - return r; -} - -static bool -wait_process (process_info pi, char const* name) -{ - DWORD status; - - if (WaitForSingleObject (pi.id, INFINITE) != WAIT_OBJECT_0 || - !GetExitCodeProcess (pi.id, &status)) - { - print_error (name); - throw process_failure (); - } - - CloseHandle (pi.id); - return status == 0; -} - -#endif // _WIN32 |