From f18e7bfae2e4dbf28e45da176e3836232484dd92 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 20 Jan 2011 16:02:41 +0200 Subject: Implement support for profiles New option: --profile/-p. --- odb/odb.cxx | 321 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 286 insertions(+), 35 deletions(-) (limited to 'odb/odb.cxx') diff --git a/odb/odb.cxx b/odb/odb.cxx index 9438bd0..6fcb2fc 100644 --- a/odb/odb.cxx +++ b/odb/odb.cxx @@ -6,7 +6,7 @@ #include #include // getenv, setenv #include // strerror, memset -#include // stat +#include // stat, close #include // stat #include // stat @@ -45,6 +45,9 @@ using namespace std; using cutl::fs::path; using cutl::fs::invalid_path; +typedef vector strings; +typedef vector paths; + // // Path manipulation. // @@ -78,23 +81,25 @@ struct process_info HANDLE id; #endif - int fd; + int out_fd; + int in_fd; }; struct process_failure {}; // Start another process using the specified command line. Connect the -// newly created process stdin to our stdout. Issue diagnostics and -// throw process_failure if anything goes wrong. The name argument -// is the name of the current process for diagnostics. +// newly created process' stdin to out_fd. Also if connect_out is 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); +start_process (char const* args[], char const* name, bool connect_out = false); -// Close stdout and 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. +// 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); @@ -104,6 +109,27 @@ wait_process (process_info, char const* name); 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. +// +struct profile_failure {}; + +static paths +profile_paths (strings const& args, char const* name); + +// Search for the profile options file. +// +struct profile_data +{ + profile_data (paths const& p, char const* n): search_paths (p), name (n) {} + + paths const& search_paths; + char const* name; +}; + +static string +profile_search (char const* profile, void* arg); static char const* const db_macro[] = { @@ -118,8 +144,6 @@ main (int argc, char* argv[]) try { - typedef vector strings; - // Find the plugin. It should be in the same directory as the // driver. // @@ -315,6 +339,19 @@ main (int argc, char* argv[]) plugin_args.push_back (a); } + // 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; + } + // Parse plugin options. // vector av; @@ -327,7 +364,19 @@ main (int argc, char* argv[]) } int ac (static_cast (av.size ())); - cli::argv_file_scanner scan (ac, &av[0], "--options-file"); + + profile_data pd (prof_paths, argv[0]); + cli::argv_file_scanner::option_info oi[3]; + oi[0].option = "--options-file"; + oi[0].search_func = 0; + oi[1].option = "-p"; + oi[1].search_func = &profile_search; + oi[1].arg = &pd; + oi[2].option = "--profile"; + oi[2].search_func = &profile_search; + oi[2].arg = &pd; + + cli::argv_file_scanner scan (ac, &av[0], oi, 3); options ops (scan); @@ -487,7 +536,7 @@ main (int argc, char* argv[]) { __gnu_cxx::stdio_filebuf fb ( - pi.fd, ios_base::out | ios_base::binary); + pi.out_fd, ios_base::out | ios_base::binary); ostream os (&fb); // Add the standard prologue. @@ -558,6 +607,12 @@ main (int argc, char* argv[]) return 1; } } + catch (profile_failure const&) + { + // Diagnostics has already been issued. + // + return 1; + } catch (process_failure const&) { // Diagnostics has already been issued. @@ -611,6 +666,154 @@ encode_plugin_option (string const& k, string const& cv) return o; } +static paths +profile_paths (strings const& sargs, char const* name) +{ + // Copy some of the arguments from the passed list. We only need + // the g++ executable and the -I options. + // + 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); + + if (a.size () > 1 && a[0] == '-' && a[1] == 'I') + { + args.push_back (a); + + if (a.size () == 2) // -I /path + { + ++i; + args.push_back (*i); + } + } + } + + // Create an execvp-compatible argument array. + // + vector 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 and parse the output. + // + paths r; + { + __gnu_cxx::stdio_filebuf fb (pi.in_fd, ios_base::in); + istream is (&fb); + + enum + { + read_prefix, + read_path, + read_suffix + } state = read_prefix; + + while (!is.eof ()) + { + string line; + getline (is, line); + + if (is.fail () && !is.eof ()) + { + cerr << name << ": error: " + << "io failure while parsing profile paths" << endl; + throw profile_failure (); + } + + switch (state) + { + case read_prefix: + { + if (line == "#include <...> search starts here:") + state = read_path; + break; + } + case read_path: + { + if (line == "End of search list.") + state = read_suffix; + else + // Paths are indented with a space. + // + r.push_back (path (string (line, 1))); + + break; + } + case read_suffix: + { + // Keep reading until eof to make sure the process is not + // blocked. + break; + } + } + } + + if (state != read_suffix) + { + cerr << name << ": error: unable to parse profile paths" << endl; + throw profile_failure (); + } + } + + wait_process (pi, name); + return r; +} + +static string +profile_search (char const* prof, void* arg) +{ + profile_data* pd (static_cast (arg)); + paths const& ps (pd->search_paths); + + path p (prof), odb ("odb"), r; + p.normalize (); // Convert '/' to the canonical path separator form. + p += ".options"; + + struct stat info; + + for (paths::const_iterator i (ps.begin ()), end (ps.end ()); i != end; ++i) + { + // First check in the search directory itself and then try the odb/ + // subdirectory. + // + r = *i / p; + + // Just check that the file exist without checking for permissions, etc. + // + if (stat (r.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode)) + return r.string (); + + r = *i / odb / p; + + if (stat (r.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode)) + return r.string (); + } + + cerr << pd->name << ": error: unable to locate options file for profile '" + << prof << "'" << endl; + throw profile_failure (); +} + // // Path manipulation. // @@ -750,11 +953,12 @@ plugin_path (path const& drv) #ifndef _WIN32 static process_info -start_process (char const* args[], char const* name) +start_process (char const* args[], char const* name, bool out) { - int fd[2]; + int out_fd[2]; + int in_fd[2]; - if (pipe (fd) == -1) + if (pipe (out_fd) == -1 || (out && pipe (in_fd) == -1)) { char const* err (strerror (errno)); cerr << name << ": error: " << err << endl; @@ -775,15 +979,30 @@ start_process (char const* args[], char const* name) // Child. Close the write end of the pipe and duplicate the read end // to stdin. Then close the original read end descriptors. // - if (close (fd[1]) == -1 || - dup2 (fd[0], STDIN_FILENO) == -1 || - close (fd[0]) == -1) + 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 out if requested. + // + if (out) + { + if (close (in_fd[0]) == -1 || + dup2 (in_fd[1], STDOUT_FILENO) == -1 || + dup2 (in_fd[1], STDERR_FILENO) == -1 || + close (in_fd[1]) == -1) + { + char const* err (strerror (errno)); + cerr << name << ": error: " << err << endl; + throw process_failure (); + } + } + if (execvp (args[0], const_cast (&args[0])) == -1) { char const* err (strerror (errno)); @@ -793,9 +1012,9 @@ start_process (char const* args[], char const* name) } else { - // Parent. Close the read end of the pipe and. + // Parent. Close the other ends of the pipes. // - if (close (fd[0]) == -1) + if (close (out_fd[0]) == -1 || (out && close (in_fd[1]) == -1)) { char const* err (strerror (errno)); cerr << name << ": error: " << err << endl; @@ -805,7 +1024,8 @@ start_process (char const* args[], char const* name) process_info r; r.id = pid; - r.fd = fd[1]; + r.out_fd = out_fd[1]; + r.in_fd = out ? in_fd[0] : 0; return r; } @@ -852,22 +1072,33 @@ print_error (char const* name) } static process_info -start_process (char const* args[], char const* name) +start_process (char const* args[], char const* name, bool out) { - HANDLE in, out; + HANDLE out_h[2]; + HANDLE in_h[2]; SECURITY_ATTRIBUTES sa; sa.nLength = sizeof (SECURITY_ATTRIBUTES); sa.bInheritHandle = true; sa.lpSecurityDescriptor = 0; - if (!CreatePipe (&in, &out, &sa, 0) || - !SetHandleInformation (out, HANDLE_FLAG_INHERIT, 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 (out) + { + if (!CreatePipe (&in_h[0], &in_h[1], &sa, 0) || + !SetHandleInformation (in_h[0], HANDLE_FLAG_INHERIT, 0)) + { + print_error (name); + throw process_failure (); + } + } + // Create the process. // path file (args[0]); @@ -903,9 +1134,17 @@ start_process (char const* args[], char const* name) memset (&pi, 0, sizeof (PROCESS_INFORMATION)); si.cb = sizeof(STARTUPINFO); - si.hStdError = GetStdHandle (STD_ERROR_HANDLE); - si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); - si.hStdInput = in; + if (out) + { + si.hStdError = in_h[1]; + si.hStdOutput = in_h[1]; + } + else + { + si.hStdError = GetStdHandle (STD_ERROR_HANDLE); + si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); + } + si.hStdInput = out_h[0]; si.dwFlags |= STARTF_USESTDHANDLES; if (!CreateProcess ( @@ -926,17 +1165,29 @@ start_process (char const* args[], char const* name) CloseHandle (pi.hThread); - int fd (_open_osfhandle ((intptr_t) (out), 0)); + process_info r; + r.id = pi.hProcess; + r.out_fd = _open_osfhandle ((intptr_t) (out_h[1]), 0); - if (fd == -1) + if (r.out_fd == -1) { cerr << name << ": error: unable to obtain C file handle" << endl; throw process_failure (); } - process_info r; - r.id = pi.hProcess; - r.fd = fd; + if (out) + { + r.in_fd = _open_osfhandle ((intptr_t) (in_h[0]), 0); + + if (r.in_fd == -1) + { + cerr << name << ": error: unable to obtain C file handle" << endl; + throw process_failure (); + } + } + else + r.in_fd = 0; + return r; } -- cgit v1.1