aboutsummaryrefslogtreecommitdiff
path: root/build2/cc/compile.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build2/cc/compile.cxx')
-rw-r--r--build2/cc/compile.cxx1480
1 files changed, 1480 insertions, 0 deletions
diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx
new file mode 100644
index 0000000..b5bcc50
--- /dev/null
+++ b/build2/cc/compile.cxx
@@ -0,0 +1,1480 @@
+// file : build2/cc/compile.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/cc/compile>
+
+#include <cstdlib> // exit()
+#include <iostream> // cerr
+
+#include <build2/depdb>
+#include <build2/scope>
+#include <build2/context>
+#include <build2/variable>
+#include <build2/algorithm>
+#include <build2/diagnostics>
+
+#include <build2/bin/target>
+
+#include <build2/cc/link> // search_library()
+#include <build2/cc/target> // h
+#include <build2/cc/utility>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace cc
+ {
+ using namespace bin;
+
+ compile::
+ compile (data&& d, const link& l)
+ : common (move (d)),
+ link_ (l),
+ rule_id (string (x) += ".compile 1")
+ {
+ }
+
+ match_result compile::
+ match (action a, target& t, const string&) const
+ {
+ tracer trace (x, "compile::match");
+
+ // @@ TODO:
+ //
+ // - check prerequisites: single source file
+ // - if path already assigned, verify extension?
+ //
+
+ // See if we have a source file. Iterate in reverse so that a source
+ // file specified for an obj*{} member overrides the one specified for
+ // the group. Also "see through" groups.
+ //
+ for (prerequisite_member p: reverse_group_prerequisite_members (a, t))
+ {
+ if (p.is_a (x_src))
+ return p;
+ }
+
+ l4 ([&]{trace << "no " << x_lang << " source file for target " << t;});
+ return nullptr;
+ }
+
+ recipe compile::
+ apply (action a, target& xt, const match_result& mr) const
+ {
+ tracer trace (x, "compile::apply");
+
+ file& t (static_cast<file&> (xt));
+
+ scope& bs (t.base_scope ());
+ scope& rs (*bs.root_scope ());
+ otype ct (compile_type (t));
+
+ // Derive file name from target name.
+ //
+ if (t.path ().empty ())
+ {
+ const char* e (nullptr);
+
+ if (tsys == "win32-msvc")
+ {
+ switch (ct)
+ {
+ case otype::e: e = "exe.obj"; break;
+ case otype::a: e = "lib.obj"; break;
+ case otype::s: e = "dll.obj"; break;
+ }
+ }
+ else if (tsys == "mingw32")
+ {
+ switch (ct)
+ {
+ case otype::e: e = "exe.o"; break;
+ case otype::a: e = "a.o"; break;
+ case otype::s: e = "dll.o"; break;
+ }
+ }
+ else if (tsys == "darwin")
+ {
+ switch (ct)
+ {
+ case otype::e: e = "o"; break;
+ case otype::a: e = "a.o"; break;
+ case otype::s: e = "dylib.o"; break;
+ }
+ }
+ else
+ {
+ switch (ct)
+ {
+ case otype::e: e = "o"; break;
+ case otype::a: e = "a.o"; break;
+ case otype::s: e = "so.o"; break;
+ }
+ }
+
+ t.derive_path (e);
+ }
+
+ // Inject dependency on the output directory.
+ //
+ fsdir* dir (inject_fsdir (a, t));
+
+ // Search and match all the existing prerequisites. The injection code
+ // takes care of the ones it is adding.
+ //
+ // When cleaning, ignore prerequisites that are not in the same or a
+ // subdirectory of our project root.
+ //
+ optional<dir_paths> lib_paths; // Extract lazily.
+
+ for (prerequisite_member p: group_prerequisite_members (a, t))
+ {
+ // A dependency on a library is there so that we can get its
+ // *.export.poptions. In particular, making sure it is executed before
+ // us will only restrict parallelism. But we do need to pre-match it
+ // in order to get its prerequisite_targets populated. This is the
+ // "library meta-information protocol". See also append_lib_options()
+ // above.
+ //
+ if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libs> ())
+ {
+ if (a.operation () == update_id)
+ {
+ // Handle imported libraries. We know that for such libraries
+ // we don't need to do match() in order to get options (if
+ // any, they would be set by search_library()).
+ //
+ if (p.proj () == nullptr ||
+ link_.search_library (lib_paths, p.prerequisite) == nullptr)
+ {
+ match_only (a, p.search ());
+ }
+ }
+
+ continue;
+ }
+
+ target& pt (p.search ());
+
+ if (a.operation () == clean_id && !pt.dir.sub (rs.out_path ()))
+ continue;
+
+ build2::match (a, pt);
+ t.prerequisite_targets.push_back (&pt);
+ }
+
+ // Inject additional prerequisites. We only do it when performing update
+ // since chances are we will have to update some of our prerequisites in
+ // the process (auto-generated source code).
+ //
+ if (a == perform_update_id)
+ {
+ // The cached prerequisite target should be the same as what is in
+ // t.prerequisite_targets since we used standard search() and match()
+ // above.
+ //
+ file& src (mr.as_target<file> ());
+
+ // Make sure the output directory exists.
+ //
+ // Is this the right thing to do? It does smell a bit, but then we do
+ // worse things in inject_prerequisites() below. There is also no way
+ // to postpone this until update since we need to extract and inject
+ // header dependencies now (we don't want to be calling search() and
+ // match() in update), which means we need to cache them now as well.
+ // So the only alternative, it seems, is to cache the updates to the
+ // database until later which will sure complicate (and slow down)
+ // things.
+ //
+ if (dir != nullptr)
+ execute_direct (a, *dir);
+
+ depdb dd (t.path () + ".d");
+
+ // First should come the rule name/version.
+ //
+ if (dd.expect (rule_id) != nullptr)
+ l4 ([&]{trace << "rule mismatch forcing update of " << t;});
+
+ // Then the compiler checksum. Note that here we assume it
+ // incorporates the (default) target so that if the compiler changes
+ // but only in what it targets, then the checksum will still change.
+ //
+ if (dd.expect (cast<string> (rs[x_checksum])) != nullptr)
+ l4 ([&]{trace << "compiler mismatch forcing update of " << t;});
+
+ // Then the options checksum.
+ //
+ // The idea is to keep them exactly as they are passed to the compiler
+ // since the order may be significant.
+ //
+ sha256 cs;
+
+ // Hash *.export.poptions from prerequisite libraries.
+ //
+ lorder lo (link_order (bs, ct));
+ for (prerequisite& p: group_prerequisites (t))
+ {
+ target* pt (p.target); // Already searched and matched.
+
+ if (lib* l = pt->is_a<lib> ())
+ pt = &link_member (*l, lo);
+
+ if (pt->is_a<liba> () || pt->is_a<libs> ())
+ hash_lib_options (cs, *pt, lo,
+ c_export_poptions,
+ x_export_poptions);
+ }
+
+ hash_options (cs, t, c_poptions);
+ hash_options (cs, t, x_poptions);
+ hash_options (cs, t, c_coptions);
+ hash_options (cs, t, x_coptions);
+ hash_std (cs, rs, t);
+
+ if (ct == otype::s)
+ {
+ // On Darwin, Win32 -fPIC is the default.
+ //
+ if (tclass == "linux" || tclass == "freebsd")
+ cs.append ("-fPIC");
+ }
+
+ if (dd.expect (cs.string ()) != nullptr)
+ l4 ([&]{trace << "options mismatch forcing update of " << t;});
+
+ // Finally the source file.
+ //
+ if (dd.expect (src.path ()) != nullptr)
+ l4 ([&]{trace << "source file mismatch forcing update of " << t;});
+
+ // If any of the above checks resulted in a mismatch (different
+ // compiler, options, or source file), or if the database is newer
+ // than the target (interrupted update) then force the target update.
+ //
+ if (dd.writing () || dd.mtime () > t.mtime ())
+ t.mtime (timestamp_nonexistent);
+
+ inject (a, t, lo, src, mr.prerequisite->scope, dd);
+
+ dd.close ();
+ }
+
+ switch (a)
+ {
+ case perform_update_id:
+ return [this] (action a, target& t) {return perform_update (a, t);};
+ case perform_clean_id:
+ return [this] (action a, target& t) {return perform_clean (a, t);};
+ default:
+ return noop_recipe; // Configure update.
+ }
+ }
+
+ // Reverse-lookup target type from extension.
+ //
+ const target_type* compile::
+ map_extension (scope& s, const string& n, const string& e) const
+ {
+ // We will just have to try all of the possible ones, in the "most
+ // likely to match" order.
+ //
+ const variable& var (var_pool["extension"]);
+
+ auto test = [&s, &n, &e, &var] (const target_type& tt) -> bool
+ {
+ if (auto l = s.find (var, tt, n))
+ if (cast<string> (l) == e)
+ return true;
+
+ return false;
+ };
+
+ for (const target_type* const* p (x_inc); *p != nullptr; ++p)
+ if (test (**p)) return *p;
+
+ return nullptr;
+ }
+
+ void compile::
+ append_prefixes (prefix_map& m, target& t, const variable& var) const
+ {
+ tracer trace (x, "append_prefixes");
+
+ // If this target does not belong to any project (e.g, an
+ // "imported as installed" library), then it can't possibly
+ // generate any headers for us.
+ //
+ scope* rs (t.base_scope ().root_scope ());
+ if (rs == nullptr)
+ return;
+
+ const dir_path& out_base (t.dir);
+ const dir_path& out_root (rs->out_path ());
+
+ if (auto l = t[var])
+ {
+ const auto& v (cast<strings> (l));
+
+ for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
+ {
+ // -I can either be in the "-Ifoo" or "-I foo" form. For VC it can
+ // also be /I.
+ //
+ const string& o (*i);
+
+ if (o.size () < 2 || (o[0] != '-' && o[0] != '/') || o[1] != 'I')
+ continue;
+
+ dir_path d;
+ if (o.size () == 2)
+ {
+ if (++i == e)
+ break; // Let the compiler complain.
+
+ d = dir_path (*i);
+ }
+ else
+ d = dir_path (*i, 2, string::npos);
+
+ l6 ([&]{trace << "-I '" << d << "'";});
+
+ // If we are relative or not inside our project root, then
+ // ignore.
+ //
+ if (d.relative () || !d.sub (out_root))
+ continue;
+
+ // If the target directory is a sub-directory of the include
+ // directory, then the prefix is the difference between the
+ // two. Otherwise, leave it empty.
+ //
+ // The idea here is to make this "canonical" setup work auto-
+ // magically:
+ //
+ // 1. We include all files with a prefix, e.g., <foo/bar>.
+ // 2. The library target is in the foo/ sub-directory, e.g.,
+ // /tmp/foo/.
+ // 3. The poptions variable contains -I/tmp.
+ //
+ dir_path p (out_base.sub (d) ? out_base.leaf (d) : dir_path ());
+
+ auto j (m.find (p));
+
+ if (j != m.end ())
+ {
+ if (j->second != d)
+ {
+ // We used to reject duplicates but it seems this can
+ // be reasonably expected to work according to the order
+ // of the -I options.
+ //
+ if (verb >= 4)
+ trace << "overriding dependency prefix '" << p << "'\n"
+ << " old mapping to " << j->second << "\n"
+ << " new mapping to " << d;
+
+ j->second = d;
+ }
+ }
+ else
+ {
+ l6 ([&]{trace << "'" << p << "' = '" << d << "'";});
+ m.emplace (move (p), move (d));
+ }
+ }
+ }
+ }
+
+ // Append library prefixes based on the *.export.poptions variables
+ // recursively, prerequisite libraries first.
+ //
+ void compile::
+ append_lib_prefixes (prefix_map& m, target& l, lorder lo) const
+ {
+ for (target* t: l.prerequisite_targets)
+ {
+ if (t == nullptr)
+ continue;
+
+ if (lib* l = t->is_a<lib> ())
+ t = &link_member (*l, lo); // Pick one of the members.
+
+ if (t->is_a<liba> () || t->is_a<libs> ())
+ append_lib_prefixes (m, *t, lo);
+ }
+
+ append_prefixes (m, l, c_export_poptions);
+ append_prefixes (m, l, x_export_poptions);
+ }
+
+ auto compile::
+ build_prefix_map (target& t, lorder lo) const -> prefix_map
+ {
+ prefix_map m;
+
+ // First process the include directories from prerequisite libraries.
+ // Note that here we don't need to see group members (see apply()).
+ //
+ for (prerequisite& p: group_prerequisites (t))
+ {
+ target* pt (p.target); // Already searched and matched.
+
+ if (lib* l = pt->is_a<lib> ())
+ pt = &link_member (*l, lo); // Pick one of the members.
+
+ if (pt->is_a<liba> () || pt->is_a<libs> ())
+ append_lib_prefixes (m, *pt, lo);
+ }
+
+ // Then process our own.
+ //
+ append_prefixes (m, t, c_poptions);
+ append_prefixes (m, t, x_poptions);
+
+ return m;
+ }
+
+ // Return the next make prerequisite starting from the specified
+ // position and update position to point to the start of the
+ // following prerequisite or l.size() if there are none left.
+ //
+ static string
+ next_make (const string& l, size_t& p)
+ {
+ size_t n (l.size ());
+
+ // Skip leading spaces.
+ //
+ for (; p != n && l[p] == ' '; p++) ;
+
+ // Lines containing multiple prerequisites are 80 characters max.
+ //
+ string r;
+ r.reserve (n);
+
+ // Scan the next prerequisite while watching out for escape sequences.
+ //
+ for (; p != n && l[p] != ' '; p++)
+ {
+ char c (l[p]);
+
+ if (p + 1 != n)
+ {
+ if (c == '$')
+ {
+ // Got to be another (escaped) '$'.
+ //
+ if (l[p + 1] == '$')
+ ++p;
+ }
+ else if (c == '\\')
+ {
+ // This may or may not be an escape sequence depending on whether
+ // what follows is "escapable".
+ //
+ switch (c = l[++p])
+ {
+ case '\\': break;
+ case ' ': break;
+ default: c = '\\'; --p; // Restore.
+ }
+ }
+ }
+
+ r += c;
+ }
+
+ // Skip trailing spaces.
+ //
+ for (; p != n && l[p] == ' '; p++) ;
+
+ // Skip final '\'.
+ //
+ if (p == n - 1 && l[p] == '\\')
+ p++;
+
+ return r;
+ }
+
+ // Extract the include path from the VC /showIncludes output line. Return
+ // empty string if the line is not an include note or include error. Set
+ // the good_error flag if it is an include error (which means the process
+ // will terminate with the error status that needs to be ignored).
+ //
+ static string
+ next_show (const string& l, bool& good_error)
+ {
+ // The include error should be the last line that we handle.
+ //
+ assert (!good_error);
+
+ // VC /showIncludes output. The first line is the file being
+ // compiled. Then we have the list of headers, one per line, in this
+ // form (text can presumably be translated):
+ //
+ // Note: including file: C:\Program Files (x86)\[...]\iostream
+ //
+ // Finally, if we hit a non-existent header, then we end with an error
+ // line in this form:
+ //
+ // x.cpp(3): fatal error C1083: Cannot open include file: 'd/h.hpp':
+ // No such file or directory
+ //
+
+ // Distinguishing between the include note and the include error is
+ // easy: we can just check for C1083. Distinguising between the note and
+ // other errors/warnings is harder: an error could very well end with
+ // what looks like a path so we cannot look for the note but rather have
+ // to look for an error. Here we assume that a line containing ' CNNNN:'
+ // is an error. Should be robust enough in the face of language
+ // translation, etc.
+ //
+ size_t p (l.find (':'));
+ size_t n (l.size ());
+
+ for (; p != string::npos; p = ++p != n ? l.find (':', p) : string::npos)
+ {
+ auto isnum = [](char c) {return c >= '0' && c <= '9';};
+
+ if (p > 5 &&
+ l[p - 6] == ' ' &&
+ l[p - 5] == 'C' &&
+ isnum (l[p - 4]) &&
+ isnum (l[p - 3]) &&
+ isnum (l[p - 2]) &&
+ isnum (l[p - 1]))
+ {
+ p -= 4; // Start of the error code.
+ break;
+ }
+ }
+
+ if (p == string::npos)
+ {
+ // Include note. We assume the path is always at the end but
+ // need to handle both absolute Windows and POSIX ones.
+ //
+ size_t p (l.rfind (':'));
+
+ if (p != string::npos)
+ {
+ // See if this one is part of the Windows drive letter.
+ //
+ if (p > 1 && p + 1 < n && // 2 chars before, 1 after.
+ l[p - 2] == ' ' &&
+ alpha (l[p - 1]) &&
+ path::traits::is_separator (l[p + 1]))
+ p = l.rfind (':', p - 2);
+ }
+
+ if (p != string::npos)
+ {
+ // VC uses indentation to indicate the include nesting so there
+ // could be any number of spaces after ':'. Skip them.
+ //
+ p = l.find_first_not_of (' ', p + 1);
+ }
+
+ if (p == string::npos)
+ fail << "unable to parse /showIncludes include note line";
+
+ return string (l, p);
+ }
+ else if (l.compare (p, 4, "1083") == 0)
+ {
+ // Include error. The path is conveniently quoted with ''.
+ //
+ size_t p2 (l.rfind ('\''));
+
+ if (p2 != string::npos && p2 != 0)
+ {
+ size_t p1 (l.rfind ('\'', p2 - 1));
+
+ if (p1 != string::npos)
+ {
+ good_error = true;
+ return string (l, p1 + 1 , p2 - p1 - 1);
+ }
+ }
+
+ error << "unable to parse /showIncludes include error line";
+ throw failed ();
+ }
+ else
+ {
+ // Some other error.
+ //
+ return string ();
+ }
+ }
+
+ void compile::
+ inject (action a,
+ target& t,
+ lorder lo,
+ file& src,
+ scope& ds,
+ depdb& dd) const
+ {
+ tracer trace (x, "compile::inject");
+
+ l6 ([&]{trace << "target: " << t;});
+
+ // If things go wrong (and they often do in this area), give the user a
+ // bit extra context.
+ //
+ auto g (
+ make_exception_guard (
+ [&src]()
+ {
+ info << "while extracting header dependencies from " << src;
+ }));
+
+ scope& rs (t.root_scope ());
+
+ // Initialize lazily, only if required.
+ //
+ cstrings args;
+ string std; // Storage.
+
+ auto init_args = [&t, lo, &src, &rs, &args, &std, this] ()
+ {
+ args.push_back (cast<path> (rs[config_x]).string ().c_str ());
+
+ // Add *.export.poptions from prerequisite libraries. Note that here
+ // we don't need to see group members (see apply()).
+ //
+ for (prerequisite& p: group_prerequisites (t))
+ {
+ target* pt (p.target); // Already searched and matched.
+
+ if (lib* l = pt->is_a<lib> ())
+ pt = &link_member (*l, lo);
+
+ if (pt->is_a<liba> () || pt->is_a<libs> ())
+ append_lib_options (args, *pt, lo,
+ c_export_poptions,
+ x_export_poptions);
+ }
+
+ append_options (args, t, c_poptions);
+ append_options (args, t, x_poptions);
+
+ // Some compile options (e.g., -std, -m) affect the preprocessor.
+ //
+ append_options (args, t, c_coptions);
+ append_options (args, t, x_coptions);
+
+ append_std (args, rs, t, std);
+
+ if (t.is_a<objs> ())
+ {
+ // On Darwin, Win32 -fPIC is the default.
+ //
+ if (tclass == "linux" || tclass == "freebsd")
+ args.push_back ("-fPIC");
+ }
+
+ if (cid == "msvc")
+ {
+ args.push_back ("/nologo");
+
+ // See perform_update() for details on overriding the default
+ // exceptions and runtime.
+ //
+ if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
+ args.push_back ("/EHsc");
+
+ if (!find_option_prefixes ({"/MD", "/MT"}, args))
+ args.push_back ("/MD");
+
+ args.push_back ("/EP"); // Preprocess to stdout.
+ args.push_back ("/showIncludes"); // Goes to sterr becasue of /EP.
+ args.push_back (x_lang == lang::c ? "/TC" : "/TP"); // Compile as.
+ }
+ else
+ {
+ args.push_back ("-M"); // Note: -MM -MG skips missing <>-included.
+ args.push_back ("-MG"); // Treat missing headers as generated.
+
+ // Previously we used '*' as a target name but it gets expanded to
+ // the current directory file names by GCC (4.9) that comes with
+ // MSYS2 (2.4). Yes, this is the (bizarre) behavior of GCC being
+ // executed in the shell with -MQ '*' option and not just -MQ *.
+ //
+ args.push_back ("-MQ"); // Quoted target name.
+ args.push_back ("^"); // Old versions can't do empty target name.
+ }
+
+ // We are using absolute source file path in order to get absolute
+ // paths in the result. Any relative paths in the result are non-
+ // existent, potentially auto-generated headers.
+ //
+ // @@ We will also have to use absolute -I paths to guarantee
+ // that. Or just detect relative paths and error out?
+ //
+ args.push_back (src.path ().string ().c_str ());
+ args.push_back (nullptr);
+ };
+
+ // Build the prefix map lazily only if we have non-existent files.
+ // Also reuse it over restarts since it doesn't change.
+ //
+ prefix_map pm;
+
+ // If any prerequisites that we have extracted changed, then we have to
+ // redo the whole thing. The reason for this is auto-generated headers:
+ // the updated header may now include a yet-non-existent header. Unless
+ // we discover this and generate it (which, BTW, will trigger another
+ // restart since that header, in turn, can also include auto-generated
+ // headers), we will end up with an error during compilation proper.
+ //
+ // One complication with this restart logic is that we will see a
+ // "prefix" of prerequisites that we have already processed (i.e., they
+ // are already in our prerequisite_targets list) and we don't want to
+ // keep redoing this over and over again. One thing to note, however, is
+ // that the prefix that we have seen on the previous run must appear
+ // exactly the same in the subsequent run. The reason for this is that
+ // none of the files that it can possibly be based on have changed and
+ // thus it should be exactly the same. To put it another way, the
+ // presence or absence of a file in the dependency output can only
+ // depend on the previous files (assuming the compiler outputs them as
+ // it encounters them and it is hard to think of a reason why would
+ // someone do otherwise). And we have already made sure that all those
+ // files are up to date. And here is the way we are going to exploit
+ // this: we are going to keep track of how many prerequisites we have
+ // processed so far and on restart skip right to the next one.
+ //
+ // And one more thing: most of the time this list of headers would stay
+ // unchanged and extracting them by running the compiler every time is a
+ // bit wasteful. So we are going to cache them in the depdb. If the db
+ // hasn't been invalidated yet (e.g., because the compiler options have
+ // changed), then we start by reading from it. If anything is out of
+ // date then we use the same restart and skip logic to switch to the
+ // compiler run.
+ //
+
+ // Update the target "smartly". Return true if it has changed or if the
+ // passed timestamp is not timestamp_unknown and is older than the
+ // target.
+ //
+ // There would normally be a lot of headers for every source file (think
+ // all the system headers) and just calling execute_direct() on all of
+ // them can get expensive. At the same time, most of these headers are
+ // existing files that we will never be updating (again, system headers,
+ // for example) and the rule that will match them is the fallback
+ // file_rule. That rule has an optimization: it returns noop_recipe
+ // (which causes the target state to be automatically set to unchanged)
+ // if the file is known to be up to date.
+ //
+ auto update = [&trace, a] (path_target& pt, timestamp ts) -> bool
+ {
+ if (pt.state () != target_state::unchanged)
+ {
+ // We only want to restart if our call to execute() actually
+ // caused an update. In particular, the target could already
+ // have been in target_state::changed because of a dependency
+ // extraction run for some other source file.
+ //
+ target_state os (pt.state ());
+ target_state ns (execute_direct (a, pt));
+
+ if (ns != os && ns != target_state::unchanged)
+ {
+ l6 ([&]{trace << "updated " << pt
+ << "; old state " << os
+ << "; new state " << ns;});
+ return true;
+ }
+ }
+
+ if (ts != timestamp_unknown)
+ {
+ timestamp mt (pt.mtime ());
+
+ // See execute_prerequisites() for rationale behind the equal part.
+ //
+ return ts < mt || (ts == mt && pt.state () != target_state::changed);
+ }
+
+ return false;
+ };
+
+ // Update and add a header file to the list of prerequisite targets.
+ // Depending on the cache flag, the file is assumed to either have come
+ // from the depdb cache or from the compiler run. Return whether the
+ // extraction process should be restarted.
+ //
+ auto add = [&trace, &update, &pm, a, &t, lo, &ds, &dd, this]
+ (path f, bool cache) -> bool
+ {
+ if (!f.absolute ())
+ {
+ f.normalize ();
+
+ // This is probably as often an error as an auto-generated file, so
+ // trace at level 4.
+ //
+ l4 ([&]{trace << "non-existent header '" << f << "'";});
+
+ // If we already did this and build_prefix_map() returned empty,
+ // then we would have failed below.
+ //
+ if (pm.empty ())
+ pm = build_prefix_map (t, lo);
+
+ // First try the whole file. Then just the directory.
+ //
+ // @@ Has to be a separate map since the prefix can be
+ // the same as the file name.
+ //
+ // auto i (pm.find (f));
+
+ // Find the most qualified prefix of which we are a sub-path.
+ //
+ auto i (pm.end ());
+
+ if (!pm.empty ())
+ {
+ const dir_path& d (f.directory ());
+ i = pm.upper_bound (d);
+
+ // Get the greatest less than, if any. We might still not be a
+ // sub. Note also that we still have to check the last element if
+ // upper_bound() returned end().
+ //
+ if (i == pm.begin () || !d.sub ((--i)->first))
+ i = pm.end ();
+ }
+
+ if (i == pm.end ())
+ fail << "unable to map presumably auto-generated header '"
+ << f << "' to a project";
+
+ f = i->second / f;
+ }
+ else
+ {
+ // We used to just normalize the path but that could result in an
+ // invalid path (e.g., on CentOS 7 with Clang 3.4) because of the
+ // symlinks. So now we realize (i.e., realpath(3)) it instead. If
+ // it comes from the depdb, in which case we've already done that.
+ //
+ if (!cache)
+ f.realize ();
+ }
+
+ l6 ([&]{trace << "injecting " << f;});
+
+ // Split the name into its directory part, the name part, and
+ // extension. Here we can assume the name part is a valid filesystem
+ // name.
+ //
+ // Note that if the file has no extension, we record an empty
+ // extension rather than NULL (which would signify that the default
+ // extension should be added).
+ //
+ dir_path d (f.directory ());
+ string n (f.leaf ().base ().string ());
+ const char* es (f.extension ());
+ const string* e (&extension_pool.find (es != nullptr ? es : ""));
+
+ // Determine the target type.
+ //
+ const target_type* tt (nullptr);
+
+ // See if this directory is part of any project out_root hierarchy.
+ // Note that this will miss all the headers that come from src_root
+ // (so they will be treated as generic C headers below). Generally,
+ // we don't have the ability to determine that some file belongs to
+ // src_root of some project. But that's not a problem for our
+ // purposes: it is only important for us to accurately determine
+ // target types for headers that could be auto-generated.
+ //
+ // While at it also try to determine if this target is from the src
+ // or out tree of said project.
+ //
+ dir_path out;
+
+ scope& bs (scopes.find (d));
+ if (scope* rs = bs.root_scope ())
+ {
+ tt = map_extension (bs, n, *e);
+
+ if (bs.out_path () != bs.src_path () && d.sub (bs.src_path ()))
+ out = out_src (d, *rs);
+ }
+
+ // If it is outside any project, or the project doesn't have such an
+ // extension, assume it is a plain old C header.
+ //
+ if (tt == nullptr)
+ tt = &h::static_type;
+
+ // Find or insert target.
+ //
+ // @@ OPT: move d, out, n
+ //
+ path_target& pt (
+ static_cast<path_target&> (search (*tt, d, out, n, e, &ds)));
+
+ // Assign path.
+ //
+ if (pt.path ().empty ())
+ pt.path (move (f));
+ else
+ assert (pt.path () == f);
+
+ // Match to a rule.
+ //
+ build2::match (a, pt);
+
+ // Update.
+ //
+ // If this header came from the depdb, make sure it is no older than
+ // the db itself (if it has changed since the db was written, then
+ // chances are the cached data is stale).
+ //
+ bool restart (update (pt, cache ? dd.mtime () : timestamp_unknown));
+
+ // Verify/add it to the dependency database. We do it after update in
+ // order not to add bogus files (non-existent and without a way to
+ // update).
+ //
+ if (!cache)
+ dd.expect (pt.path ());
+
+ // Add to our prerequisite target list.
+ //
+ t.prerequisite_targets.push_back (&pt);
+
+ return restart;
+ };
+
+ // If nothing so far has invalidated the dependency database, then
+ // try the cached data before running the compiler.
+ //
+ bool cache (dd.reading ());
+
+ // But, before we do all that, make sure the source file itself if up to
+ // date.
+ //
+ if (update (src, dd.mtime ()))
+ {
+ // If the file got updated or is newer than the database, then we
+ // cannot rely on the cache any further. However, the cached data
+ // could actually still be valid so the compiler run will validate it.
+ //
+ // We do need to update the database timestamp, however. Failed that,
+ // we will keep re-validating the cached data over and over again.
+ //
+ if (cache)
+ {
+ cache = false;
+ dd.touch ();
+ }
+ }
+
+ size_t skip_count (0);
+ for (bool restart (true); restart; cache = false)
+ {
+ restart = false;
+
+ if (cache)
+ {
+ // If any, this is always the first run.
+ //
+ assert (skip_count == 0);
+
+ while (dd.more ())
+ {
+ string* l (dd.read ());
+
+ // If the line is invalid, run the compiler.
+ //
+ if (l == nullptr)
+ {
+ restart = true;
+ break;
+ }
+
+ restart = add (path (move (*l)), true);
+ skip_count++;
+
+ // The same idea as in the source file update above.
+ //
+ if (restart)
+ {
+ l6 ([&]{trace << "restarting";});
+ dd.touch ();
+ break;
+ }
+ }
+ }
+ else
+ {
+ try
+ {
+ if (args.empty ())
+ init_args ();
+
+ if (verb >= 3)
+ print_process (args);
+
+ // For VC with /EP we need a pipe to stderr and stdout should go
+ // to /dev/null.
+ //
+ process pr (args.data (),
+ 0,
+ cid == "msvc" ? -2 : -1,
+ cid == "msvc" ? -1 : 2);
+
+ try
+ {
+ // We may not read all the output (e.g., due to a restart).
+ // Before we used to just close the file descriptor to signal to
+ // the other end that we are not interested in the rest. This
+ // works fine with GCC but Clang (3.7.0) finds this impolite and
+ // complains, loudly (broken pipe). So now we are going to skip
+ // until the end.
+ //
+ ifdstream is (cid == "msvc" ? pr.in_efd : pr.in_ofd,
+ fdstream_mode::text | fdstream_mode::skip,
+ ifdstream::badbit);
+
+ // In some cases we may need to ignore the error return
+ // status. The good_error flag keeps track of that. Similarly
+ // we sometimes expect the error return status based on the
+ // output we see. The bad_error flag is for that.
+ //
+ bool good_error (false), bad_error (false);
+
+ size_t skip (skip_count);
+ for (bool first (true), second (false);
+ !(restart || is.eof ()); )
+ {
+ string l;
+ getline (is, l);
+
+ if (is.fail ())
+ {
+ if (is.eof ()) // Trailing newline.
+ break;
+
+ throw ifdstream::failure ("");
+ }
+
+ l6 ([&]{trace << "header dependency line '" << l << "'";});
+
+ // Parse different dependency output formats.
+ //
+ if (cid == "msvc")
+ {
+ if (first)
+ {
+ // The first line should be the file we are compiling. If
+ // it is not, then something went wrong even before we
+ // could compile anything (e.g., file does not exist). In
+ // this case the first line (and everything after it) is
+ // presumably diagnostics.
+ //
+ if (l != src.path ().leaf ().string ())
+ {
+ text << l;
+ bad_error = true;
+ break;
+ }
+
+ first = false;
+ continue;
+ }
+
+ string f (next_show (l, good_error));
+
+ if (f.empty ()) // Some other diagnostics.
+ {
+ text << l;
+ bad_error = true;
+ break;
+ }
+
+ // Skip until where we left off.
+ //
+ if (skip != 0)
+ {
+ // We can't be skipping over a non-existent header.
+ //
+ assert (!good_error);
+ skip--;
+ }
+ else
+ {
+ restart = add (path (move (f)), false);
+ skip_count++;
+
+ // If the header does not exist, we better restart.
+ //
+ assert (!good_error || restart);
+
+ if (restart)
+ l6 ([&]{trace << "restarting";});
+ }
+ }
+ else
+ {
+ // Make dependency declaration.
+ //
+ size_t pos (0);
+
+ if (first)
+ {
+ // Empty output should mean the wait() call below will
+ // return false.
+ //
+ if (l.empty ())
+ {
+ bad_error = true;
+ break;
+ }
+
+ assert (l[0] == '^' && l[1] == ':' && l[2] == ' ');
+
+ first = false;
+ second = true;
+
+ // While normally we would have the source file on the
+ // first line, if too long, it will be moved to the next
+ // line and all we will have on this line is "^: \".
+ //
+ if (l.size () == 4 && l[3] == '\\')
+ continue;
+ else
+ pos = 3; // Skip "^: ".
+
+ // Fall through to the 'second' block.
+ }
+
+ if (second)
+ {
+ second = false;
+ next_make (l, pos); // Skip the source file.
+ }
+
+ while (pos != l.size ())
+ {
+ string f (next_make (l, pos));
+
+ // Skip until where we left off.
+ //
+ if (skip != 0)
+ {
+ skip--;
+ continue;
+ }
+
+ restart = add (path (move (f)), false);
+ skip_count++;
+
+ if (restart)
+ {
+ l6 ([&]{trace << "restarting";});
+ break;
+ }
+ }
+ }
+ }
+
+ // In case of VC, we are parsing stderr and if things go south,
+ // we need to copy the diagnostics for the user to see.
+ //
+ // Note that the eof check is important: if the stream is at
+ // eof, this and all subsequent writes to cerr will fail (and
+ // you won't see a thing).
+ //
+ if (is.peek () != ifdstream::traits_type::eof () &&
+ cid == "msvc" &&
+ bad_error)
+ cerr << is.rdbuf ();
+
+ is.close ();
+
+ // We assume the child process issued some diagnostics.
+ //
+ if (!pr.wait ())
+ {
+ if (!good_error) // Ignore expected errors (restart).
+ throw failed ();
+ }
+ else if (bad_error)
+ fail << "expected error exist status from " << x_lang
+ << " compiler";
+ }
+ catch (const ifdstream::failure&)
+ {
+ pr.wait ();
+ fail << "unable to read " << x_lang << " compiler header "
+ << "dependency output";
+ }
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << args[0] << ": " << e.what ();
+
+ // In a multi-threaded program that fork()'ed but did not exec(),
+ // it is unwise to try to do any kind of cleanup (like unwinding
+ // the stack and running destructors).
+ //
+ if (e.child ())
+ exit (1);
+
+ throw failed ();
+ }
+ }
+ }
+ }
+
+ // Filter cl.exe noise (msvc.cxx).
+ //
+ void
+ msvc_filter_cl (ifdstream&, const path& src);
+
+ target_state compile::
+ perform_update (action a, target& xt) const
+ {
+ file& t (static_cast<file&> (xt));
+ file* s (execute_prerequisites<file> (x_src, a, t, t.mtime ()));
+
+ if (s == nullptr)
+ return target_state::unchanged;
+
+ scope& bs (t.base_scope ());
+ scope& rs (*bs.root_scope ());
+ otype ct (compile_type (t));
+
+ cstrings args {cast<path> (rs[config_x]).string ().c_str ()};
+
+ // Translate paths to relative (to working directory) ones. This
+ // results in easier to read diagnostics.
+ //
+ path relo (relative (t.path ()));
+ path rels (relative (s->path ()));
+
+ // Add *.export.poptions from prerequisite libraries. Note that here we
+ // don't need to see group members (see apply()).
+ //
+ lorder lo (link_order (bs, ct));
+ for (prerequisite& p: group_prerequisites (t))
+ {
+ target* pt (p.target); // Already searched and matched.
+
+ if (lib* l = pt->is_a<lib> ())
+ pt = &link_member (*l, lo);
+
+ if (pt->is_a<liba> () || pt->is_a<libs> ())
+ append_lib_options (args, *pt, lo,
+ c_export_poptions,
+ x_export_poptions);
+ }
+
+ append_options (args, t, c_poptions);
+ append_options (args, t, x_poptions);
+ append_options (args, t, c_coptions);
+ append_options (args, t, x_coptions);
+
+ string std, out, out1; // Storage.
+
+ append_std (args, rs, t, std);
+
+ if (cid == "msvc")
+ {
+ // The /F*: option variants with separate names only became available
+ // in VS2013/12.0. Why do we bother? Because the command line suddenly
+ // becomes readable.
+ //
+ uint64_t ver (cast<uint64_t> (rs[x_version_major]));
+
+ args.push_back ("/nologo");
+
+ // While we want to keep the low-level build as "pure" as possible,
+ // the two misguided defaults, exceptions and runtime, just have to be
+ // fixed. Otherwise the default build is pretty much unusable. But we
+ // also make sure that the user can easily disable our defaults: if we
+ // see any relevant options explicitly specified, we take our hands
+ // off.
+ //
+ // For C looks like no /EH* (exceptions supported but no C++ objects
+ // destroyed) is a reasonable default.
+ //
+ if (x_lang == lang::cxx && !find_option_prefix ("/EH", args))
+ args.push_back ("/EHsc");
+
+ // The runtime is a bit more interesting. At first it may seem like a
+ // good idea to be a bit clever and use the static runtime if we are
+ // building obja{}. And for obje{} we could decide which runtime to
+ // use based on the library link order: if it is static-only, then we
+ // could assume the static runtime. But it is indeed too clever: when
+ // building liba{} we have no idea who is going to use it. It could be
+ // an exe{} that links both static and shared libraries (and is
+ // therefore built with the shared runtime). And to safely use the
+ // static runtime, everything must be built with /MT and there should
+ // be no DLLs in the picture. So we are going to play it safe and
+ // always default to the shared runtime.
+ //
+ // In a similar vein, it would seem reasonable to use the debug runtime
+ // if we are compiling with debug. But, again, there will be fireworks
+ // if we have some projects built with debug and some without and then
+ // we try to link them together (which is not an unreasonable thing to
+ // do). So by default we will always use the release runtime.
+ //
+ if (!find_option_prefixes ({"/MD", "/MT"}, args))
+ args.push_back ("/MD");
+
+ // The presence of /Zi or /ZI causes the compiler to write debug info
+ // to the .pdb file. By default it is a shared file called vcNN.pdb
+ // (where NN is the VC version) created (wait for it) in the current
+ // working directory (and not the directory of the .obj file). Also,
+ // because it is shared, there is a special Windows service that
+ // serializes access. We, of course, want none of that so we will
+ // create a .pdb per object file.
+ //
+ // Note that this also changes the name of the .idb file (used for
+ // minimal rebuild and incremental compilation): cl.exe take the /Fd
+ // value and replaces the .pdb extension with .idb.
+ //
+ // Note also that what we are doing here appears to be incompatible
+ // with PCH (/Y* options) and /Gm (minimal rebuild).
+ //
+ if (find_options ({"/Zi", "/ZI"}, args))
+ {
+ if (ver >= 18)
+ args.push_back ("/Fd:");
+ else
+ out1 = "/Fd";
+
+ out1 += relo.string ();
+ out1 += ".pdb";
+
+ args.push_back (out1.c_str ());
+ }
+
+ if (ver >= 18)
+ {
+ args.push_back ("/Fo:");
+ args.push_back (relo.string ().c_str ());
+ }
+ else
+ {
+ out = "/Fo" + relo.string ();
+ args.push_back (out.c_str ());
+ }
+
+ args.push_back ("/c"); // Compile only.
+ args.push_back (x_lang == lang::c ? "/TC" : "/TP"); // Compile as.
+ args.push_back (rels.string ().c_str ());
+ }
+ else
+ {
+ if (ct == otype::s)
+ {
+ // On Darwin, Win32 -fPIC is the default.
+ //
+ if (tclass == "linux" || tclass == "freebsd")
+ args.push_back ("-fPIC");
+ }
+
+ args.push_back ("-o");
+ args.push_back (relo.string ().c_str ());
+
+ args.push_back ("-c");
+ args.push_back (rels.string ().c_str ());
+ }
+
+ args.push_back (nullptr);
+
+ if (verb >= 2)
+ print_process (args);
+ else if (verb)
+ text << x_name << ' ' << *s;
+
+ try
+ {
+ // VC cl.exe sends diagnostics to stdout. It also prints the file name
+ // being compiled as the first line. So for cl.exe we redirect stdout
+ // to a pipe, filter that noise out, and send the rest to stderr.
+ //
+ // For other compilers redirect stdout to stderr, in case any of them
+ // tries to pull off something similar. For sane compilers this should
+ // be harmless.
+ //
+ bool filter (cid == "msvc");
+
+ process pr (args.data (), 0, (filter ? -1 : 2));
+
+ if (filter)
+ {
+ try
+ {
+ ifdstream is (pr.in_ofd, fdstream_mode::text, ifdstream::badbit);
+
+ msvc_filter_cl (is, rels);
+
+ // If anything remains in the stream, send it all to stderr. Note
+ // that the eof check is important: if the stream is at eof, this
+ // and all subsequent writes to cerr will fail (and you won't see
+ // a thing).
+ //
+ if (is.peek () != ifdstream::traits_type::eof ())
+ cerr << is.rdbuf ();
+
+ is.close ();
+ }
+ catch (const ifdstream::failure&) {} // Assume exits with error.
+ }
+
+ if (!pr.wait ())
+ throw failed ();
+
+ // Should we go to the filesystem and get the new mtime? We
+ // know the file has been modified, so instead just use the
+ // current clock time. It has the advantage of having the
+ // subseconds precision.
+ //
+ t.mtime (system_clock::now ());
+ return target_state::changed;
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << args[0] << ": " << e.what ();
+
+ // In a multi-threaded program that fork()'ed but did not exec(),
+ // it is unwise to try to do any kind of cleanup (like unwinding
+ // the stack and running destructors).
+ //
+ if (e.child ())
+ exit (1);
+
+ throw failed ();
+ }
+ }
+
+ target_state compile::
+ perform_clean (action a, target& xt) const
+ {
+ file& t (static_cast<file&> (xt));
+
+ initializer_list<const char*> e;
+
+ if (cid == "msvc")
+ e = {".d", ".idb", ".pdb"};
+ else
+ e = {".d"};
+
+ return clean_extra (a, t, e);
+ }
+ }
+}