diff options
Diffstat (limited to 'odb/odb.cxx')
-rw-r--r-- | odb/odb.cxx | 270 |
1 files changed, 219 insertions, 51 deletions
diff --git a/odb/odb.cxx b/odb/odb.cxx index 5381668..701f6e1 100644 --- a/odb/odb.cxx +++ b/odb/odb.cxx @@ -18,7 +18,7 @@ # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include <windows.h> // CreatePipe, CreateProcess +# include <windows.h> // CreatePipe, CreateProcess, GetTemp*, MAX_PATH # include <io.h> // _open_osfhandle # include <fcntl.h> // _O_TEXT #endif @@ -31,7 +31,8 @@ #include <iostream> #include <ext/stdio_filebuf.h> -#include <cutl/fs/path.hxx> +#include <libcutl/fs/path.hxx> +#include <libcutl/fs/auto-remove.hxx> #include <odb/version.hxx> #include <odb/options.hxx> @@ -44,6 +45,7 @@ using namespace std; using cutl::fs::path; using cutl::fs::invalid_path; +using cutl::fs::auto_remove; typedef vector<string> strings; typedef vector<path> paths; @@ -90,6 +92,16 @@ struct process_info struct process_failure {}; +#ifdef _WIN32 +// Deal with Windows command line length limit. +// +static auto_remove +fixup_cmd_line (vector<const char*>& args, + size_t start, + const char* name, + string& arg); +#endif + // 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 @@ -259,9 +271,7 @@ main (int argc, char* argv[]) 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"); + cli::argv_file_scanner s (file.string ()); bool first_x (true); @@ -381,11 +391,15 @@ main (int argc, char* argv[]) // Parse driver options. // + // We scan expanding --options-file in order to allow specifying ad hoc + // options (-I, etc) in options files. + // bool first_x (true); - for (int i = 1; i < argc; ++i) + for (cli::argv_file_scanner scan (argc, argv, "--options-file"); + scan.more (); ) { - string a (argv[i]); + string a (scan.next ()); size_t n (a.size ()); // -v @@ -399,14 +413,14 @@ main (int argc, char* argv[]) // else if (a == "-x") { - if (++i == argc || argv[i][0] == '\0') + const char* v; + if (!scan.more () || (v = scan.next ())[0] == '\0') { - e << argv[0] << ": error: expected argument for the -x option" << endl; + e << argv[0] << ": error: expected argument for the -x option" + << endl; return 1; } - a = argv[i]; - if (first_x) { first_x = false; @@ -414,13 +428,13 @@ main (int argc, char* argv[]) // 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; + if (v[0] != '-') + args[0] = v; else - args.push_back (a); + args.push_back (v); } else - args.push_back (a); + args.push_back (v); } // -I // @@ -430,14 +444,15 @@ main (int argc, char* argv[]) if (n == 2) // -I /path { - if (++i == argc || argv[i][0] == '\0') + const char* v; + if (!scan.more () || (v = scan.next ())[0] == '\0') { e << argv[0] << ": error: expected argument for the -I option" << endl; return 1; } - args.push_back (argv[i]); + args.push_back (v); } } // -isystem, -iquote, -idirafter, and -framework (Mac OS X) @@ -449,14 +464,15 @@ main (int argc, char* argv[]) { args.push_back (a); - if (++i == argc || argv[i][0] == '\0') + const char* v; + if (!scan.more () || (v = scan.next ())[0] == '\0') { e << argv[0] << ": error: expected argument for the " << a << " option" << endl; return 1; } - args.push_back (argv[i]); + args.push_back (v); } // -D // @@ -466,14 +482,15 @@ main (int argc, char* argv[]) if (n == 2) // -D macro { - if (++i == argc || argv[i][0] == '\0') + const char* v; + if (!scan.more () || (v = scan.next ())[0] == '\0') { e << argv[0] << ": error: expected argument for the -D option" << endl; return 1; } - args.push_back (argv[i]); + args.push_back (v); } } // -U @@ -484,14 +501,15 @@ main (int argc, char* argv[]) if (n == 2) // -U macro { - if (++i == argc || argv[i][0] == '\0') + const char* v; + if (!scan.more () || (v = scan.next ())[0] == '\0') { e << argv[0] << ": error: expected argument for the -U option" << endl; return 1; } - args.push_back (argv[i]); + args.push_back (v); } } // Store everything else in a list so that we can parse it with the @@ -577,7 +595,7 @@ main (int argc, char* argv[]) int ac (static_cast<int> (av.size ())); cli::argv_file_scanner::option_info oi[3]; - oi[0].option = "--options-file"; + oi[0].option = "--options-file"; // Keep in case profile uses it. oi[0].search_func = 0; oi[1].option = "-p"; oi[2].option = "--profile"; @@ -592,20 +610,41 @@ main (int argc, char* argv[]) 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 + << "odb.environment = [strings] CPATH CPLUS_INCLUDE_PATH GCC_EXEC_PREFIX COMPILER_PATH" << endl; + + return 0; + } + // Handle --version. // if (ops.version ()) { - cout << "ODB object-relational mapping (ORM) compiler for C++ " + ostream& o (cout); + + o << "ODB object-relational mapping (ORM) compiler for C++ " ODB_COMPILER_VERSION_STR << endl; #ifdef ODB_BUILD2 - cout << "Copyright (c) " << ODB_COPYRIGHT << "." << endl; + o << "Copyright (c) " << ODB_COPYRIGHT << "." << endl; #endif - cout << "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; + 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; } @@ -614,8 +653,10 @@ main (int argc, char* argv[]) // if (ops.help ()) { - cout << "Usage: " << argv[0] << " [options] file [file ...]" << endl - << "Options:" << endl; + ostream& o (cout); + + o << "Usage: " << argv[0] << " [options] file [file ...]" << endl + << "Options:" << endl; options::print_usage (cout); return 0; @@ -666,6 +707,11 @@ main (int argc, char* argv[]) args[3] = "-std=c++1z"; break; } + case cxx_version::cxx20: + { + args[3] = "-std=c++2a"; + break; + } } } @@ -866,6 +912,14 @@ main (int argc, char* argv[]) } } + // Deal with Windows command line length limit. + // +#ifdef _WIN32 + string ops_file_arg; + auto_remove opt_file_rm ( + fixup_cmd_line (exec_args, 1, argv[0], ops_file_arg)); +#endif + process_info pi (start_process (&exec_args[0], argv[0], false, true)); { @@ -1299,6 +1353,11 @@ profile_paths (strings const& sargs, char const* name) exec_args.push_back ("-"); // Compile stdin. exec_args.push_back (0); +#ifdef _WIN32 + string ops_file_arg; + auto_remove opt_file_rm (fixup_cmd_line (exec_args, 1, name, ops_file_arg)); +#endif + process_info pi (start_process (&exec_args[0], name, true)); close (pi.out_fd); // Preprocess empty file. @@ -1813,6 +1872,134 @@ print_error (char const* name) LocalFree (msg); } +// On Windows we need to protect command line arguments with spaces using +// quotes. Since there could be actual quotes in the value, we need to escape +// them. +// +static void +append_quoted (string& cmd_line, const char* ca) +{ + string a (ca); + 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 += '"'; +} + +// Deal with Windows command line length limit. +// +// The best approach seems to be passing the command line in an "options file" +// ("response file" in Microsoft's terminology). +// +static auto_remove +fixup_cmd_line (vector<const char*>& args, + size_t start, + const char* name, + string& arg) +{ + // Calculate the would-be command line length similar to how start_process() + // implementation does it. + // + size_t n (0); + string s; + for (const char* a: args) + { + if (a != nullptr) + { + if (n != 0) + n++; // For the space separator. + + s.clear (); + append_quoted (s, a); + n += s.size (); + } + } + + if (n <= 32766) // 32768 - "Unicode terminating null character". + return auto_remove (); + + // Create the temporary file. + // + char d[MAX_PATH + 1], p[MAX_PATH + 1]; + if (GetTempPathA (sizeof (d), d) == 0 || + GetTempFileNameA (d, "odb-options-", 0, p) == 0) + { + print_error (name); + throw process_failure (); + } + + auto_remove rm = auto_remove (path (p)); + try + { + ofstream ofs (p); + if (!ofs.is_open ()) + { + cerr << name << ": error: unable to open '" << p << "' in write mode" + << endl; + throw process_failure (); + } + + ofs.exceptions (ios_base::badbit | ios_base::failbit); + + // Write the arguments to file. + // + // The format is a space-separated list of potentially-quoted arguments + // with support for backslash-escaping. + // + string b; + for (size_t i (start), n (args.size () - 1); i != n; ++i) + { + const char* a (args[i]); + + // We will most likely have backslashes so just do it. + // + { + for (b.clear (); *a != '\0'; ++a) + { + if (*a != '\\') + b += *a; + else + b += "\\\\"; + } + + a = b.c_str (); + } + + s.clear (); + append_quoted (s, a); + ofs << (i != start ? " " : "") << s; + } + + ofs << '\n'; + ofs.close (); + } + catch (const ios_base::failure&) + { + cerr << name << ": error: unable to write to '" << p << "'" << endl; + throw process_failure (); + } + + // Rewrite the command line. + // + arg = string ("@") + p; + args.resize (start); + args.push_back (arg.c_str()); + args.push_back (nullptr); + + return rm; +} + static process_info start_process (char const* args[], char const* name, bool err, bool out) { @@ -1878,26 +2065,7 @@ start_process (char const* args[], char const* name, bool err, bool out) 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 += '"'; + append_quoted (cmd_line, *p); } // Prepare other info. |