aboutsummaryrefslogtreecommitdiff
path: root/build/cxx/rule.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build/cxx/rule.cxx')
-rw-r--r--build/cxx/rule.cxx213
1 files changed, 149 insertions, 64 deletions
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index 65616f8..9d25143 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -20,12 +20,18 @@
#include <build/diagnostics>
#include <build/context>
+#include <build/bin/target>
+
+#include <build/cxx/target>
+
using namespace std;
namespace build
{
namespace cxx
{
+ using namespace bin;
+
// T is either target or scope.
//
template <typename T>
@@ -73,27 +79,16 @@ namespace build
// - if path already assigned, verify extension?
//
- if (t.is_a<obj> ())
- fail << diag_doing (a, t) << " target group" <<
- info << "explicitly select either obja{} or objso{} member";
-
- // See if we have a C++ source file.
+ // See if we have a C++ source file. Iterate in reverse so that
+ // a source file specified for an obj member overrides the one
+ // specified for the group.
//
- for (prerequisite_target& pe: t.prerequisites)
+ for (prerequisite_target& pe: reverse_iterate (group_prerequisites (t)))
{
if (pe.prereq->type.id == typeid (cxx))
return &pe;
}
- if (t.group != nullptr)
- {
- for (prerequisite_target& pe: t.group->prerequisites)
- {
- if (pe.prereq->type.id == typeid (cxx))
- return &pe;
- }
- }
-
level3 ([&]{trace << "no c++ source file for target " << t;});
return nullptr;
}
@@ -146,7 +141,7 @@ namespace build
switch (a)
{
case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean_file;
+ case perform_clean_id: return &perform_clean;
default: return default_recipe; // Forward to prerequisites.
}
}
@@ -273,9 +268,13 @@ namespace build
path f (next (l, pos));
f.normalize ();
- assert (f.absolute ()); // Logic below depends on this.
+ if (!f.absolute ())
+ {
+ level5 ([&]{trace << "skipping generated/non-existent " << f;});
+ continue;
+ }
- level5 ([&]{trace << "prerequisite path: " << f;});
+ level5 ([&]{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
@@ -343,7 +342,7 @@ namespace build
perform_update (action a, target& xt)
{
path_target& t (static_cast<path_target&> (xt));
- cxx* s (execute_prerequisites<cxx> (a, t, t.mtime ()));
+ cxx* s (execute_find_prerequisites<cxx> (a, t, t.mtime ()));
if (s == nullptr)
return target_state::unchanged;
@@ -431,13 +430,13 @@ namespace build
// (i.e., a utility library).
//
- bool so (t.is_a<lib> ());
+ bool so (t.is_a<libso> ());
// Scan prerequisites and see if we can work with what we've got.
//
bool seen_cxx (false), seen_c (false), seen_obj (false);
- for (prerequisite& p: t.prerequisites)
+ for (prerequisite& p: group_prerequisites (t))
{
if (p.type.id == typeid (cxx)) // @@ Should use is_a (add to p.type).
{
@@ -478,6 +477,20 @@ namespace build
return seen_cxx || seen_c || seen_obj ? &t : nullptr;
}
+ static inline target_state
+ select_a (action a, target& t)
+ {
+ obj* o (t.is_a<obj> ());
+ return execute (a, o != nullptr ? *o->a : t);
+ }
+
+ static inline target_state
+ select_so (action a, target& t)
+ {
+ obj* o (t.is_a<obj> ());
+ return execute (a, o != nullptr ? *o->so : t);
+ }
+
recipe link::
apply (action a, target& xt, void*) const
{
@@ -485,16 +498,22 @@ namespace build
path_target& t (static_cast<path_target&> (xt));
- bool so (t.is_a<lib> ());
+ type tt (t.is_a<exe> ()
+ ? type::exe
+ : (t.is_a<liba> () ? type::liba : type::libso));
+
+ bool so (tt == type::libso);
// Derive file name from target name.
//
if (t.path ().empty ())
{
- if (so)
- t.path (t.derived_path ("so", "lib"));
- else
- t.path (t.derived_path ()); // exe
+ switch (tt)
+ {
+ case type::exe: t.path (t.derived_path ( )); break;
+ case type::liba: t.path (t.derived_path ("a", "lib")); break;
+ case type::libso: t.path (t.derived_path ("so", "lib")); break;
+ }
}
// We may need the project roots for rule chaining (see below).
@@ -506,8 +525,9 @@ namespace build
// Process prerequisites: do rule chaining for C and C++ source
// files as well as search and match.
//
- for (prerequisite_target& pe: t.prerequisites)
+ for (prerequisite_target& pe: group_prerequisites (t))
{
+ bool group (!pe.belongs (t)); // Target group's prerequisite.
prerequisite& p (pe);
if (!p.is_a<c> () && !p.is_a<cxx> ())
@@ -525,21 +545,37 @@ namespace build
// If this is the obj{} target group, then pick the appropriate
// member and make sure it is searched and matched.
//
+ target* pt;
+
if (obj* o = pe.target->is_a<obj> ())
{
- pe.target = so ? static_cast<target*> (o->so) : o->a;
+ pt = so ? static_cast<target*> (o->so) : o->a;
- if (pe.target == nullptr)
+ if (pt == nullptr)
{
const target_type& type (
so ? objso::static_type : obja::static_type);
- pe.target = &search (
+ pt = &search (
prerequisite_key {&type, &p.dir, &p.name, &p.ext, &p.scope});
}
+
+ // This is a bit tricky: if this is a group's prerequisite,
+ // then we have to keep pe.target pointing to the obj{} group
+ // since there could be another match that uses a different
+ // member of this prerequisite. In this case we specify the
+ // "executor" (see below) which will pick the right member
+ // for one of common algorithms. However, if this is our own
+ // prerequisite, then we are free to hard-wire the member
+ // we need directly in pe.target.
+ //
+ if (!group)
+ pe.target = pt;
}
+ else
+ pt = pe.target;
- build::match (a, *pe.target);
+ build::match (a, *pt);
continue;
}
@@ -557,7 +593,9 @@ namespace build
prerequisite& cp (p);
const target_type& o_type (
- so ? objso::static_type : obja::static_type);
+ group
+ ? obj::static_type
+ : (so ? objso::static_type : obja::static_type));
// Come up with the obj*{} prerequisite. The c(xx){} prerequisite
// directory can be relative (to the scope) or absolute. If it is
@@ -590,7 +628,7 @@ namespace build
// Resolve this prerequisite to target.
//
- target& ot (search (op));
+ target* ot (&search (op));
// If we are cleaning, check that this target is in the same or
// a subdirectory of ours.
@@ -601,7 +639,7 @@ namespace build
// update will come and finish the rewrite process (it will even
// reuse op that we have created but then ignored). So all is good.
//
- if (a.operation () == clean_id && !ot.dir.sub (t.dir))
+ if (a.operation () == clean_id && !ot->dir.sub (t.dir))
{
// If we shouldn't clean obj{}, then it is fair to assume
// we shouldn't clean cxx{} either (generated source will
@@ -612,6 +650,26 @@ namespace build
continue;
}
+ pe.target = ot;
+
+ // If we have created the obj{} target group, pick one of its
+ // members; the rest would be primarily concerned with it.
+ //
+ if (group)
+ {
+ obj& o (static_cast<obj&> (*ot));
+ ot = so ? static_cast<target*> (o.so) : o.a;
+
+ if (ot == nullptr)
+ {
+ const target_type& type (
+ so ? objso::static_type : obja::static_type);
+
+ ot = &search (
+ prerequisite_key {&type, &o.dir, &o.name, &o.ext, nullptr});
+ }
+ }
+
// If this target already exists, then it needs to be "compatible"
// with what we are doing here.
//
@@ -625,7 +683,7 @@ namespace build
// speculatively doesn't really hurt.
//
prerequisite* cp1 (nullptr);
- for (prerequisite& p: ot.prerequisites)
+ for (prerequisite& p: reverse_iterate (group_prerequisites (*ot)))
{
// Ignore some known target types (fsdir, headers).
//
@@ -643,19 +701,19 @@ namespace build
}
fail << "synthesized target for prerequisite " << cp
- << " would be incompatible with existing target " << ot <<
+ << " would be incompatible with existing target " << *ot <<
info << "unknown existing prerequsite type " << p <<
info << "specify corresponding obj{} target explicitly";
}
if (cp1 != nullptr)
{
- build::match (a, ot); // Now cp1 should be resolved.
- search (cp); // Our own prerequisite, so this is ok.
+ build::match (a, *ot); // Now cp1 should be resolved.
+ search (cp); // Our own prerequisite, so this is ok.
if (cp.target != cp1->target)
fail << "synthesized target for prerequisite " << cp
- << " would be incompatible with existing target " << ot <<
+ << " would be incompatible with existing target " << *ot <<
info << "existing prerequsite " << *cp1 << " does not "
<< "match " << cp <<
info << "specify corresponding " << o_type.name << "{} "
@@ -663,40 +721,55 @@ namespace build
}
else
{
- ot.prerequisites.emplace_back (cp);
- build::match (a, ot);
+ // Note: add the source to the group, not member.
+ //
+ pe.target->prerequisites.emplace_back (cp);
+ build::match (a, *ot);
}
// Change the exe{} target's prerequsite ref from cxx{} to obj*{}.
//
pe.prereq = &op;
- pe.target = &ot;
}
// Inject dependency on the output directory.
//
inject_parent_fsdir (a, t);
- switch (a)
+ if (so)
{
- case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean_file;
- default: return default_recipe; // Forward to prerequisites.
+ switch (a)
+ {
+ case perform_update_id: return &perform_update;
+ case perform_clean_id: return &perform_clean<select_so>;
+ default: return default_action<select_so>; // Forward to prerequisites.
+ }
+ }
+ else
+ {
+ switch (a)
+ {
+ case perform_update_id: return &perform_update;
+ case perform_clean_id: return &perform_clean<select_a>;
+ default: return default_action<select_a>; // Forward to prerequisites.
+ }
}
}
target_state link::
perform_update (action a, target& xt)
{
- // @@ Q:
- //
- // - what are we doing with libraries?
- //
path_target& t (static_cast<path_target&> (xt));
- bool so (t.is_a<lib> ());
+ type tt (t.is_a<exe> ()
+ ? type::exe
+ : (t.is_a<liba> () ? type::liba : type::libso));
+
+ bool so (tt == type::libso);
- if (!execute_prerequisites (a, t, t.mtime ()))
+ if (so
+ ? !execute_prerequisites<select_so> (a, t, t.mtime ())
+ : !execute_prerequisites<select_a> (a, t, t.mtime ()))
return target_state::unchanged;
// Translate paths to relative (to working directory) ones. This
@@ -706,24 +779,35 @@ namespace build
vector<path> relo;
scope& rs (*t.root_scope ()); // Shouldn't have matched if nullptr.
- const string& cxx (rs["config.cxx"].as<const string&> ());
+ vector<const char*> args;
+ string storage1;
- vector<const char*> args {cxx.c_str ()};
+ if (tt == type::liba)
+ {
+ //@@ ranlib
+ //
+ args.push_back ("ar");
+ args.push_back ("-rc");
+ args.push_back (relt.string ().c_str ());
+ }
+ else
+ {
+ args.push_back (rs["config.cxx"].as<const string&> ().c_str ());
- append_options (args, t, "cxx.coptions");
+ append_options (args, t, "cxx.coptions");
- string std; // Storage.
- append_std (args, t, std);
+ append_std (args, t, storage1);
- if (so)
- args.push_back ("-shared");
+ if (so)
+ args.push_back ("-shared");
- args.push_back ("-o");
- args.push_back (relt.string ().c_str ());
+ args.push_back ("-o");
+ args.push_back (relt.string ().c_str ());
- append_options (args, t, "cxx.loptions");
+ append_options (args, t, "cxx.loptions");
+ }
- for (target* pt: t.prerequisites)
+ for (target* pt: group_prerequisites (t))
{
if (pt == nullptr)
continue; // Skipped.
@@ -743,7 +827,8 @@ namespace build
args.push_back (relo.back ().string ().c_str ());
}
- append_options (args, t, "cxx.libs");
+ if (tt != type::liba)
+ append_options (args, t, "cxx.libs");
args.push_back (nullptr);