aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-06-29 10:39:26 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-06-29 10:39:26 +0200
commit50e1ffc8f9c48e3e81bd5fa38381193942182df3 (patch)
treebefd6198abbbfbee98c2d87f48bb06b667941415
parent67b17701d5b1753dc6aaf5a63a4dc2ef2642ba83 (diff)
Use depdb to track changes to cli compiler, options, etc
-rw-r--r--build2/algorithm.cxx29
-rw-r--r--build2/algorithm.hxx6
-rw-r--r--build2/cli/init.cxx33
-rw-r--r--build2/cli/rule.cxx77
4 files changed, 123 insertions, 22 deletions
diff --git a/build2/algorithm.cxx b/build2/algorithm.cxx
index 5dc3637..c46e878 100644
--- a/build2/algorithm.cxx
+++ b/build2/algorithm.cxx
@@ -2056,4 +2056,33 @@ namespace build2
r |= reverse_execute_prerequisites (a, g);
return r;
}
+
+ target_state
+ perform_clean_group_depdb (action a, const target& g)
+ {
+ // The same twisted target state merging logic as in clean_extra().
+ //
+ target_state er (target_state::unchanged);
+ path ep;
+
+ group_view gv (g.group_members (a));
+ if (gv.count != 0)
+ {
+ ep = gv.members[0]->as<file> ().path () + ".d";
+
+ if (rmfile (ep, 3))
+ er = target_state::changed;
+ }
+
+ target_state tr (perform_clean_group (a, g));
+
+ if (tr != target_state::changed && er == target_state::changed)
+ {
+ if (verb > (current_diag_noise ? 0 : 1) && verb < 3)
+ text << "rm " << ep;
+ }
+
+ tr |= er;
+ return tr;
+ }
}
diff --git a/build2/algorithm.hxx b/build2/algorithm.hxx
index 48f48c8..93fe131 100644
--- a/build2/algorithm.hxx
+++ b/build2/algorithm.hxx
@@ -616,6 +616,12 @@ namespace build2
target_state
perform_clean_group (action, const target&);
+ // As above but clean both the target group and depdb. The depdb file path
+ // is derived from the first member file path.
+ //
+ target_state
+ perform_clean_group_depdb (action, const target&);
+
// Helper for custom perform(clean) implementations that cleans extra files
// and directories (recursively) specified as a list of either absolute
// paths or "path derivation directives". The directive string can be NULL,
diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx
index 88a8d69..8e0abdb 100644
--- a/build2/cli/init.cxx
+++ b/build2/cli/init.cxx
@@ -53,7 +53,11 @@ namespace build2
v.insert<path> ("config.cli", true);
v.insert<strings> ("config.cli.options", true);
+ //@@ TODO: split version into componets (it is stdver).
+ //
v.insert<process_path> ("cli.path");
+ v.insert<string> ("cli.version");
+ v.insert<string> ("cli.checksum");
v.insert<strings> ("cli.options");
}
@@ -214,11 +218,23 @@ namespace build2
nv = p.second;
}
- // Note that we are unconfigured so that we don't keep re-testing this
- // on each run.
- //
- if (!conf)
+ string checksum;
+ if (conf)
+ {
+ // Hash the compiler path and version.
+ //
+ sha256 cs;
+ cs.append (pp.effect_string ());
+ cs.append (ver);
+ checksum = cs.string ();
+ }
+ else
+ {
+ // Note that we are unconfigured so that we don't keep re-testing
+ // this on each run.
+ //
nv = config::unconfigured (rs, "cli", true) || nv;
+ }
// If this is a new value (e.g., we are configuring), then print the
// report at verbosity level 2 and up (-v).
@@ -230,13 +246,18 @@ namespace build2
if (conf)
dr << " cli " << pp << '\n'
- << " version " << ver;
+ << " version " << ver << '\n'
+ << " checksum " << checksum;
else
dr << " cli " << "not found, leaving unconfigured";
}
if (conf)
- rs.assign<process_path> ("cli.path") = move (pp);
+ {
+ rs.assign ("cli.path") = move (pp);
+ rs.assign ("cli.version") = move (ver);
+ rs.assign ("cli.checksum") = move (checksum);
+ }
}
if (conf)
diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx
index 45b8185..27668c1 100644
--- a/build2/cli/rule.cxx
+++ b/build2/cli/rule.cxx
@@ -4,6 +4,7 @@
#include <build2/cli/rule.hxx>
+#include <build2/depdb.hxx>
#include <build2/scope.hxx>
#include <build2/target.hxx>
#include <build2/context.hxx>
@@ -166,10 +167,12 @@ namespace build2
//
match_prerequisite_members (a, t);
+ //@@ TODO: inject dependency on exe{cli}.
+
switch (a)
{
case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean_group; // Standard impl.
+ case perform_clean_id: return &perform_clean_group_depdb;
default: return noop_recipe; // Configure/dist update.
}
}
@@ -206,34 +209,76 @@ namespace build2
target_state compile_rule::
perform_update (action a, const target& xt)
{
+ tracer trace ("cli::compile_rule::perform_update");
+
+ // The rule has been matched which means the members should be resolved
+ // and paths assigned. We use the header file as our "target path" for
+ // timestamp, depdb, etc.
+ //
const cli_cxx& t (xt.as<cli_cxx> ());
+ const scope& rs (t.root_scope ());
+ const path& tp (t.h->path ());
// Update prerequisites and determine if any relevant ones render us
- // out-of-date. Note that currently we treat all the prerequisites
- // as potentially affecting the result (think prologues/epilogues,
- // etc).
+ // out-of-date. Note that currently we treat all the prerequisites as
+ // potentially affecting the result (think prologues/epilogues, etc).
//
+ timestamp mt (t.load_mtime (tp));
+ auto pr (execute_prerequisites<cli> (a, t, mt));
- // The rule has been matched which means the members should be resolved
- // and paths assigned.
+ bool update (!pr.first);
+ target_state ts (update ? target_state::changed : *pr.first);
+
+ const cli& s (pr.second);
+
+ // We use depdb to track changes to the .cli file name, options,
+ // compiler, etc.
//
- auto pr (
- execute_prerequisites<cli> (
- a, t, t.load_mtime (t.h->path ())));
+ {
+ depdb dd (tp + ".d");
- if (pr.first)
- return *pr.first;
+ // First should come the rule name/version.
+ //
+ if (dd.expect ("cli.compile 1") != nullptr)
+ l4 ([&]{trace << "rule mismatch forcing update of " << t;});
- const cli& s (pr.second);
+ // Then the compiler checksum.
+ //
+ if (dd.expect (cast<string> (rs["cli.checksum"])) != nullptr)
+ l4 ([&]{trace << "compiler mismatch forcing update of " << t;});
- // Translate paths to relative (to working directory). This
- // results in easier to read diagnostics.
+ // Then the options checksum.
+ //
+ sha256 cs;
+ hash_options (cs, t, "cli.options");
+
+ if (dd.expect (cs.string ()) != nullptr)
+ l4 ([&]{trace << "options mismatch forcing update of " << t;});
+
+ // Finally the .cli input file.
+ //
+ if (dd.expect (s.path ()) != nullptr)
+ l4 ([&]{trace << "input file mismatch forcing update of " << t;});
+
+ // Update if depdb mismatch.
+ //
+ if (dd.writing () || dd.mtime () > mt)
+ update = true;
+
+ dd.close ();
+ }
+
+ // If nothing changed, then we are done.
+ //
+ if (!update)
+ return ts;
+
+ // Translate paths to relative (to working directory). This results in
+ // easier to read diagnostics.
//
path relo (relative (t.dir));
path rels (relative (s.path ()));
- const scope& rs (t.root_scope ());
-
const process_path& cli (cast<process_path> (rs["cli.path"]));
cstrings args {cli.recall_string ()};