From 650d61845b3f61e9596a8a2dc97458998ba26013 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 8 Jul 2015 14:40:15 +0200 Subject: Implement automatic amalgamation discovery --- build/b.cxx | 38 +-------- build/bin/module.cxx | 8 +- build/config/operation.cxx | 23 +++--- build/file | 17 +++- build/file.cxx | 121 ++++++++++++++++++++++++++--- build/scope | 10 +-- build/target | 10 +-- build/variable | 15 +++- build/variable.ixx | 5 ++ tests/amalgam/config/build/bootstrap.build | 1 + tests/amalgam/config/buildfile | 2 +- tests/amalgam/simple/build/bootstrap.build | 1 - 12 files changed, 167 insertions(+), 84 deletions(-) diff --git a/build/b.cxx b/build/b.cxx index 0291729..1650a73 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -40,42 +40,6 @@ using namespace std; -namespace 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. - // - dir_path - find_src_root (const dir_path& b) - { - for (dir_path d (b); !d.root () && d != home; d = d.directory ()) - { - if (is_src_root (d)) - return d; - } - - return dir_path (); - } - - // The same but for out. Note that we also check whether a - // directory happens to be src_root, in case this is an in- - // tree build. - // - dir_path - find_out_root (const dir_path& b, bool& src) - { - for (dir_path d (b); !d.root () && d != home; d = d.directory ()) - { - if ((src = is_src_root (d)) || is_out_root (d)) - return d; - } - - src = false; - return dir_path (); - } -} - #include #include #include @@ -359,7 +323,7 @@ main (int argc, char* argv[]) // If no src_base was explicitly specified, search for out_root. // bool src; - out_root = find_out_root (out_base, src); + out_root = find_out_root (out_base, &src); // If not found (i.e., we have no idea where the roots are), // then this can mean two things: an in-tree build of a diff --git a/build/bin/module.cxx b/build/bin/module.cxx index b2374ff..ab1c1f5 100644 --- a/build/bin/module.cxx +++ b/build/bin/module.cxx @@ -82,7 +82,7 @@ namespace build // config.bin.lib // { - auto v (base.vars.assign ("bin.lib")); + auto v (base.assign ("bin.lib")); if (!v) v = required (root, "config.bin.lib", "shared").first; } @@ -90,7 +90,7 @@ namespace build // config.bin.exe.lib // { - auto v (base.vars.assign ("bin.exe.lib")); + auto v (base.assign ("bin.exe.lib")); if (!v) v = required (root, "config.bin.exe.lib", exe_lib).first; } @@ -98,7 +98,7 @@ namespace build // config.bin.liba.lib // { - auto v (base.vars.assign ("bin.liba.lib")); + auto v (base.assign ("bin.liba.lib")); if (!v) v = required (root, "config.bin.liba.lib", liba_lib).first; } @@ -106,7 +106,7 @@ namespace build // config.bin.libso.lib // { - auto v (base.vars.assign ("bin.libso.lib")); + auto v (base.assign ("bin.libso.lib")); if (!v) v = required (root, "config.bin.libso.lib", libso_lib).first; } diff --git a/build/config/operation.cxx b/build/config/operation.cxx index e6c258c..300dd22 100644 --- a/build/config/operation.cxx +++ b/build/config/operation.cxx @@ -85,9 +85,10 @@ namespace build << "# feel free to edit." << endl << "#" << endl; - if (auto v = root.vars["amalgamation"]) + auto av = root.vars["amalgamation"]; + if (av && !av.empty ()) { - const dir_path& d (v.as ()); + const dir_path& d (av.as ()); ofs << "# Base configuration inherited from " << d << endl << "#" << endl; @@ -276,20 +277,20 @@ namespace build // Create and bootstrap subproject's root scope. // dir_path out_nroot (out_root / n.dir); - dir_path src_nroot (src_root / n.dir); - scope& nroot (create_root (out_nroot, src_nroot)); + // The same logic to src_root as in create_bootstrap_inner(). + // + scope& nroot (create_root (out_nroot, dir_path ())); bootstrap_out (nroot); - // Check if the bootstrap process changed src_root. - // - const dir_path& p (nroot.vars["src_root"].as ()); + auto val (nroot.assign ("src_root")); - if (src_nroot != p) - fail << "bootstrapped src_root " << p << " does not match " - << "subproject " << src_nroot; + if (!val) + val = is_src_root (out_nroot) + ? out_nroot + : (src_root / n.dir); - nroot.src_path_ = &p; + nroot.src_path_ = &val.as (); bootstrap_src (nroot); diff --git a/build/file b/build/file index 7ac4d4d..8e96f4a 100644 --- a/build/file +++ b/build/file @@ -19,6 +19,21 @@ namespace build bool is_out_root (const dir_path&); + // Given an src_base directory, look for a project's src_root + // based on the presence of known special files. Return empty + // path if not found. + // + dir_path + find_src_root (const dir_path&); + + // The same as above but for project's out. Note that we also + // check whether a directory happens to be src_root, in case + // this is an in-tree build. The second argument is the out + // flag that is set to true if this is src_root. + // + dir_path + find_out_root (const dir_path&, bool* src = nullptr); + void source (const path& buildfile, scope& root, scope& base); @@ -45,7 +60,7 @@ namespace build bootstrap_out (scope& root); // Bootstrap the project's root scope, the src part. Return true if - // we loaded anything. + // we loaded anything (which confirms the src_root is not bogus). // bool bootstrap_src (scope& root); diff --git a/build/file.cxx b/build/file.cxx index 948b6ce..e65d775 100644 --- a/build/file.cxx +++ b/build/file.cxx @@ -31,6 +31,36 @@ namespace build return file_exists (d / path ("build/bootstrap/src-root.build")); } + dir_path + find_src_root (const dir_path& b) + { + for (dir_path d (b); !d.root () && d != home; d = d.directory ()) + { + if (is_src_root (d)) + return d; + } + + return dir_path (); + } + + dir_path + find_out_root (const dir_path& b, bool* src) + { + for (dir_path d (b); !d.root () && d != home; d = d.directory ()) + { + bool s (false); + if ((s = is_src_root (d)) || is_out_root (d)) // Order is important! + { + if (src != nullptr) + *src = s; + + return d; + } + } + + return dir_path (); + } + void source (const path& bf, scope& root, scope& base) { @@ -147,18 +177,91 @@ namespace build { tracer trace ("bootstrap_src"); + bool r (false); + path bf (root.src_path () / path ("build/bootstrap.build")); - if (!file_exists (bf)) - return false; + if (file_exists (bf)) + { + // We assume that bootstrap out cannot load this file explicitly. It + // feels wrong to allow this since that makes the whole bootstrap + // process hard to reason about. But we may try to bootstrap the + // same root scope multiple time. + // + source_once (bf, root, root); + r = true; + } - // We assume that bootstrap out cannot load this file explicitly. It - // feels wrong to allow this since that makes the whole bootstrap - // process hard to reason about. But we may try to bootstrap the - // same root scope multiple time. + // See if we are a part of an amalgamation. There are two key + // players: the outer root scope which may already be present + // (i.e., we were loaded as part of an amalgamation) and the + // amalgamation variable that may or may not be set by the + // user (in bootstrap.build) or by an earlier call to this + // function for the same scope. When set by the user, the + // empty special value means that the project shall not be + // amalgamated. When calculated, the NULL value indicates + // that we are not amalgamated. // - source_once (bf, root, root); - return true; + { + auto rp (root.vars.assign("amalgamation")); // Set NULL by default. + auto& val (rp.first); + const dir_path& d (root.path ()); + + if (scope* aroot = root.parent_scope ()->root_scope ()) + { + const dir_path& ad (aroot->path ()); + dir_path rd (ad.relative (d)); + + // If we already have the amalgamation variable set, verify + // that aroot matches its value. + // + if (!rp.second) + { + if (val.null () || val.empty ()) + { + fail << d << " cannot be amalgamated" << + info << "amalgamated by " << ad; + } + else + { + const dir_path& vd (val.as ()); + + if (vd != rd) + { + fail << "inconsistent amalgamation of " << d << + info << "specified: " << vd << + info << "actual: " << rd << " by " << ad; + } + } + } + else + { + // Otherwise, use the outer root as our amalgamation. + // + level4 ([&]{trace << d << " amalgamated as " << rd;}); + val = move (rd); + } + } + else if (rp.second) + { + // If there is no outer root and the amalgamation variable + // hasn't been set, then we need to check if any of the + // outer directories is a project's out_root. If so, then + // that's our amalgamation. + // + const dir_path& d (root.path ()); + const dir_path& ad (find_out_root (d.directory ())); + + if (!ad.empty ()) + { + dir_path rd (ad.relative (d)); + level4 ([&]{trace << d << " amalgamated as " << rd;}); + val = move (rd); + } + } + } + + return r; } void @@ -166,7 +269,7 @@ namespace build { auto v (root.vars["amalgamation"]); - if (!v) + if (!v || v.empty ()) return; const dir_path& d (v.as ()); diff --git a/build/scope b/build/scope index d1cd941..817ee95 100644 --- a/build/scope +++ b/build/scope @@ -70,16 +70,10 @@ namespace build // returned. // value_proxy - assign (const variable& var) - { - return vars.assign (var); - } + assign (const variable& var) {return vars.assign (var).first;} value_proxy - assign (const std::string& name) - { - return assign (variable_pool.find (name)); - } + assign (const std::string& name) {return vars.assign (name).first;} // Return a value_proxy suitable for appending. If the variable // does not exist in this scope's map, then outer scopes are diff --git a/build/target b/build/target index 954604c..ba51f5e 100644 --- a/build/target +++ b/build/target @@ -254,16 +254,10 @@ namespace build // for details. // value_proxy - assign (const variable& var) - { - return vars.assign (var); - } + assign (const variable& var) {return vars.assign (var).first;} value_proxy - assign (const std::string& name) - { - return assign (variable_pool.find (name)); - } + assign (const std::string& name) {return vars.assign (name).first;} // Return a value_proxy suitable for appending. See class scope // for details. diff --git a/build/variable b/build/variable index 2164f92..064a10a 100644 --- a/build/variable +++ b/build/variable @@ -8,7 +8,7 @@ #include #include // unique_ptr #include // nullptr_t -#include // move() +#include // move(), pair, make_pair() #include #include // hash #include @@ -104,6 +104,9 @@ namespace build bool null () const {return *p == nullptr;} + bool + empty () const; + explicit operator bool () const {return defined () && !null ();} explicit operator value_ptr& () const {return *p;} @@ -267,13 +270,17 @@ namespace build return operator[] (variable_pool.find (name)); } - value_proxy + // The second member in the pair indicates whether new (NULL) + // value was set. + // + std::pair assign (const variable& var) { - return value_proxy (&variable_map_base::operator[] (var), this); + auto r (emplace (var, value_ptr ())); + return std::make_pair (value_proxy (&r.first->second, this), r.second); } - value_proxy + std::pair assign (const std::string& name) { return assign (variable_pool.find (name)); diff --git a/build/variable.ixx b/build/variable.ixx index b0ff021..b97815e 100644 --- a/build/variable.ixx +++ b/build/variable.ixx @@ -4,6 +4,11 @@ namespace build { + // value_proxy + // + inline bool value_proxy:: + empty () const {return as ().empty ();} + inline const value_proxy& value_proxy:: operator= (value_ptr v) const { diff --git a/tests/amalgam/config/build/bootstrap.build b/tests/amalgam/config/build/bootstrap.build index 5951f06..5a01046 100644 --- a/tests/amalgam/config/build/bootstrap.build +++ b/tests/amalgam/config/build/bootstrap.build @@ -1,3 +1,4 @@ project = amalgam-config +amalgamation = # Shall not be amalgamated. subprojects = 1/ 2/ using config diff --git a/tests/amalgam/config/buildfile b/tests/amalgam/config/buildfile index e9fc7f4..7132877 100644 --- a/tests/amalgam/config/buildfile +++ b/tests/amalgam/config/buildfile @@ -1,3 +1,3 @@ -d = 1/ 2/ +d = #1/ 2/ .: $d include $d diff --git a/tests/amalgam/simple/build/bootstrap.build b/tests/amalgam/simple/build/bootstrap.build index 8da7272..698a248 100644 --- a/tests/amalgam/simple/build/bootstrap.build +++ b/tests/amalgam/simple/build/bootstrap.build @@ -1,3 +1,2 @@ project = amalgam-simple -amalgamation = ../ using config -- cgit v1.1