aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-03-10 15:42:04 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-03-10 15:42:04 +0200
commit5925c11a1fe8b2e02b790dd40b031ae005d5b68f (patch)
tree14455da2f4b58d49542023ef0b415414b926d56f /build
parent5807ff000225acf47064eb7f0be965bf1598faaa (diff)
Further operation implementation
Diffstat (limited to 'build')
-rw-r--r--build/algorithm34
-rw-r--r--build/algorithm.cxx81
-rw-r--r--build/algorithm.ixx8
-rw-r--r--build/algorithm.txx24
-rw-r--r--build/b.cxx66
-rw-r--r--build/buildfile6
-rw-r--r--build/context4
-rw-r--r--build/context.cxx3
-rw-r--r--build/cxx/rule14
-rw-r--r--build/cxx/rule.cxx38
-rw-r--r--build/operation70
-rw-r--r--build/operation.cxx24
-rw-r--r--build/rule32
-rw-r--r--build/rule.cxx43
-rw-r--r--build/target20
-rw-r--r--build/target.cxx2
-rw-r--r--build/utility53
17 files changed, 358 insertions, 164 deletions
diff --git a/build/algorithm b/build/algorithm
index 82913ad..8ce35f8 100644
--- a/build/algorithm
+++ b/build/algorithm
@@ -6,6 +6,7 @@
#define BUILD_ALGORITHM
#include <build/target>
+#include <build/operation>
#include <build/timestamp>
namespace build
@@ -19,41 +20,44 @@ namespace build
target&
search (prerequisite&);
- // Match a rule to the target with ambiguity detection.
+ // Match a rule to the action/target with ambiguity detection.
//
void
- match (target&);
+ match (action, target&);
// The default prerequisite search and match implementation. It calls
// search() and then match() for each prerequisite in a loop.
//
void
- search_and_match (target&);
+ search_and_match (action, target&);
+ // Execute the action on target, assuming a rule has been matched
+ // and the recipe for this action has been set.
+ //
target_state
- update (target&);
+ execute (action, target&);
- // The default prerequisite update implementation. It calls update()
- // for each prerequisite in a loop. Returns target_state::updated
- // if any of them were updated and target_state::uptodate otherwise.
+ // The default prerequisite execute implementation. It calls execute()
+ // for each prerequisite in a loop. Returns target_state::changed
+ // if any of them were changed and target_state::unchanged otherwise.
//
target_state
- update_prerequisites (target&);
+ execute_prerequisites (action, target&);
- // A version of the above that also determines whether the target
- // needs updating based on the passed timestamp.
+ // A version of the above that also determines whether the action
+ // needs to be executed on the target based on the passed timestamp.
//
bool
- update_prerequisites (target&, const timestamp&);
+ execute_prerequisites (action, target&, const timestamp&);
// Another version of the above that does two extra things for the
- // caller: it determines whether the target needs updating based
- // on the passed timestamp and, if so, finds a prerequisite of the
- // specified type.
+ // caller: it determines whether the action needs to be executed on
+ // the target based on the passed timestamp and, if so, finds a
+ // prerequisite of the specified type (e.g., source file).
//
template <typename T>
T*
- update_prerequisites (target&, const timestamp&);
+ execute_prerequisites (action, target&, const timestamp&);
}
#include <build/algorithm.ixx>
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index 5be5cad..46387b3 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -33,21 +33,36 @@ namespace build
}
void
- match_impl (target& t)
+ match_impl (action a, target& t)
{
for (auto tt (&t.type ());
- tt != nullptr && !t.recipe ();
+ tt != nullptr && !t.recipe (a);
tt = tt->base)
{
- auto i (rules.find (tt->id));
+ auto i (current_rules->find (tt->id));
- if (i == rules.end ()) // No rules registered for this target type.
- continue;
+ if (i == current_rules->end () || i->second.empty ())
+ continue; // No rules registered for this target type, try base.
const auto& rules (i->second); // Hint map.
- string hint; // @@ TODO
- auto rs (rules.find_prefix (hint));
+ // @@ TODO
+ //
+ // Different rules can be used for different operations (update
+ // vs test is a good example). So, at some point, we will probably
+ // have to support a list of hints or even an operation-hint map
+ // (e.g., 'hint=cxx test=foo' if cxx supports the test operation
+ // but we want the foo rule instead). This is also the place where
+ // the '{build clean}=cxx' construct (which we currently do not
+ // support) can come handy.
+ //
+ // Also, ignore the hint (that is most likely ment for a different
+ // operation) if this is a unique match.
+ //
+ string hint;
+ auto rs (rules.size () == 1
+ ? make_pair (rules.begin (), rules.end ())
+ : rules.find_prefix (hint));
for (auto i (rs.first); i != rs.second; ++i)
{
@@ -64,7 +79,7 @@ namespace build
},
t, n));
- m = ru.match (t, hint);
+ m = ru.match (a, t, hint);
}
if (m != nullptr)
@@ -91,7 +106,7 @@ namespace build
},
t, n1));
- m1 = ru1.match (t, hint);
+ m1 = ru1.match (a, t, hint);
}
if (m1 != nullptr)
@@ -117,7 +132,7 @@ namespace build
},
t, n));
- t.recipe (ru.apply (t, m));
+ t.recipe (a, ru.apply (a, t, m));
break;
}
else
@@ -126,24 +141,24 @@ namespace build
}
}
- if (!t.recipe ())
+ if (!t.recipe (a))
fail << "no rule to update target " << t;
}
void
- search_and_match (target& t)
+ search_and_match (action a, target& t)
{
for (prerequisite& p: t.prerequisites)
{
if (p.target == nullptr)
search (p);
- match (*p.target);
+ match (a, *p.target);
}
}
target_state
- update (target& t)
+ execute (action a, target& t)
{
// Implementation with some multi-threading ideas in mind.
//
@@ -158,13 +173,13 @@ namespace build
[](target& t){info << "while updating target " << t;},
t));
- ts = t.recipe () (t);
+ ts = t.recipe (a) (a, t);
assert (ts != target_state::unknown && ts != target_state::failed);
t.state (ts);
return ts;
}
- case target_state::uptodate:
- case target_state::updated:
+ case target_state::unchanged:
+ case target_state::changed:
return ts;
case target_state::failed:
throw failed ();
@@ -172,34 +187,34 @@ namespace build
}
target_state
- update_prerequisites (target& t)
+ execute_prerequisites (action a, target& t)
{
- target_state ts (target_state::uptodate);
+ target_state ts (target_state::unchanged);
for (const prerequisite& p: t.prerequisites)
{
assert (p.target != nullptr);
- if (update (*p.target) != target_state::uptodate)
- ts = target_state::updated;
+ if (execute (a, *p.target) != target_state::unchanged)
+ ts = target_state::changed;
}
return ts;
}
bool
- update_prerequisites (target& t, const timestamp& mt)
+ execute_prerequisites (action a, target& t, const timestamp& mt)
{
- bool u (mt == timestamp_nonexistent);
+ bool e (mt == timestamp_nonexistent);
for (const prerequisite& p: t.prerequisites)
{
assert (p.target != nullptr);
target& pt (*p.target);
- target_state ts (update (pt));
+ target_state ts (execute (a, pt));
- if (!u)
+ if (!e)
{
// If this is an mtime-based target, then compare timestamps.
//
@@ -210,22 +225,22 @@ namespace build
// What do we do if timestamps are equal? This can happen, for
// example, on filesystems that don't have subsecond resolution.
// There is not much we can do here except detect the case where
- // the prerequisite was updated in this run which means the
- // target must be out of date.
+ // the prerequisite was changed in this run which means the
+ // action must be executed on the target as well.
//
- if (mt < mp || (mt == mp && ts == target_state::updated))
- u = true;
+ if (mt < mp || (mt == mp && ts == target_state::changed))
+ e = true;
}
else
{
- // Otherwise we assume the prerequisite is newer if it was updated.
+ // Otherwise we assume the prerequisite is newer if it was changed.
//
- if (ts == target_state::updated)
- u = true;
+ if (ts == target_state::changed)
+ e = true;
}
}
}
- return u;
+ return e;
}
}
diff --git a/build/algorithm.ixx b/build/algorithm.ixx
index 02c223c..e33566b 100644
--- a/build/algorithm.ixx
+++ b/build/algorithm.ixx
@@ -5,12 +5,12 @@
namespace build
{
void
- match_impl (target&);
+ match_impl (action, target&);
inline void
- match (target& t)
+ match (action a, target& t)
{
- if (!t.recipe ())
- match_impl (t);
+ if (!t.recipe (a))
+ match_impl (a, t);
}
}
diff --git a/build/algorithm.txx b/build/algorithm.txx
index 20817d2..4c9a673 100644
--- a/build/algorithm.txx
+++ b/build/algorithm.txx
@@ -6,7 +6,7 @@ namespace build
{
template <typename T>
T*
- update_prerequisites (target& t, const timestamp& mt)
+ execute_prerequisites (action a, target& t, const timestamp& mt)
{
//@@ Can factor the bulk of it into a non-template code. Can
// either do a function template that will do dynamic_cast check
@@ -14,16 +14,16 @@ namespace build
//
T* r (nullptr);
- bool u (mt == timestamp_nonexistent);
+ bool e (mt == timestamp_nonexistent);
for (const prerequisite& p: t.prerequisites)
{
assert (p.target != nullptr);
target& pt (*p.target);
- target_state ts (update (pt));
+ target_state ts (execute (a, pt));
- if (!u)
+ if (!e)
{
// If this is an mtime-based target, then compare timestamps.
//
@@ -34,18 +34,18 @@ namespace build
// What do we do if timestamps are equal? This can happen, for
// example, on filesystems that don't have subsecond resolution.
// There is not much we can do here except detect the case where
- // the prerequisite was updated in this run which means the
- // target must be out of date.
+ // the prerequisite was changed in this run which means the
+ // action must be executed on the target as well.
//
- if (mt < mp || (mt == mp && ts == target_state::updated))
- u = true;
+ if (mt < mp || (mt == mp && ts == target_state::changed))
+ e = true;
}
else
{
- // Otherwise we assume the prerequisite is newer if it was updated.
+ // Otherwise we assume the prerequisite is newer if it was changed.
//
- if (ts == target_state::updated)
- u = true;
+ if (ts == target_state::changed)
+ e = true;
}
}
@@ -54,6 +54,6 @@ namespace build
}
assert (r != nullptr);
- return u ? r : nullptr;
+ return e ? r : nullptr;
}
}
diff --git a/build/b.cxx b/build/b.cxx
index 4779a57..499ac10 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -19,8 +19,10 @@
#include <typeinfo>
#include <system_error>
-#include <build/spec>
+#include <build/path>
#include <build/name>
+#include <build/spec>
+#include <build/operation>
#include <build/scope>
#include <build/target>
#include <build/prerequisite>
@@ -102,16 +104,17 @@ main (int argc, char* argv[])
target_types.insert (cxx::ixx::static_type);
target_types.insert (cxx::txx::static_type);
- // Enter built-in meta-operation and operation names. Loading of
- // the buildfiles can result in additional names being added (via
- // module loading).
+ // Enter built-in meta-operation and operation names into tables.
+ // Note that the order of registration should match the id constants;
+ // see <operation> for details. Loading of the buildfiles can result
+ // in additional names being added (via module loading).
//
- meta_operations.emplace ("perform"); // Default.
- meta_operations.emplace ("configure");
- meta_operations.emplace ("disfigure");
+ meta_operations.insert ("perform"); // Default.
+ meta_operations.insert ("configure");
+ meta_operations.insert ("disfigure");
- operations.emplace ("update"); // Default.
- operations.emplace ("clean");
+ operations.insert ("update"); // Default.
+ operations.insert ("clean");
// Figure out work and home directories.
//
@@ -334,6 +337,12 @@ main (int argc, char* argv[])
}
}
+ // We store the combined action id in uint8_t; see <operation> for
+ // details.
+ //
+ assert (operations.size () <= 128);
+ assert (meta_operations.size () <= 128);
+
dump_scopes ();
dump ();
@@ -360,7 +369,7 @@ main (int argc, char* argv[])
continue;
}
- if (meta_operations.count (os.name))
+ if (meta_operations.find (os.name) != 0)
{
if (!ms.name.empty ())
fail (l) << "nested meta-operation " << os.name;
@@ -419,7 +428,7 @@ main (int argc, char* argv[])
}
}
- if (!operations.count (os.name))
+ if (operations.find (os.name) == 0)
fail (l) << "unknown operation " << os.name;
}
@@ -428,7 +437,7 @@ main (int argc, char* argv[])
//
if (mi->name.empty ())
mi->name = "perform";
- else if (!meta_operations.count (mi->name))
+ else if (meta_operations.find (mi->name) == 0)
fail (l) << "unknown meta-operation " << mi->name;
}
@@ -437,19 +446,24 @@ main (int argc, char* argv[])
// Register rules.
//
cxx::link cxx_link;
- rules[typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
+ rules["update"][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
+ rules["clean"][typeid (exe)].emplace ("cxx.gnu.link", cxx_link);
cxx::compile cxx_compile;
- rules[typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules["update"][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
+ rules["clean"][typeid (obj)].emplace ("cxx.gnu.compile", cxx_compile);
dir_rule dir_r;
- rules[typeid (dir)].emplace ("dir", dir_r);
+ rules["update"][typeid (dir)].emplace ("dir", dir_r);
+ rules["clean"][typeid (dir)].emplace ("dir", dir_r);
fsdir_rule fsdir_r;
- rules[typeid (fsdir)].emplace ("fsdir", fsdir_r);
+ rules["update"][typeid (fsdir)].emplace ("fsdir", fsdir_r);
+ rules["clean"][typeid (fsdir)].emplace ("fsdir", fsdir_r);
path_rule path_r;
- rules[typeid (path_target)].emplace ("path", path_r);
+ rules["update"][typeid (path_target)].emplace ("path", path_r);
+ rules["clean"][typeid (path_target)].emplace ("path", path_r);
// Do the operations. We do meta-operations and operations sequentially
@@ -459,8 +473,12 @@ main (int argc, char* argv[])
{
for (opspec& os: ms)
{
- // But multiple targets in the same operation can be done in
- // parallel.
+ current_rules = &rules[os.name];
+ action act (meta_operations.find (ms.name), operations.find (os.name));
+
+ level4 ([&]{trace << ms.name << " " << os.name << " " << act;});
+
+ // Multiple targets in the same operation can be done in parallel.
//
vector<reference_wrapper<target>> tgs;
tgs.reserve (os.size ());
@@ -497,10 +515,10 @@ main (int argc, char* argv[])
target& t (**i);
- if (!t.recipe ())
+ if (!t.recipe (act))
{
level4 ([&]{trace << "matching target " << t;});
- match (t);
+ match (act, t);
}
tgs.push_back (t);
@@ -520,17 +538,17 @@ main (int argc, char* argv[])
if (s == target_state::unknown)
{
level4 ([&]{trace << "updating target " << t;});
- s = update (t);
+ s = execute (act, t);
}
switch (s)
{
- case target_state::uptodate:
+ case target_state::unchanged:
{
info << "target " << t << " is up to date";
break;
}
- case target_state::updated:
+ case target_state::changed:
break;
case target_state::failed:
//@@ This could probably happen in a parallel build.
diff --git a/build/buildfile b/build/buildfile
index 5c1225d..8f11823 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -1,3 +1,3 @@
-exe{b1}: cxx{b algorithm parser lexer name spec scope variable target \
- prerequisite rule native context search diagnostics cxx/target cxx/rule \
- process timestamp path utility mkdir dump}
+exe{b1}: cxx{b algorithm parser lexer name operation spec scope variable \
+ target prerequisite rule native context search diagnostics cxx/target \
+ cxx/rule process timestamp path utility mkdir dump}
diff --git a/build/context b/build/context
index 1967a83..7c7421d 100644
--- a/build/context
+++ b/build/context
@@ -7,7 +7,6 @@
#include <string>
#include <ostream>
-#include <unordered_set>
#include <build/path>
@@ -18,9 +17,6 @@ namespace build
extern path work;
extern path home;
- extern std::unordered_set<std::string> meta_operations;
- extern std::unordered_set<std::string> operations;
-
// Return the src/out directory corresponding to the given out/src. The
// passed directory should be a sub-directory of out/src_root.
//
diff --git a/build/context.cxx b/build/context.cxx
index d58f9c7..6c37e38 100644
--- a/build/context.cxx
+++ b/build/context.cxx
@@ -16,9 +16,6 @@ namespace build
path work;
path home;
- unordered_set<string> meta_operations;
- unordered_set<string> operations;
-
path
src_out (const path& out, scope& s)
{
diff --git a/build/cxx/rule b/build/cxx/rule
index 80a917e..1dfb8b5 100644
--- a/build/cxx/rule
+++ b/build/cxx/rule
@@ -23,30 +23,30 @@ namespace build
{
public:
virtual void*
- match (target&, const std::string& hint) const;
+ match (action a, target&, const std::string& hint) const;
virtual recipe
- apply (target&, void*) const;
+ apply (action a, target&, void*) const;
static target_state
- update (target&);
+ update (action a, target&);
private:
void
- inject_prerequisites (obj&, const cxx&, scope&) const;
+ inject_prerequisites (action a, obj&, const cxx&, scope&) const;
};
class link: public rule
{
public:
virtual void*
- match (target&, const std::string& hint) const;
+ match (action a, target&, const std::string& hint) const;
virtual recipe
- apply (target&, void*) const;
+ apply (action a, target&, void*) const;
static target_state
- update (target&);
+ update (action a, target&);
};
}
}
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index e495c27..4c93a1a 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -28,7 +28,7 @@ namespace build
// compile
//
void* compile::
- match (target& t, const string&) const
+ match (action a, target& t, const string&) const
{
tracer trace ("cxx::compile::match");
@@ -57,7 +57,7 @@ namespace build
}
recipe compile::
- apply (target& t, void* v) const
+ apply (action a, target& t, void* v) const
{
// Derive object file name from target name.
//
@@ -69,7 +69,7 @@ namespace build
// Search and match all the existing prerequisites. The injection
// code (below) takes care of the ones it is adding.
//
- search_and_match (t);
+ search_and_match (a, t);
// Inject additional prerequisites.
//
@@ -77,7 +77,7 @@ namespace build
auto& st (dynamic_cast<cxx&> (*sp.target));
if (st.mtime () != timestamp_nonexistent)
- inject_prerequisites (o, st, sp.scope);
+ inject_prerequisites (a, o, st, sp.scope);
return &update;
}
@@ -125,7 +125,7 @@ namespace build
}
void compile::
- inject_prerequisites (obj& o, const cxx& s, scope& ds) const
+ inject_prerequisites (action a, obj& o, const cxx& s, scope& ds) const
{
tracer trace ("cxx::compile::inject_prerequisites");
@@ -226,7 +226,7 @@ namespace build
// Match to a rule.
//
- build::match (t);
+ build::match (a, t);
}
}
@@ -251,13 +251,13 @@ namespace build
}
target_state compile::
- update (target& t)
+ update (action a, target& t)
{
obj& o (dynamic_cast<obj&> (t));
- cxx* s (update_prerequisites<cxx> (o, o.mtime ()));
+ cxx* s (execute_prerequisites<cxx> (a, o, o.mtime ()));
if (s == nullptr)
- return target_state::uptodate;
+ return target_state::unchanged;
// Translate paths to relative (to working directory) ones. This
// results in easier to read diagnostics.
@@ -293,7 +293,7 @@ namespace build
// subseconds precision.
//
o.mtime (system_clock::now ());
- return target_state::updated;
+ return target_state::changed;
}
catch (const process_error& e)
{
@@ -313,7 +313,7 @@ namespace build
// link
//
void* link::
- match (target& t, const string& hint) const
+ match (action a, target& t, const string& hint) const
{
tracer trace ("cxx::link::match");
@@ -371,7 +371,7 @@ namespace build
}
recipe link::
- apply (target& t, void*) const
+ apply (action a, target& t, void*) const
{
tracer trace ("cxx::link::apply");
@@ -400,7 +400,7 @@ namespace build
if (p.target == nullptr)
search (p);
- build::match (*p.target);
+ build::match (a, *p.target);
continue;
}
@@ -487,7 +487,7 @@ namespace build
if (cp1 != nullptr)
{
- build::match (ot); // Now cp1 should be resolved.
+ build::match (a, ot); // Now cp1 should be resolved.
if (cp.target == nullptr)
search (cp); // Our own prerequisite, so this is ok.
@@ -502,7 +502,7 @@ namespace build
else
{
ot.prerequisites.push_back (cp);
- build::match (ot);
+ build::match (a, ot);
}
// Change the exe{} target's prerequsite from cxx{} to obj{}.
@@ -514,7 +514,7 @@ namespace build
}
target_state link::
- update (target& t)
+ update (action a, target& t)
{
// @@ Q:
//
@@ -523,8 +523,8 @@ namespace build
exe& e (dynamic_cast<exe&> (t));
- if (!update_prerequisites (e, e.mtime ()))
- return target_state::uptodate;
+ if (!execute_prerequisites (a, e, e.mtime ()))
+ return target_state::unchanged;
// Translate paths to relative (to working directory) ones. This
// results in easier to read diagnostics.
@@ -563,7 +563,7 @@ namespace build
// subseconds precision.
//
e.mtime (system_clock::now ());
- return target_state::updated;
+ return target_state::changed;
}
catch (const process_error& e)
{
diff --git a/build/operation b/build/operation
new file mode 100644
index 0000000..3398f32
--- /dev/null
+++ b/build/operation
@@ -0,0 +1,70 @@
+// file : build/operation -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_OPERATION
+#define BUILD_OPERATION
+
+#include <string>
+#include <cstdint>
+#include <iosfwd>
+
+#include <build/utility> // string_table
+
+namespace build
+{
+ // While we are using uint8_t for the meta/operation ids, we assume
+ // that each is limited to 4 bits (max 128 entries) so that we can
+ // store the combined action id in uint8_t as well. This makes our
+ // life easier when it comes to defining switch labels for action
+ // ids (no need to mess with endian-ness).
+ //
+ // Note that 0 is not a valid meta/operation/action id.
+ //
+ using meta_operation_id = std::uint8_t;
+ using operation_id = std::uint8_t;
+ using action_id = std::uint8_t;
+
+ struct action
+ {
+ action (meta_operation_id m, operation_id o): id ((m << 4) | o) {}
+
+ meta_operation_id
+ meta_operation () const {return id >> 4;}
+
+ operation_id
+ operation () const {return id & 0xF;}
+
+ // Implicit conversion operator to action_id for the switch()
+ // statement, etc.
+ //
+ operator action_id () const {return id;}
+
+ action_id id;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, action);
+
+ // Id constants for build-in operations.
+ //
+ const meta_operation_id perform_id = 1;
+ const meta_operation_id configure_id = 2;
+ const meta_operation_id disfigure_id = 3;
+
+ const operation_id update_id = 1;
+ const operation_id clean_id = 2;
+
+ const action_id perform_update_id = (perform_id << 4) | update_id;
+ const action_id perform_clean_id = (perform_id << 4) | clean_id;
+
+ // Meta/operation id tables.
+ //
+ using meta_operation_table = string_table<meta_operation_id>;
+ using operation_table = string_table<operation_id>;
+
+ extern meta_operation_table meta_operations;
+ extern operation_table operations;
+}
+
+#endif // BUILD_OPERATION
diff --git a/build/operation.cxx b/build/operation.cxx
new file mode 100644
index 0000000..0d90c95
--- /dev/null
+++ b/build/operation.cxx
@@ -0,0 +1,24 @@
+// file : build/operation.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/operation>
+
+#include <ostream>
+
+using namespace std;
+
+namespace build
+{
+ ostream&
+ operator<< (ostream& os, action a)
+ {
+ return os << '('
+ << static_cast<uint16_t> (a.meta_operation ()) << ','
+ << static_cast<uint16_t> (a.operation ())
+ << ')';
+ }
+
+ meta_operation_table meta_operations;
+ operation_table operations;
+}
diff --git a/build/rule b/build/rule
index 4336c8f..2e11191 100644
--- a/build/rule
+++ b/build/rule
@@ -11,6 +11,7 @@
#include <unordered_map>
#include <build/target>
+#include <build/operation>
#include <build/prefix-map>
namespace build
@@ -19,17 +20,20 @@ namespace build
{
public:
virtual void*
- match (target&, const std::string& hint) const = 0;
+ match (action, target&, const std::string& hint) const = 0;
virtual recipe
- apply (target&, void*) const = 0;
+ apply (action, target&, void*) const = 0;
};
- typedef std::unordered_map<
+ using target_rule_map = std::unordered_map<
std::type_index,
- prefix_multimap<std::string, std::reference_wrapper<rule>, '.'>> rule_map;
+ prefix_multimap<std::string, std::reference_wrapper<rule>, '.'>>;
- extern rule_map rules;
+ using operation_rule_map = std::unordered_map<std::string, target_rule_map>;
+
+ extern operation_rule_map rules;
+ extern const target_rule_map* current_rules; // Rules for current operation.
// Fallback rule that check that the path exists.
//
@@ -37,39 +41,39 @@ namespace build
{
public:
virtual void*
- match (target&, const std::string& hint) const;
+ match (action, target&, const std::string& hint) const;
virtual recipe
- apply (target&, void*) const;
+ apply (action, target&, void*) const;
static target_state
- update (target&);
+ update (action, target&);
};
class dir_rule: public rule
{
public:
virtual void*
- match (target&, const std::string& hint) const;
+ match (action, target&, const std::string& hint) const;
virtual recipe
- apply (target&, void*) const;
+ apply (action, target&, void*) const;
static target_state
- update (target&);
+ update (action, target&);
};
class fsdir_rule: public rule
{
public:
virtual void*
- match (target&, const std::string& hint) const;
+ match (action, target&, const std::string& hint) const;
virtual recipe
- apply (target&, void*) const;
+ apply (action, target&, void*) const;
static target_state
- update (target&);
+ update (action, target&);
};
}
diff --git a/build/rule.cxx b/build/rule.cxx
index 41296f0..f193760 100644
--- a/build/rule.cxx
+++ b/build/rule.cxx
@@ -16,12 +16,13 @@ using namespace std;
namespace build
{
- rule_map rules;
+ operation_rule_map rules;
+ const target_rule_map* current_rules;
// path_rule
//
void* path_rule::
- match (target& t, const string&) const
+ match (action a, target& t, const string&) const
{
// @@ TODO:
//
@@ -55,17 +56,17 @@ namespace build
}
recipe path_rule::
- apply (target& t, void*) const
+ apply (action a, target& t, void*) const
{
// Search and match all the prerequisites.
//
- search_and_match (t);
+ search_and_match (a, t);
return &update;
}
target_state path_rule::
- update (target& t)
+ update (action a, target& t)
{
// Make sure the target is not older than any of its prerequisites.
//
@@ -74,7 +75,7 @@ namespace build
for (const prerequisite& p: t.prerequisites)
{
target& pt (*p.target);
- target_state ts (update (pt));
+ target_state ts (update (a, pt));
// If this is an mtime-based target, then compare timestamps.
//
@@ -89,52 +90,52 @@ namespace build
}
else
{
- // Otherwise we assume the prerequisite is newer if it was updated.
+ // Otherwise we assume the prerequisite is newer if it was changed.
//
- if (ts == target_state::updated)
+ if (ts == target_state::changed)
fail << "no recipe to update target " << t <<
info << "prerequisite " << pt << " is ahead of " << t
<< " because it was updated";
}
}
- return target_state::uptodate;
+ return target_state::unchanged;
}
// dir_rule
//
void* dir_rule::
- match (target& t, const string&) const
+ match (action a, target& t, const string&) const
{
return &t;
}
recipe dir_rule::
- apply (target& t, void*) const
+ apply (action a, target& t, void*) const
{
- search_and_match (t);
+ search_and_match (a, t);
return &update;
}
target_state dir_rule::
- update (target& t)
+ update (action a, target& t)
{
- // Return updated if any of our prerequsites were updated and
- // uptodate otherwise.
+ // Return changed if any of our prerequsites were updated and
+ // unchanged otherwise.
//
- return update_prerequisites (t);
+ return execute_prerequisites (a, t);
}
// fsdir_rule
//
void* fsdir_rule::
- match (target& t, const string&) const
+ match (action a, target& t, const string&) const
{
return &t;
}
recipe fsdir_rule::
- apply (target& t, void*) const
+ apply (action a, target& t, void*) const
{
// Let's not allow any prerequisites for this target since it
// doesn't make much sense. The sole purpose of this target type
@@ -147,7 +148,7 @@ namespace build
}
target_state fsdir_rule::
- update (target& t)
+ update (action a, target& t)
{
path d (t.dir / path (t.name));
@@ -160,7 +161,7 @@ namespace build
}
if (path_mtime (d) != timestamp_nonexistent)
- return target_state::uptodate;
+ return target_state::unchanged;
if (verb >= 1)
text << "mkdir " << d.string ();
@@ -177,6 +178,6 @@ namespace build
<< e.what ();
}
- return target_state::updated;
+ return target_state::changed;
}
}
diff --git a/build/target b/build/target
index 9c4640e..a49c57f 100644
--- a/build/target
+++ b/build/target
@@ -19,6 +19,7 @@
#include <build/key-set>
#include <build/timestamp>
#include <build/name>
+#include <build/operation>
#include <build/prerequisite>
#include <build/utility> // compare_*, extension_pool
@@ -26,11 +27,11 @@ namespace build
{
class target;
- enum class target_state {unknown, uptodate, updated, failed};
+ enum class target_state {unknown, unchanged, changed, failed};
// Note: should throw rather than returning target_state::failed.
//
- typedef std::function<target_state (target&)> recipe;
+ typedef std::function<target_state (action, target&)> recipe;
struct target_type
{
@@ -71,10 +72,19 @@ namespace build
typedef build::recipe recipe_type;
const recipe_type&
- recipe () const {return recipe_;}
+ recipe (action_id a) const {return action_ == a ? recipe_ : empty_recipe_;}
void
- recipe (recipe_type r) {assert (!recipe_); recipe_ = r;}
+ recipe (action_id a, recipe_type r)
+ {
+ assert (action_ != a || !recipe_);
+ action_ = a;
+ recipe_ = r;
+
+ // Also reset the target state.
+ //
+ state_ = target_state::unknown;
+ }
public:
target_state
@@ -92,7 +102,9 @@ namespace build
static const target_type static_type;
private:
+ action_id action_ {0}; // Action id of this target recipe.
recipe_type recipe_;
+ static const recipe_type empty_recipe_;
target_state state_ {target_state::unknown};
};
diff --git a/build/target.cxx b/build/target.cxx
index f09a858..5cf131f 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -15,6 +15,8 @@ namespace build
{
// target
//
+ const recipe target::empty_recipe_;
+
ostream&
operator<< (ostream& os, const target& t)
{
diff --git a/build/utility b/build/utility
index fd7888c..93fb441 100644
--- a/build/utility
+++ b/build/utility
@@ -6,11 +6,15 @@
#define BUILD_UTILITY
#include <tuple>
+#include <limits> // numeric_limits
#include <string>
+#include <vector>
#include <utility>
-#include <cstring> // strcmp
+#include <cstring> // strcmp()
+#include <cassert>
#include <exception>
#include <unordered_set>
+#include <unordered_map>
#include <build/path>
@@ -94,6 +98,53 @@ namespace build
};
extern string_pool extension_pool;
+
+ // A pool of strings in which each string is assigned an individual
+ // index (or id) of type I (e.g., uint8_t, uint16_t, etc., depending
+ // on how many entries are expected). Index value 0 is reserved to
+ // indicate the no entry condition.
+ //
+ template <typename I>
+ struct string_table
+ {
+ // Find existing or insert new.
+ //
+ I
+ insert (const std::string& s)
+ {
+ std::size_t i (vec_.size () + 1);
+ auto r (map_.emplace (s, static_cast<I> (i)));
+
+ if (r.second)
+ {
+ assert (i <= std::numeric_limits<I>::max ());
+ vec_.push_back (&r.first->first);
+ }
+
+ return r.first->second;
+ }
+
+ // Find existing.
+ //
+ I
+ find (const std::string& s) const
+ {
+ auto i (map_.find (s));
+ return i != map_.end () ? i->second : 0;
+ }
+
+ // Reverse lookup.
+ //
+ const std::string&
+ operator[] (I i) const {assert (i > 0); return *vec_[i - 1];}
+
+ I
+ size () const {return static_cast<I> (vec_.size ());}
+
+ private:
+ std::unordered_map<std::string, I> map_;
+ std::vector<const std::string*> vec_;
+ };
}
#endif // BUILD_UTILITY