aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/algorithm.cxx2
-rw-r--r--build/cxx/rule.cxx87
-rw-r--r--build/dump.cxx32
-rw-r--r--build/file3
-rw-r--r--build/file.cxx117
-rw-r--r--build/name2
-rw-r--r--build/parser11
-rw-r--r--build/parser.cxx56
-rw-r--r--build/target6
-rw-r--r--build/variable10
10 files changed, 259 insertions, 67 deletions
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index e6a71df..95724cf 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -175,7 +175,7 @@ namespace build
{
target& pt (search (p));
- if (p.target->dir.sub (d))
+ if (pt.dir.sub (d))
{
match (a, pt);
t.prerequisite_targets.push_back (&pt);
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index ec2a0b9..a23e81e 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -68,6 +68,22 @@ namespace build
}
}
+ // Append library options from one of the cxx.export.* variables
+ // recursively, prerequisite libraries first.
+ //
+ static void
+ append_lib_options (vector<const char*>& args, target& l, const char* var)
+ {
+ for (target* t: l.prerequisite_targets)
+ {
+ if (t != nullptr &&
+ (t->is_a<lib> () || t->is_a<liba> () || t->is_a<libso> ()))
+ append_lib_options (args, *t, var);
+ }
+
+ append_options (args, l, var);
+ }
+
// compile
//
void* compile::
@@ -121,12 +137,25 @@ namespace build
// When cleaning, ignore prerequisites that are not in the same
// or a subdirectory of ours.
//
- switch (a.operation ())
+ for (prerequisite& p: group_prerequisites (t))
{
- case default_id:
- case update_id: search_and_match (a, t); break;
- case clean_id: search_and_match (a, t, t.dir); break;
- default: assert (false);
+ target& pt (search (p));
+
+ if (a.operation () == clean_id && !pt.dir.sub (t.dir))
+ continue;
+
+ build::match (a, pt);
+
+ // A dependency on a library is there so that we can get its
+ // cxx.export.poptions. In particular, making sure it is
+ // executed before us will only restrict parallelism. But we
+ // do need to match it in order to get its prerequisite_targets
+ // populated; see append_lib_options() above.
+ //
+ if (pt.is_a<lib> () || pt.is_a<liba> () || pt.is_a<libso> ())
+ continue;
+
+ t.prerequisite_targets.push_back (&pt);
}
// Inject additional prerequisites. For now we only do it for
@@ -205,6 +234,16 @@ namespace build
vector<const char*> args {cxx.c_str ()};
+ // Add cxx.export.poptions from prerequisite libraries.
+ //
+ for (prerequisite& p: group_prerequisites (t))
+ {
+ target& pt (*p.target); // Already searched and matched.
+
+ if (pt.is_a<lib> () || pt.is_a<liba> () || pt.is_a<libso> ())
+ append_lib_options (args, pt, "cxx.export.poptions");
+ }
+
append_options (args, t, "cxx.poptions");
// @@ Some C++ options (e.g., -std, -m) affect the preprocessor.
@@ -369,6 +408,16 @@ namespace build
vector<const char*> args {cxx.c_str ()};
+ // Add cxx.export.poptions from prerequisite libraries.
+ //
+ for (prerequisite& p: group_prerequisites (t))
+ {
+ target& pt (*p.target); // Already searched and matched.
+
+ if (pt.is_a<lib> () || pt.is_a<liba> () || pt.is_a<libso> ())
+ append_lib_options (args, pt, "cxx.export.poptions");
+ }
+
append_options (args, t, "cxx.poptions");
append_options (args, t, "cxx.coptions");
@@ -586,6 +635,7 @@ namespace build
// We may need the project roots for rule chaining (see below).
// We will resolve them lazily only if needed.
//
+ scope* root (nullptr);
const dir_path* out_root (nullptr);
const dir_path* src_root (nullptr);
@@ -673,16 +723,16 @@ namespace build
continue;
}
- if (out_root == nullptr)
+ if (root == nullptr)
{
// Which scope shall we use to resolve the root? Unlikely,
// but possible, the prerequisite is from a different project
// altogether. So we are going to use the target's project.
//
- scope* rs (t.root_scope ());
- assert (rs != nullptr); // Shouldn't have matched.
- out_root = &rs->path ();
- src_root = &rs->src_path ();
+ root = t.root_scope ();
+ assert (root != nullptr); // Shouldn't have matched.
+ out_root = &root->path ();
+ src_root = &root->src_path ();
}
prerequisite& cp (p);
@@ -817,6 +867,23 @@ namespace build
// Note: add the source to the group, not the member.
//
pt->prerequisites.emplace_back (cp);
+
+ // Add our imported lib*{} prerequisites to the object file (see
+ // cxx.export.poptions above for details).
+ //
+ for (prerequisite& p: group_prerequisites (t))
+ {
+ if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libso> ())
+ {
+ // Check that it is imported, that is its root scope differs
+ // from ours.
+ //
+ if (p.dir.absolute () && // Imported is always absolute.
+ scopes.find (p.dir).root_scope () != root)
+ pt->prerequisites.emplace_back (p);
+ }
+ }
+
build::match (a, *ot);
}
diff --git a/build/dump.cxx b/build/dump.cxx
index 3f8eb15..3f3bd93 100644
--- a/build/dump.cxx
+++ b/build/dump.cxx
@@ -28,31 +28,31 @@ namespace build
os << ':';
- // If the target has been matched to a rule, print resolved
+ for (const prerequisite& p: t.prerequisites)
+ {
+ os << ' ';
+
+ // Print it as target if one has been cached.
+ //
+ if (p.target != nullptr)
+ os << *p.target;
+ else
+ os << p;
+ }
+
+ // If the target has been matched to a rule, also print resolved
// prerequisite targets.
//
if (t.recipe (a))
{
+ bool first (true);
for (const target* pt: t.prerequisite_targets)
{
if (pt == nullptr) // Skipped.
continue;
- os << ' ' << *pt;
- }
- }
- else
- {
- for (const prerequisite& p: t.prerequisites)
- {
- os << ' ';
-
- // Print it as target if one has been cached.
- //
- if (p.target != nullptr)
- os << *p.target;
- else
- os << p;
+ os << (first ? " | " : " ") << *pt;
+ first = false;
}
}
}
diff --git a/build/file b/build/file
index 937be1b..4dbe9fe 100644
--- a/build/file
+++ b/build/file
@@ -7,6 +7,7 @@
#include <build/path>
#include <build/name>
+#include <build/variable> // list_value
namespace build
{
@@ -70,7 +71,7 @@ namespace build
void
load_root_pre (scope& root);
- void
+ list_value
import (scope& base, const name&, const location&);
}
diff --git a/build/file.cxx b/build/file.cxx
index c130663..1e42079 100644
--- a/build/file.cxx
+++ b/build/file.cxx
@@ -7,12 +7,11 @@
#include <fstream>
#include <build/scope>
+#include <build/context>
#include <build/parser>
#include <build/filesystem>
#include <build/diagnostics>
-#include <build/config/utility>
-
using namespace std;
namespace build
@@ -252,19 +251,85 @@ namespace build
source_once (bf, root, root);
}
- void
+ list_value
import (scope& ibase, const name& n, const location& l)
{
+ tracer trace ("import");
+
+ // Split the name into the project and target.
+ //
+ string project;
+ name target;
+
+ if (n.dir.empty ())
+ {
+ if (!n.simple ())
+ fail << "project name expected before imported target " << n;
+
+ // Note that value can be foo/bar/baz; in this case probably
+ // means sub-projects? Or only to a certain point, then
+ // (untyped) target? Looks like I will need to scan anything
+ // that looks like a directory checking if this is a subproject.
+ // If not, then that's part of the target.
+ //
+ project = n.value;
+ }
+ else
+ {
+ //@@ This can be a path inside a sub-project. So, eventually,
+ // we should find the innermost sub-project and load the
+ // export stub from there (will probably still have to
+ // resolve root from the top-level project). For now we
+ // assume the project is always top-level.
+ //
+ project = *n.dir.begin ();
+
+ target.dir = n.dir.leaf (dir_path (project));
+ target.type = n.type;
+ target.value = n.value;
+ }
+
scope& iroot (*ibase.root_scope ());
// Figure out this project's out_root.
//
- string var ("config." + n.value);
- const dir_path& out_root (
- config::required (iroot, var.c_str (), dir_path ()).first);
+ dir_path out_root;
+ string var ("config." + project);
- if (out_root.empty ())
- fail (l) << "unable to find out_root for imported " << n <<
+ if (auto v = iroot[var])
+ {
+ if (!v.belongs (*global_scope)) // A value from (some) config.build.
+ out_root = v.as<const dir_path&> ();
+ else
+ {
+ // Process the path by making it absolute and normalized. Also,
+ // for usability's sake, treat a simple name that doesn't end
+ // with '/' as a directory.
+ //
+ const list_value& lv (v.as<const list_value&> ());
+
+ if (lv.size () == 1)
+ {
+ const name& n (lv.front ());
+
+ if (n.directory ())
+ out_root = n.dir;
+ else if (n.simple ())
+ out_root = dir_path (n.value);
+ }
+
+ if (out_root.empty ())
+ fail (l) << "invalid " << var << " value " << lv;
+
+ if (out_root.relative ())
+ out_root = work / out_root;
+
+ out_root.normalize ();
+ iroot.assign (var) = out_root;
+ }
+ }
+ else
+ fail (l) << "unable to find out_root for imported " << project <<
info << "consider explicitly configuring its out_root via the "
<< var << " command line variable";
@@ -290,7 +355,7 @@ namespace build
root.src_path_ = &p;
}
else
- fail (l) << "unable to determine src_root for imported " << n <<
+ fail (l) << "unable to determine src_root for imported " << project <<
info << "consider configuring " << out_root;
bootstrap_src (root);
@@ -311,14 +376,42 @@ namespace build
// "Pass" the imported project's roots to the stub.
//
- ts.assign ("out_root") = out_root;
- ts.assign ("src_root") = src_root;
+ ts.assign ("out_root") = move (out_root);
+ ts.assign ("src_root") = move (src_root);
+
+ // Also pass the target being imported.
+ //
+ {
+ auto v (ts.assign ("target"));
+
+ if (!target.empty ()) // Otherwise leave NULL.
+ v = list_value {move (target)};
+ }
// Load the export stub. Note that it is loaded in the context
// of the importing project, not the imported one. The export
// stub will normally switch to the imported root scope at some
// point.
//
- source (root.src_path () / path ("build/export.build"), iroot, ts);
+ path es (root.src_path () / path ("build/export.build"));
+ ifstream ifs (es.string ());
+ if (!ifs.is_open ())
+ fail << "unable to open " << es;
+
+ level4 ([&]{trace << "importing " << es;});
+
+ ifs.exceptions (ifstream::failbit | ifstream::badbit);
+ parser p;
+
+ try
+ {
+ p.parse_buildfile (ifs, es, iroot, ts);
+ }
+ catch (const std::ios_base::failure&)
+ {
+ fail << "failed to read from " << es;
+ }
+
+ return p.export_value ();
}
}
diff --git a/build/name b/build/name
index b0fd6cb..0af944a 100644
--- a/build/name
+++ b/build/name
@@ -24,6 +24,8 @@ namespace build
//
struct name
{
+ name () = default;
+
explicit
name (std::string v): value (std::move (v)) {}
diff --git a/build/parser b/build/parser
index 6168645..94bf7f3 100644
--- a/build/parser
+++ b/build/parser
@@ -7,11 +7,13 @@
#include <string>
#include <iosfwd>
+#include <utility> // move()
#include <build/path>
#include <build/token>
#include <build/name>
#include <build/spec>
+#include <build/variable> // list_value
#include <build/diagnostics>
namespace build
@@ -36,6 +38,14 @@ namespace build
token
parse_variable (lexer&, scope&, std::string name, token_type kind);
+ list_value
+ export_value ()
+ {
+ list_value r (std::move (export_value_));
+ export_value_.clear (); // Empty state.
+ return r;
+ }
+
// Recursive descent parser.
//
private:
@@ -123,6 +133,7 @@ namespace build
const dir_path* out_root_;
const dir_path* src_root_;
target* default_target_;
+ list_value export_value_;
token peek_ {token_type::eos, false, 0, 0};
bool peeked_ {false};
diff --git a/build/parser.cxx b/build/parser.cxx
index 4275411..443253c 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -605,6 +605,26 @@ namespace build
if (src_root_ == nullptr)
fail (t) << "import during bootstrap";
+ // General import format:
+ //
+ // import [<var>=](<project>|<project>/<target>])+
+ //
+ value_proxy val;
+ token_type at; // Assignment type.
+ if (tt == type::name)
+ {
+ at = peek ();
+
+ if (at == token_type::equal || at == token_type::plus_equal)
+ {
+ val.rebind (at == token_type::equal
+ ? scope_->assign (t.name ())
+ : scope_->append (t.name ()));
+ next (t, tt); // Consume =/+=.
+ next (t, tt);
+ }
+ }
+
// The rest should be a list of projects and/or targets. Parse
// them as names to get variable expansion and directory prefixes.
//
@@ -615,13 +635,15 @@ namespace build
for (name& n: ns)
{
- // For now we only support project names.
- //
- if (!n.simple ())
- fail (l) << "project name expected instead of " << n;
+ list_value r (build::import (*scope_, n, l));
-
- build::import (*scope_, n, l);
+ if (val.defined ())
+ {
+ if (at == token_type::equal)
+ val = move (r);
+ else
+ val += move (r);
+ }
}
if (tt == type::newline)
@@ -642,26 +664,12 @@ namespace build
if (ps == nullptr || ps->path () != scope_->path ())
fail (t) << "export outside export stub";
- // The rest should be a list of variables. Parse them as names
- // to get variable expansion.
+ // The rest is a value. Parse it as names to get variable expansion.
//
location l (get_location (t, &path_));
- names_type ns (tt != type::newline && tt != type::eos
- ? names (t, tt)
- : names_type ());
-
- for (name& n: ns)
- {
- if (!n.simple ())
- fail (l) << "variable name expected instead of " << n;
-
- const auto& var (variable_pool.find (n.value));
-
- if (auto val = scope_->vars[var])
- ps->assign (var) = val; //@@ Move?
- else
- fail (l) << "undefined exported variable " << var.name;
- }
+ export_value_ = (tt != type::newline && tt != type::eos
+ ? names (t, tt)
+ : names_type ());
if (tt == type::newline)
next (t, tt);
diff --git a/build/target b/build/target
index 0aa0227..7776a77 100644
--- a/build/target
+++ b/build/target
@@ -335,15 +335,15 @@ namespace build
iterator
operator-- (int) {iterator r (*this); return --r;}
+ reference operator* () const {return *i_;}
+ pointer operator-> () const {return i_.operator -> ();}
+
friend bool
operator== (const iterator& x, const iterator& y)
{
return x.t_ == y.t_ && x.c_ == y.c_ && x.i_ == y.i_;
}
- reference operator* () const {return *i_;}
- pointer operator-> () const {return i_.operator -> ();}
-
friend bool
operator!= (const iterator& x, const iterator& y) {return !(x == y);}
diff --git a/build/variable b/build/variable
index 59db060..0ccd242 100644
--- a/build/variable
+++ b/build/variable
@@ -67,6 +67,8 @@ namespace build
struct list_value: value, names
{
public:
+ using names::names;
+
list_value () = default;
list_value (names d): names (std::move (d)) {}
list_value (std::string d) {emplace_back (std::move (d));}
@@ -87,6 +89,10 @@ namespace build
// value_proxy
//
// A variable can be undefined, null, or contain some actual value.
+ // Note that once value_proxy is bound to a value, the only way to
+ // rebind it to a different value is by using explicit rebind(). In
+ // particular, assigning one value proxy to another will assing the
+ // values.
//
struct variable_map;
@@ -148,8 +154,12 @@ namespace build
//
const variable_map* map; // Variable map to which this value belongs.
+ value_proxy (): map (nullptr), p (nullptr) {}
value_proxy (value_ptr* p, const variable_map* m): map (m), p (p) {}
+ void
+ rebind (const value_proxy& x) {map = x.map; p = x.p;}
+
private:
value_ptr* p;
};