aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/b.cxx54
-rw-r--r--build/diagnostics10
-rw-r--r--build/dump.cxx55
-rw-r--r--build/file32
-rw-r--r--build/file.cxx169
-rw-r--r--build/name10
-rw-r--r--build/operation.cxx2
-rw-r--r--build/parser17
-rw-r--r--build/parser.cxx216
-rw-r--r--build/path-io22
-rw-r--r--build/scope16
11 files changed, 425 insertions, 178 deletions
diff --git a/build/b.cxx b/build/b.cxx
index 49fb027..8d2ca47 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -43,19 +43,6 @@ using namespace std;
namespace build
{
- inline bool
- is_src_root (const path& d)
- {
- return file_exists (d / path ("build/bootstrap.build")) ||
- file_exists (d / path ("build/root.build"));
- }
-
- inline bool
- is_out_root (const path& d)
- {
- return file_exists (d / path ("build/bootstrap/src-root.build"));
- }
-
// Given an src_base directory, look for the project's src_root
// based on the presence of known special files. Return empty
// path if not found.
@@ -88,41 +75,6 @@ namespace build
src = false;
return path ();
}
-
- // Create and bootstrap outer root scopes, if any. Loading is
- // done by root_pre().
- //
- static void
- create_outer_roots (scope& root)
- {
- auto v (root.ro_variables ()["amalgamation"]);
-
- if (!v)
- return;
-
- const path& d (v.as<const path&> ());
- path out_root (root.path () / d);
- path src_root (root.src_path () / d);
- out_root.normalize ();
- src_root.normalize ();
-
- scope& rs (create_root (out_root, src_root));
-
- bootstrap_out (rs);
-
- // Check if the bootstrap process changed src_root.
- //
- const path& p (rs.variables["src_root"].as<const path&> ());
-
- if (src_root != p)
- fail << "bootstrapped src_root " << p << " does not match "
- << "amalgamated " << src_root;
-
- rs.src_path_ = &p;
-
- bootstrap_src (rs);
- create_outer_roots (rs);
- }
}
#include <build/native>
@@ -588,10 +540,10 @@ main (int argc, char* argv[])
}
// Create and bootstrap outer roots if any. Loading is done
- // by root_pre() (that would normally be called by the meta-
- // operation's load() callback below).
+ // by load_root_pre() (that would normally be called by the
+ // meta-operation's load() callback below).
//
- create_outer_roots (rs);
+ create_bootstrap_outer (rs);
// The src bootstrap should have loaded all the modules that
// may add new meta/operations. So at this stage they should
diff --git a/build/diagnostics b/build/diagnostics
index 64a0107..3ec80a3 100644
--- a/build/diagnostics
+++ b/build/diagnostics
@@ -14,7 +14,7 @@
#include <exception>
#include <type_traits>
-#include <build/path>
+#include <build/path> // <build/path-io> included at the end.
namespace build
{
@@ -30,12 +30,6 @@ namespace build
std::string
diag_relative (const path&);
- inline std::ostream&
- operator<< (std::ostream& os, const path& p)
- {
- return os << diag_relative (p);
- }
-
// Action phrases, e.g., "configure update exe{foo}", "updating exe{foo}",
// and "updating exe{foo} already configured".
//
@@ -363,4 +357,6 @@ namespace build
extern const fail_mark<failed> fail;
}
+#include <build/path-io>
+
#endif // BUILD_DIAGNOSTICS
diff --git a/build/dump.cxx b/build/dump.cxx
index 6c1cdcf..05ec679 100644
--- a/build/dump.cxx
+++ b/build/dump.cxx
@@ -7,7 +7,6 @@
#include <set>
#include <string>
#include <cassert>
-#include <iostream>
#include <build/scope>
#include <build/target>
@@ -20,29 +19,30 @@ using namespace std;
namespace build
{
static void
- dump_target (const target& t)
+ dump_target (ostream& os, const target& t)
{
- cerr << t << ':';
+ os << t << ':';
for (const prerequisite& p: t.prerequisites)
{
- cerr << ' ' << p;
+ os << ' ' << p;
}
}
static void
- dump_scope (scope& p,
+ dump_scope (ostream& os,
+ scope& p,
scope_map::iterator& i,
string& ind,
set<const target*>& rts)
{
- string d (diag_relative (p.path ()));
+ string d (relative (p.path ()).string ());
if (d.back () != path::traits::directory_separator)
d += '/';
- cerr << ind << d << ":" << endl
- << ind << '{';
+ os << ind << d << ":" << endl
+ << ind << '{';
const path* orb (relative_base);
relative_base = &p.path ();
@@ -58,16 +58,16 @@ namespace build
const variable& var (e.first);
const value_ptr& val (e.second);
- cerr << endl
- << ind << var.name << " = ";
+ os << endl
+ << ind << var.name << " = ";
if (val == nullptr)
- cerr << "[null]";
+ os << "[null]";
else
{
//@@ TODO: assuming it is a list.
//
- cerr << dynamic_cast<list_value&> (*val);
+ os << dynamic_cast<list_value&> (*val);
}
vb = true;
@@ -79,16 +79,16 @@ namespace build
{
if (vb)
{
- cerr << endl;
+ os << endl;
vb = false;
}
if (sb)
- cerr << endl; // Extra newline between scope blocks.
+ os << endl; // Extra newline between scope blocks.
- cerr << endl;
+ os << endl;
scope& s (i->second);
- dump_scope (s, ++i, ind, rts);
+ dump_scope (os, s, ++i, ind, rts);
sb = true;
}
@@ -129,31 +129,34 @@ namespace build
if (vb || sb)
{
- cerr << endl;
+ os << endl;
vb = sb = false;
}
- cerr << endl
- << ind;
- dump_target (t);
+ os << endl
+ << ind;
+ dump_target (os, t);
}
ind.resize (ind.size () - 2);
relative_base = orb;
- cerr << endl
- << ind << '}';
+ os << endl
+ << ind << '}';
}
void
dump ()
{
- string ind;
- set<const target*> rts;
auto i (scopes.begin ());
scope& g (i->second); // Global scope.
assert (&g == global_scope);
- dump_scope (g, ++i, ind, rts);
- cerr << endl;
+
+ string ind;
+ set<const target*> rts;
+
+ ostream& os (*diag_stream);
+ dump_scope (os, g, ++i, ind, rts);
+ os << endl;
}
}
diff --git a/build/file b/build/file
index 9975e06..32cac48 100644
--- a/build/file
+++ b/build/file
@@ -6,10 +6,18 @@
#define BUILD_FILE
#include <build/path>
+#include <build/name>
namespace build
{
class scope;
+ struct location;
+
+ bool
+ is_src_root (const path&);
+
+ bool
+ is_out_root (const path&);
void
source (const path& buildfile, scope& root, scope& base);
@@ -25,7 +33,7 @@ namespace build
void
source_once (const path& buildfile, scope& root, scope& base, scope& once);
- // Create project's root scope. Only set the src_root variable is the
+ // Create project's root scope. Only set the src_root variable if the
// passed src_root value is not empty.
//
scope&
@@ -42,10 +50,28 @@ namespace build
bool
bootstrap_src (scope& root);
- // Load project's root[-pre].build unless already loaded.
+ // Create and bootstrap outer root scopes, if any. Loading is
+ // done by load_root_pre() below.
+ //
+ void
+ create_bootstrap_outer (scope& root);
+
+ // Create and bootstrap inner root scopes between root and base,
+ // if any. Return the innermost created root scope or root if
+ // none were created. Loading is done by load_root_pre() below.
+ //
+ scope&
+ create_bootstrap_inner (scope& root, const path& out_base);
+
+ // Load project's root[-pre].build unless already loaded. Also
+ // make sure all outer root scopes are loaded prior to loading
+ // this root scope.
//
void
- root_pre (scope& root);
+ load_root_pre (scope& root);
+
+ void
+ import (scope& base, const name&, const location&);
}
#include <build/file.ixx>
diff --git a/build/file.cxx b/build/file.cxx
index 0b0220d..70a5ed8 100644
--- a/build/file.cxx
+++ b/build/file.cxx
@@ -15,6 +15,19 @@ using namespace std;
namespace build
{
+ bool
+ is_src_root (const path& d)
+ {
+ return file_exists (d / path ("build/bootstrap.build")) ||
+ file_exists (d / path ("build/root.build"));
+ }
+
+ bool
+ is_out_root (const path& d)
+ {
+ return file_exists (d / path ("build/bootstrap/src-root.build"));
+ }
+
void
source (const path& bf, scope& root, scope& base)
{
@@ -146,18 +159,170 @@ namespace build
}
void
- root_pre (scope& root)
+ create_bootstrap_outer (scope& root)
+ {
+ auto v (root.ro_variables ()["amalgamation"]);
+
+ if (!v)
+ return;
+
+ const path& d (v.as<const path&> ());
+ path out_root (root.path () / d);
+ path src_root (root.src_path () / d);
+ out_root.normalize ();
+ src_root.normalize ();
+
+ scope& rs (create_root (out_root, src_root));
+
+ bootstrap_out (rs);
+
+ // Check if the bootstrap process changed src_root.
+ //
+ const path& p (rs.variables["src_root"].as<const path&> ());
+
+ if (src_root != p)
+ fail << "bootstrapped src_root " << p << " does not match "
+ << "amalgamated " << src_root;
+
+ rs.src_path_ = &p;
+
+ bootstrap_src (rs);
+ create_bootstrap_outer (rs);
+ }
+
+ scope&
+ create_bootstrap_inner (scope& root, const path& out_base)
+ {
+ if (auto v = root.ro_variables ()["subprojects"])
+ {
+ for (const name& n: v.as<const list_value&> ())
+ {
+ // Should be a list of directories.
+ //
+ if (!n.type.empty () || !n.value.empty () || n.dir.empty ())
+ fail << "expected directory in subprojects variable "
+ << "instead of " << n;
+
+ path out_root (root.path () / n.dir);
+
+ if (!out_base.sub (out_root))
+ continue;
+
+ path src_root (root.src_path () / n.dir);
+ scope& rs (create_root (out_root, src_root));
+
+ bootstrap_out (rs);
+
+ // Check if the bootstrap process changed src_root.
+ //
+ const path& p (rs.variables["src_root"].as<const path&> ());
+
+ if (src_root != p)
+ fail << "bootstrapped src_root " << p << " does not match "
+ << "subproject " << src_root;
+
+ rs.src_path_ = &p;
+
+ bootstrap_src (rs);
+
+ // See if there are more inner roots.
+ //
+ return create_bootstrap_inner (rs, out_base);
+ }
+ }
+
+ return root;
+ }
+
+ void
+ load_root_pre (scope& root)
{
tracer trace ("root_pre");
// First load outer roots, if any.
//
if (scope* rs = root.parent_scope ()->root_scope ())
- root_pre (*rs);
+ load_root_pre (*rs);
path bf (root.src_path () / path ("build/root.build"));
if (file_exists (bf))
source_once (bf, root, root);
}
+
+ void
+ import (scope& ibase, const name& n, const location& l)
+ {
+ scope& iroot (*ibase.root_scope ());
+
+ // Figure out this project's out_root.
+ //
+ const variable& var (variable_pool.find ("config." + n.value));
+ auto val (iroot[var]);
+
+ if (val)
+ {
+ if (val.scope == global_scope)
+ iroot.variables[var] = val; // Copy into root scope.
+ }
+ else
+ fail (l) << "unable to find out_root for imported " << n <<
+ info << "consider explicitly configuring its out_root via the "
+ << var.name << " command line variable";
+
+ const path& out_root (val.as<const path&> ());
+
+ // Bootstrap the imported root scope. This is pretty similar to
+ // what we do in main() except that here we don't try to guess
+ // src_root.
+ //
+ path src_root (is_src_root (out_root) ? out_root : path ());
+ scope& root (create_root (out_root, src_root));
+
+ bootstrap_out (root);
+
+ // Check that the bootstrap process set src_root.
+ //
+ if (auto v = root.ro_variables ()["src_root"])
+ {
+ const path& p (v.as<const path&> ());
+
+ if (!src_root.empty () && p != src_root)
+ fail << "bootstrapped src_root " << p << " does not match "
+ << "discovered " << src_root;
+
+ root.src_path_ = &p;
+ }
+ else
+ fail (l) << "unable to determine src_root for imported " << n <<
+ info << "consider configuring " << out_root;
+
+ bootstrap_src (root);
+
+ // Bootstrap outer roots if any. Loading will be done by
+ // load_root_pre() below.
+ //
+ create_bootstrap_outer (root);
+
+ // Load the imported root scope.
+ //
+ load_root_pre (root);
+
+ // Create a temporary scope so that the export stub does not mess
+ // up any of our variables.
+ //
+ temp_scope ts (ibase);
+
+ // "Pass" the imported project's roots to the stub.
+ //
+ ts.variables["out_root"] = out_root;
+ ts.variables["src_root"] = src_root;
+
+ // 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);
+ }
}
diff --git a/build/name b/build/name
index f45930e..cf7427b 100644
--- a/build/name
+++ b/build/name
@@ -33,6 +33,16 @@ namespace build
name (std::string t, path d, std::string v)
: type (std::move (t)), dir (std::move (d)), value (std::move (v)) {}
+ bool
+ empty () const {type.empty () && dir.empty () && value.empty ();}
+
+ bool
+ simple () const {return type.empty () && dir.empty ();}
+
+ bool
+ directory () const
+ {return type.empty () && !dir.empty () && value.empty ();}
+
std::string type;
path dir;
std::string value;
diff --git a/build/operation.cxx b/build/operation.cxx
index 0d6bfcd..a8fdfb1 100644
--- a/build/operation.cxx
+++ b/build/operation.cxx
@@ -41,7 +41,7 @@ namespace build
{
// Load project's root[-pre].build.
//
- root_pre (root);
+ load_root_pre (root);
// Create the base scope. Note that its existence doesn't
// mean it was already processed as a base scope; it can
diff --git a/build/parser b/build/parser
index 1597432..53ce2d0 100644
--- a/build/parser
+++ b/build/parser
@@ -54,6 +54,12 @@ namespace build
include (token&, token_type&);
void
+ import (token&, token_type&);
+
+ void
+ export_ (token&, token_type&);
+
+ void
using_ (token&, token_type&);
void
@@ -80,11 +86,13 @@ namespace build
// Utilities.
//
private:
- void
- process_default_target (token&);
+ // Switch to new root scope and return the previous one.
+ //
+ scope*
+ switch_root (scope*);
void
- create_inner_roots (const path&);
+ process_default_target (token&);
// Lexer.
//
@@ -112,10 +120,9 @@ namespace build
lexer* lexer_;
scope* scope_; // Current base scope (out_base).
scope* root_; // Current root scope (out_root).
- target* default_target_;
-
const path* out_root_;
const path* src_root_;
+ target* default_target_;
token peek_ {token_type::eos, false, 0, 0};
bool peeked_ {false};
diff --git a/build/parser.cxx b/build/parser.cxx
index 4d5cbbe..d3abd46 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -42,18 +42,10 @@ namespace build
lexer l (is, rw);
lexer_ = &l;
scope_ = &base;
- root_ = &root;
+ root_ = nullptr;
+ switch_root (&root);
default_target_ = nullptr;
- out_root_ = &root.ro_variables ()["out_root"].as<const path&> ();
-
- // During bootstrap we may not know src_root yet.
- //
- {
- auto v (root.ro_variables ()["src_root"]);
- src_root_ = v ? &v.as<const path&> () : nullptr;
- }
-
token t (type::eos, false, 0, 0);
type tt;
next (t, tt);
@@ -123,6 +115,18 @@ namespace build
include (t, tt);
continue;
}
+ else if (n == "import")
+ {
+ next (t, tt);
+ import (t, tt);
+ continue;
+ }
+ else if (n == "export")
+ {
+ next (t, tt);
+ export_ (t, tt);
+ continue;
+ }
else if (n == "using")
{
next (t, tt);
@@ -186,6 +190,8 @@ namespace build
if (dir)
{
+ // Directory scope.
+ //
scope& prev (*scope_);
path p (move (ns[0].dir)); // Steal.
@@ -196,10 +202,19 @@ namespace build
p.normalize ();
scope_ = &scopes[p];
+ // If this is a known project root scope, switch the
+ // parser state to use it.
+ //
+ scope* ors (switch_root (scope_->root () ? scope_ : root_));
+
+ if (ors != root_)
+ level4 ([&]{trace (nloc) << "switching to root scope " << p;});
+
// A directory scope can contain anything that a top level can.
//
clause (t, tt);
+ switch_root (ors);
scope_ = &prev;
}
else
@@ -425,7 +440,7 @@ namespace build
//
if (src_root_ == nullptr)
{
- auto v ((*root_)["src_root"]);
+ auto v (root_->ro_variables ()["src_root"]);
src_root_ = v ? &v.as<const path&> () : nullptr;
}
}
@@ -436,65 +451,6 @@ namespace build
fail (t) << "expected newline instead of " << t;
}
- // Create, bootstrap, and load outer root scopes, if any. Also
- // update the parser state to point to the innermost root scope.
- //
- void parser::
- create_inner_roots (const path& out_base)
- {
- auto v (root_->ro_variables ()["subprojects"]);
-
- if (!v)
- return;
-
- for (const name& n: v.as<const list_value&> ())
- {
- // Should be a list of directories.
- //
- if (!n.type.empty () || !n.value.empty () || n.dir.empty ())
- fail << "expected directory in subprojects variable "
- << "instead of " << n;
-
- path out_root (*out_root_ / n.dir);
-
- if (!out_base.sub (out_root))
- continue;
-
- path src_root (*src_root_ / n.dir);
- scope& rs (create_root (out_root, src_root));
-
- bootstrap_out (rs);
-
- // Check if the bootstrap process changed src_root.
- //
- const path& p (rs.variables["src_root"].as<const path&> ());
-
- if (src_root != p)
- fail << "bootstrapped src_root " << p << " does not match "
- << "subproject " << src_root;
-
- rs.src_path_ = &p;
-
- bootstrap_src (rs);
-
- // Load the root scope.
- //
- root_pre (rs);
-
- // Update parser state.
- //
- root_ = &rs;
- out_root_ = &rs.variables["out_root"].as<const path&> ();
- src_root_ = &p;
-
- // See if there are more inner roots.
- //
- create_inner_roots (out_base);
-
- break; // Can only be in one sub-project.
- }
- }
-
void parser::
include (token& t, token_type& tt)
{
@@ -570,11 +526,14 @@ namespace build
out_base = out_src (src_base, *out_root_, *src_root_);
}
- // Create, bootstrap and load root scope(s) of subproject(s) that
- // this out_base belongs to.
+ // Create and bootstrap root scope(s) of subproject(s) that
+ // this out_base belongs to. If any were created, load them
+ // and update parser state.
//
- scope* ors (root_);
- create_inner_roots (out_base);
+ scope* ors (switch_root (&create_bootstrap_inner (*root_, out_base)));
+
+ if (root_ != ors)
+ load_root_pre (*root_); // Loads outer roots recursively.
ifstream ifs (p.string ());
@@ -620,12 +579,79 @@ namespace build
lexer_ = ol;
path_ = op;
- if (root_ != ors)
- {
- root_ = ors;
- out_root_ = &root_->ro_variables ()["out_root"].as<const path&> ();
- src_root_ = &root_->ro_variables ()["src_root"].as<const path&> ();
- }
+ switch_root (ors);
+ }
+
+ if (tt == type::newline)
+ next (t, tt);
+ else if (tt != type::eos)
+ fail (t) << "expected newline instead of " << t;
+ }
+
+ void parser::
+ import (token& t, token_type& tt)
+ {
+ tracer trace ("parser::import", &path_);
+
+ if (src_root_ == nullptr)
+ fail (t) << "import during bootstrap";
+
+ // The rest should be a list of projects and/or targets. Parse
+ // them as names to get variable expansion and directory prefixes.
+ //
+ location l (get_location (t, &path_));
+ names_type ns (tt != type::newline && tt != type::eos
+ ? names (t, tt)
+ : names_type ());
+
+ for (name& n: ns)
+ {
+ // For now we only support project names.
+ //
+ if (!n.simple ())
+ fail (l) << "project name expected instead of " << n;
+
+
+ build::import (*scope_, n, l);
+ }
+
+ if (tt == type::newline)
+ next (t, tt);
+ else if (tt != type::eos)
+ fail (t) << "expected newline instead of " << t;
+ }
+
+ void parser::
+ export_ (token& t, token_type& tt)
+ {
+ tracer trace ("parser::export", &path_);
+
+ scope* ps (scope_->parent_scope ());
+
+ // This should be temp_scope.
+ //
+ 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.
+ //
+ 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_->ro_variables ()[var])
+ ps->variables[var] = val; //@@ Move?
+ else
+ fail (l) << "undefined exported variable " << var.name;
}
if (tt == type::newline)
@@ -672,10 +698,11 @@ namespace build
void parser::
print (token& t, token_type& tt)
{
- for (; tt != type::newline && tt != type::eos; next (t, tt))
- cout << t;
+ names_type ns (tt != type::newline && tt != type::eos
+ ? names (t, tt)
+ : names_type ());
- cout << endl;
+ cout << ns << endl;
if (tt != type::eos)
next (t, tt); // Swallow newline.
@@ -1303,6 +1330,29 @@ namespace build
return bs;
}
+ scope* parser::
+ switch_root (scope* nr)
+ {
+ scope* r (root_);
+
+ if (nr != root_)
+ {
+ root_ = nr;
+
+ // During bootstrap we may not know src_root yet. We are also
+ // not using the scopes's path() and src_path() since pointers
+ // to their return values are not guaranteed to be stable (and,
+ // in fact, path()'s is not).
+ //
+ out_root_ = &root_->ro_variables ()["out_root"].as<const path&> ();
+
+ auto v (root_->ro_variables ()["src_root"]);
+ src_root_ = v ? &v.as<const path&> () : nullptr;
+ }
+
+ return r;
+ }
+
void parser::
process_default_target (token& t)
{
diff --git a/build/path-io b/build/path-io
new file mode 100644
index 0000000..a9deea9
--- /dev/null
+++ b/build/path-io
@@ -0,0 +1,22 @@
+// file : build/path-io -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_PATH_IO
+#define BUILD_PATH_IO
+
+#include <ostream>
+
+#include <build/path>
+#include <build/diagnostics>
+
+namespace build
+{
+ inline std::ostream&
+ operator<< (std::ostream& os, const path& p)
+ {
+ return os << (&os == diag_stream ? diag_relative (p) : p.string ());
+ }
+}
+
+#endif // BUILD_PATH_IO
diff --git a/build/scope b/build/scope
index 2b621e7..2eb8ac7 100644
--- a/build/scope
+++ b/build/scope
@@ -88,6 +88,8 @@ namespace build
private:
friend class scope_map;
+ friend class temp_scope;
+
typedef path_map<scope>::const_iterator iterator;
scope (): variables (*this) {}
@@ -97,6 +99,20 @@ namespace build
scope* root_;
};
+ // Temporary scope. The idea is to be able to create a temporary
+ // scope in order not to change the variables in the current scope.
+ // Such a scope is not entered in to the scope map. As a result it
+ // can only be used as a temporary set of variables. In particular,
+ // defining targets/prerequsites directly in such a scope will surely
+ // end up badly. Defining any nested scopes will be as if defining
+ // such a scope in the parent (since path() returns parent's path).
+ //
+ class temp_scope: public scope
+ {
+ public:
+ temp_scope (scope& p) {i_ = p.i_; parent_ = &p; root_ = p.root_;}
+ };
+
class scope_map: public path_map<scope>
{
public: