aboutsummaryrefslogtreecommitdiff
path: root/odb/odb.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb.cxx')
-rw-r--r--odb/odb.cxx270
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.