From 233255f0e14f364841751755958375fe27380ba6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 14 Mar 2017 11:37:14 +0200 Subject: Implement implied buildfile support In essence, if the buildfile is: ./: */ Then it can be omitted entirely (provided there is at least one subdirectory). --- build2/b.cxx | 36 ++++++++++++++++++++++++++--------- build2/config/operation.cxx | 1 + build2/operation | 15 ++++++++++----- build2/operation.cxx | 19 +++++++++++++------ build2/spec | 2 +- build2/target | 7 ++++++- build2/target.cxx | 22 ++++++++++++++++------ build2/target.txx | 46 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 120 insertions(+), 28 deletions(-) (limited to 'build2') diff --git a/build2/b.cxx b/build2/b.cxx index 15ace65..b13300e 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -911,16 +911,31 @@ main (int argc, char* argv[]) trace << " amalgamat: " << cast (l); } + path bf; const path& bfn (ops.buildfile ()); - path bf (bfn.string () != "-" ? src_base / bfn : bfn); - // If we were guessing src_base, check that the buildfile - // exists and if not, issue more detailed diagnostics. - // - if (guessing && bf.string () != "-" && !exists (bf)) - fail << bf << " does not exist" - << info << "consider explicitly specifying src_base " - << "for " << tn; + if (bfn.string () != "-") + { + bf = src_base / bfn; + + if (!exists (bf)) + { + // If we were guessing src_base then don't try any implied + // buildfile tricks. + // + if (guessing) + fail << bf << " does not exist" << + info << "consider explicitly specifying src_base for " << tn; + + // If the target is a directory and src_base exists, then assume + // implied buildfile; see dir::search_implied(). + // + if ((tn.directory () || tn.type == "dir") && exists (src_base)) + bf.clear (); + } + } + else + bf = bfn; // Enter project-wide (as opposed to global) variable overrides. // @@ -1040,7 +1055,10 @@ main (int argc, char* argv[]) ? out_src (d, rs) : dir_path ()); - mif->search (rs, target_key {ti, &d, &out, &tn.value, e}, l, tgs); + mif->search (rs, bs, + target_key {ti, &d, &out, &tn.value, e}, + l, + tgs); } } // target diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx index 951da30..a029b60 100644 --- a/build2/config/operation.cxx +++ b/build2/config/operation.cxx @@ -479,6 +479,7 @@ namespace build2 static void disfigure_search (const scope& root, + const scope&, const target_key&, const location&, action_targets& ts) diff --git a/build2/operation b/build2/operation index e6c34d6..39bb799 100644 --- a/build2/operation +++ b/build2/operation @@ -206,6 +206,7 @@ namespace build2 const location&); void (*search) (const scope& root, + const scope& base, const target_key&, const location&, action_targets&); @@ -230,17 +231,21 @@ namespace build2 // scope. // void - load (scope& root, - const path& buildfile, - const dir_path& out_base, - const dir_path& src_base, + load (scope&, + const path&, + const dir_path&, + const dir_path&, const location&); // Search and match the target. This is the default implementation // that does just that and adds a pointer to the target to the list. // void - search (const scope&, const target_key&, const location&, action_targets&); + search (const scope&, + const scope&, + const target_key&, + const location&, + action_targets&); void match (action, action_targets&); diff --git a/build2/operation.cxx b/build2/operation.cxx index ff2051d..b821b1d 100644 --- a/build2/operation.cxx +++ b/build2/operation.cxx @@ -60,25 +60,32 @@ namespace build2 auto i (scopes.rw (root).insert (out_base, false)); scope& base (setup_base (i, out_base, src_base)); - // Load the buildfile unless it has already been loaded. + // Load the buildfile unless it is implied. // - source_once (root, base, bf, root); + if (!bf.empty ()) + source_once (root, base, bf, root); } void search (const scope&, + const scope& bs, const target_key& tk, const location& l, action_targets& ts) { tracer trace ("search"); - phase_lock pl (run_phase::match); // Never switched. + phase_lock pl (run_phase::match); - if (const target* t = targets.find (tk, trace)) - ts.push_back (t); - else + const target* t (targets.find (tk, trace)); + + if (t == nullptr && tk.is_a ()) + t = dir::search_implied (bs, tk, trace); + + if (t == nullptr) fail (l) << "unknown target " << tk; + + ts.push_back (t); } void diff --git a/build2/spec b/build2/spec index 7a7e55e..92b56ec 100644 --- a/build2/spec +++ b/build2/spec @@ -28,7 +28,7 @@ namespace build2 // scope* root_scope = nullptr; dir_path out_base; - path buildfile; + path buildfile; // Empty if implied. }; struct opspec: vector diff --git a/build2/target b/build2/target index ef1e34d..c196060 100644 --- a/build2/target +++ b/build2/target @@ -307,7 +307,7 @@ namespace build2 prerequisites () const; // Swap-in a list of prerequisites. Return false if unsuccessful (i.e., - // some beat us to it). Note that it can be called on const target. + // someone beat us to it). Note that it can be called on const target. // bool prerequisites (prerequisites_type&&) const; @@ -1473,6 +1473,11 @@ namespace build2 public: static const target_type static_type; virtual const target_type& dynamic_type () const {return static_type;} + + public: + template + static const target* + search_implied (const scope&, const K&, tracer&); }; // While a filesystem directory is mtime-based, the semantics is diff --git a/build2/target.cxx b/build2/target.cxx index 676dc6b..668db92 100644 --- a/build2/target.cxx +++ b/build2/target.cxx @@ -14,6 +14,7 @@ #include using namespace std; +using namespace butl; namespace build2 { @@ -732,7 +733,7 @@ namespace build2 const target* t (search_existing_target (pk)); if (t == nullptr || t->implied) - fail << "no explicit target for prerequisite " << pk; + fail << "no explicit target for " << pk; return t; } @@ -762,7 +763,10 @@ namespace build2 return t; // If not found (or is implied), then try to load the corresponding - // buildfile which would normally define this target. + // buildfile (which would normally define this target). Failed that, see + // if we can assume an implied buildfile which would be equivalent to: + // + // ./: */ // const dir_path& d (*pk.tk.dir); @@ -809,18 +813,25 @@ namespace build2 scope& base (sp.first); scope& root (*sp.second); - path bf (base.src_path () / "buildfile"); + const dir_path& src_base (base.src_path ()); + + path bf (src_base / "buildfile"); if (exists (bf)) { l5 ([&]{trace << "loading buildfile " << bf << " for " << pk;}); retest = source_once (root, base, bf, root); } + else if (exists (src_base)) + { + t = dir::search_implied (base, pk, trace); + retest = (t != nullptr); + } } } assert (phase == run_phase::match); - // If we loaded the buildfile, examine the target again. + // If we loaded/implied the buildfile, examine the target again. // if (retest) { @@ -832,8 +843,7 @@ namespace build2 } } - fail << "no explicit target for prerequisite " << pk << - info << "did you forget to include the corresponding buildfile?" << endf; + fail << "no explicit target for " << pk << endf; } static bool diff --git a/build2/target.txx b/build2/target.txx index dd087c0..d854ed2 100644 --- a/build2/target.txx +++ b/build2/target.txx @@ -2,6 +2,8 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +#include // dir_iterator + #include #include #include @@ -135,4 +137,48 @@ namespace build2 return false; } + + // dir + // + template + const target* dir:: + search_implied (const scope& base, const K& k, tracer& trace) + { + using namespace butl; + + // See if we have any subdirectories. + // + prerequisites_type ps; + + for (const dir_entry& e: dir_iterator (base.src_path ())) + { + if (e.type () == entry_type::directory) + ps.push_back ( + prerequisite (nullopt, + dir::static_type, + dir_path (e.path ().representation ()), + dir_path (), // In the out tree. + string (), + nullopt, + base)); + } + + if (ps.empty ()) + return nullptr; + + l5 ([&]{trace << "implying buildfile for " << k;}); + + // We behave as if this target was explicitly mentioned in the (implied) + // buildfile. Thus not implied. + // + target& t (targets.insert (dir::static_type, + base.out_path (), + dir_path (), + string (), + nullopt, + false, + trace).first); + t.prerequisites (move (ps)); + return &t; + } } -- cgit v1.1