aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
Diffstat (limited to 'build')
-rw-r--r--build/.gitignore2
-rw-r--r--build/algorithm222
-rw-r--r--build/algorithm.cxx504
-rw-r--r--build/algorithm.ixx197
-rw-r--r--build/algorithm.txx58
-rw-r--r--build/b.cxx855
-rw-r--r--build/bin/module23
-rw-r--r--build/bin/module.cxx188
-rw-r--r--build/bin/rule39
-rw-r--r--build/bin/rule.cxx145
-rw-r--r--build/bin/target99
-rw-r--r--build/bin/target.cxx190
-rw-r--r--build/buildfile64
-rw-r--r--build/cli/module23
-rw-r--r--build/cli/module.cxx244
-rw-r--r--build/cli/rule32
-rw-r--r--build/cli/rule.cxx305
-rw-r--r--build/cli/target62
-rw-r--r--build/cli/target.cxx77
-rw-r--r--build/config/module26
-rw-r--r--build/config/module.cxx90
-rw-r--r--build/config/operation19
-rw-r--r--build/config/operation.cxx455
-rw-r--r--build/config/utility128
-rw-r--r--build/config/utility.cxx92
-rw-r--r--build/config/utility.ixx17
-rw-r--r--build/config/utility.txx45
-rw-r--r--build/context165
-rw-r--r--build/context.cxx391
-rw-r--r--build/context.txx141
-rw-r--r--build/cxx/compile32
-rw-r--r--build/cxx/compile.cxx794
-rw-r--r--build/cxx/install29
-rw-r--r--build/cxx/install.cxx66
-rw-r--r--build/cxx/link70
-rw-r--r--build/cxx/link.cxx875
-rw-r--r--build/cxx/module23
-rw-r--r--build/cxx/module.cxx230
-rw-r--r--build/cxx/target78
-rw-r--r--build/cxx/target.cxx81
-rw-r--r--build/cxx/utility37
-rw-r--r--build/cxx/utility.cxx29
-rw-r--r--build/cxx/utility.txx35
-rw-r--r--build/diagnostics402
-rw-r--r--build/diagnostics.cxx125
-rw-r--r--build/dist/module26
-rw-r--r--build/dist/module.cxx142
-rw-r--r--build/dist/operation18
-rw-r--r--build/dist/operation.cxx459
-rw-r--r--build/dist/rule29
-rw-r--r--build/dist/rule.cxx55
-rw-r--r--build/dump18
-rw-r--r--build/dump.cxx253
-rw-r--r--build/file144
-rw-r--r--build/file.cxx980
-rw-r--r--build/file.ixx12
-rw-r--r--build/install/module26
-rw-r--r--build/install/module.cxx188
-rw-r--r--build/install/operation18
-rw-r--r--build/install/operation.cxx32
-rw-r--r--build/install/rule49
-rw-r--r--build/install/rule.cxx410
-rw-r--r--build/install/utility40
-rw-r--r--build/lexer138
-rw-r--r--build/lexer.cxx431
-rw-r--r--build/module86
-rw-r--r--build/module.cxx114
-rw-r--r--build/name113
-rw-r--r--build/name.cxx63
-rw-r--r--build/operation359
-rw-r--r--build/operation.cxx232
-rw-r--r--build/options298
-rw-r--r--build/options.cli31
-rw-r--r--build/options.cxx552
-rw-r--r--build/options.ixx157
-rw-r--r--build/parser296
-rw-r--r--build/parser.cxx2206
-rw-r--r--build/path-io26
-rw-r--r--build/path-io.cxx39
-rw-r--r--build/prerequisite129
-rw-r--r--build/prerequisite.cxx82
-rw-r--r--build/rule135
-rw-r--r--build/rule-map115
-rw-r--r--build/rule.cxx249
-rw-r--r--build/scope312
-rw-r--r--build/scope.cxx317
-rw-r--r--build/search31
-rw-r--r--build/search.cxx171
-rw-r--r--build/spec61
-rw-r--r--build/spec.cxx81
-rw-r--r--build/target1084
-rw-r--r--build/target-key55
-rw-r--r--build/target-type97
-rw-r--r--build/target.cxx537
-rw-r--r--build/target.ixx85
-rw-r--r--build/target.txx58
-rw-r--r--build/test/module24
-rw-r--r--build/test/module.cxx88
-rw-r--r--build/test/operation18
-rw-r--r--build/test/operation.cxx32
-rw-r--r--build/test/rule30
-rw-r--r--build/test/rule.cxx439
-rw-r--r--build/token73
-rw-r--r--build/token.cxx35
-rw-r--r--build/types55
-rw-r--r--build/utility85
-rw-r--r--build/utility.cxx94
-rw-r--r--build/variable831
-rw-r--r--build/variable.cxx452
-rw-r--r--build/variable.ixx398
-rw-r--r--build/variable.txx168
-rw-r--r--build/version42
112 files changed, 0 insertions, 22307 deletions
diff --git a/build/.gitignore b/build/.gitignore
index 62f3460..225c27f 100644
--- a/build/.gitignore
+++ b/build/.gitignore
@@ -1,3 +1 @@
-b
-b-*
config.build
diff --git a/build/algorithm b/build/algorithm
deleted file mode 100644
index d144d6c..0000000
--- a/build/algorithm
+++ /dev/null
@@ -1,222 +0,0 @@
-// file : build/algorithm -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_ALGORITHM
-#define BUILD_ALGORITHM
-
-#include <string>
-#include <utility> // pair
-
-#include <build/types>
-#include <build/target>
-#include <build/operation>
-
-namespace build
-{
- class scope;
- class prerequisite;
- class prerequisite_key;
-
- // The default prerequisite search implementation. It first calls the
- // target-type-specific search function. If that doesn't yeld anything,
- // it creates a new target.
- //
- target&
- search (prerequisite&);
-
- // As above but specify the prerequisite to search as a key.
- //
- target&
- search (const prerequisite_key&);
-
- // As above but override the target type. Useful for searching for
- // target group members where we need to search for a different
- // target type.
- //
- target&
- search (const target_type&, const prerequisite_key&);
-
- // As above but specify the prerequisite to search as individual
- // key components.
- //
- target&
- search (const target_type& type,
- const dir_path& dir,
- const std::string& name,
- const std::string* ext,
- scope*);
-
- // As above but specify the target type as template argument.
- //
- template <typename T>
- T&
- search (const dir_path& dir,
- const std::string& name,
- const std::string* ext,
- scope*);
-
- // Search for a target identified by the name. The semantics
- // is "as if" we first created a prerequisite based on this
- // name in exactly the same way as the parser would and then
- // searched based on this prerequisite.
- //
- target&
- search (name, scope&);
-
- // Match and apply a rule to the action/target with ambiguity
- // detection. Increment the target's dependents count, which
- // means that you should call this function with the intent
- // to also call execute(). In case of optimizations that would
- // avoid calling execute(), call unmatch() to indicate this.
- //
- void
- match (action, target&);
-
- // Note that calling this function only makes sense if the
- // target itself doesn't have its own dependents.
- //
- void
- unmatch (action, target&);
-
- // Match (but do not apply) a rule to the action/target with
- // ambiguity detection. Note that this function does not touch
- // the dependents count.
- //
- void
- match_only (action, target&);
-
- // Match a "delegate rule" from withing another rules' apply()
- // function. Return recipe and recipe action (if any). Note
- // that unlike match(), this call doesn't increment the
- // dependents count. See also the companion execute_delegate().
- //
- std::pair<recipe, action>
- match_delegate (action, target&);
-
- // The standard prerequisite search and match implementations. They call
- // search_and_match_*() versions below passing non-empty directory for
- // the clean operation.
- //
- void
- search_and_match_prerequisites (action, target&);
-
- // If we are cleaning, this function doesn't go into group members,
- // as an optimization (the group should clean everything up).
- //
- void
- search_and_match_prerequisite_members (action, target&);
-
- // The actual prerequisite search and match implementations. They call
- // search() and then match() for each prerequisite in a loop. If this
- // target is a member of a group, then they first do this to the group's
- // prerequisites.
- //
- // If the directory argument is not empty, then they ignore (do not
- // match) prerequisites that are not in the same or its subdirectory.
- //
- void
- search_and_match_prerequisites (action, target&, const dir_path&);
-
- void
- search_and_match_prerequisite_members (action, target&, const dir_path&);
-
- // Unless already available, match, and, if necessary, execute the group
- // in order to obtain its members list. Note that even after that the
- // member's list might still not be available (e.g., if some wildcard/
- // fallback rule matched).
- //
- group_view
- resolve_group_members (action, target&);
-
- // Inject dependency on the parent directory's fsdir{}, unless it is
- // the project's out_root (or is outside of any project; say, for
- // example, an install directory). Normally this function is called
- // from the rule's apply() function.
- //
- void
- inject_parent_fsdir (action, target&);
-
- // Execute the action on target, assuming a rule has been matched
- // and the recipe for this action has been set. This is the default
- // executor implementation. Decrements the dependents count.
- //
- target_state
- execute (action, target&);
-
- // Execute the recipe obtained with match_delegate(). Note that
- // the target's state is neither checked nor updated by this
- // function. In other words, the appropriate usage is to call
- // this function from another recipe and to factor the obtained
- // state into the one returned.
- //
- target_state
- execute_delegate (const recipe&, action, target&);
-
- // A special version of the above that should be used for "direct"
- // and "now" execution, that is, side-stepping the normal target-
- // prerequisite relationship (so no dependents count is decremented)
- // and execution order (so this function will never return postponed
- // target state).
- //
- target_state
- execute_direct (action, target&);
-
- // The default prerequisite execute implementation. It calls execute()
- // on each non-ignored (non-NULL) prerequisite target in a loop. If this
- // target is a member of a group, then it first does this to the group's
- // prerequisites. Returns target_state::changed if any of them were
- // changed and target_state::unchanged otherwise. Note that this
- // function can be used as a recipe.
- //
- target_state
- execute_prerequisites (action, target&);
-
- // As above but iterates over the prerequisites in reverse.
- //
- target_state
- reverse_execute_prerequisites (action, target&);
-
- // A version of the above that also determines whether the action
- // needs to be executed on the target based on the passed mtime
- // timestamp.
- //
- // Note that because we use mtime, this function should normally
- // only be used in the perform_update action.
- //
- bool
- execute_prerequisites (action, target&, const timestamp&);
-
- // Another version of the above that does two extra things for the
- // 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., a source file). If
- // there are multiple prerequisites of this type, then the last
- // is returned.
- //
- template <typename T>
- T*
- execute_prerequisites (action, target&, const timestamp&);
-
- // Return noop_recipe instead of using this function directly.
- //
- target_state
- noop_action (action, target&);
-
- // Default action implementation which forwards to the prerequisites.
- // Use default_recipe instead of using this function directly.
- //
- target_state
- default_action (action, target&);
-
- // Standard perform(clean) action implementation for the file target
- // or derived.
- //
- target_state
- perform_clean (action, target&);
-}
-
-#include <build/algorithm.ixx>
-#include <build/algorithm.txx>
-
-#endif // BUILD_ALGORITHM
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
deleted file mode 100644
index a2ee7c0..0000000
--- a/build/algorithm.cxx
+++ /dev/null
@@ -1,504 +0,0 @@
-// file : build/algorithm.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/algorithm>
-
-#include <memory> // unique_ptr
-#include <cstddef> // size_t
-#include <utility> // move
-#include <cassert>
-
-#include <butl/utility> // reverse_iterate
-
-#include <build/scope>
-#include <build/target>
-#include <build/prerequisite>
-#include <build/rule>
-#include <build/file> // import()
-#include <build/search>
-#include <build/context>
-#include <build/utility>
-#include <build/diagnostics>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- target&
- search (const prerequisite_key& pk)
- {
- // If this is a project-qualified prerequisite, then this
- // is import's business.
- //
- if (pk.proj != nullptr)
- return import (pk);
-
- if (target* t = pk.tk.type->search (pk))
- return *t;
-
- return create_new_target (pk);
- }
-
- target&
- search (name n, scope& s)
- {
- const string* e;
- const target_type* tt (s.find_target_type (n, e));
-
- if (tt == nullptr)
- fail << "unknown target type " << n.type << " in name " << n;
-
- n.dir.normalize ();
-
- return search (*tt, move (n.dir), move (n.value), e, &s);
- }
-
- pair<const rule*, match_result>
- match_impl (action a, target& t, bool apply)
- {
- pair<const rule*, match_result> r;
-
- // By default, clear the resolved targets list before calling
- // match(). The rule is free to modify this list in match()
- // (provided that it matches) in order to, for example, prepare
- // it for apply().
- //
- t.reset (a);
-
- // If this is a nested operation, first try the outer operation.
- // This allows a rule to implement a "precise match", that is,
- // both inner and outer operations match.
- //
- for (operation_id oo (a.outer_operation ()), io (a.operation ()),
- o (oo != 0 ? oo : io); o != 0; o = (oo != 0 ? io : 0))
- {
- // Adjust action for recipe: on the first iteration we want it
- // {inner, outer} (which is the same as 'a') while on the second
- // -- {inner, 0}. Note that {inner, 0} is the same or "stronger"
- // (i.e., overrides; see action::operator<()) than 'a'. This
- // allows "unconditional inner" to override "inner for outer"
- // recipes.
- //
- action ra (a.meta_operation (), io, o != oo ? 0 : oo);
-
- scope& bs (t.base_scope ());
-
- for (auto tt (&t.type ()); tt != nullptr; tt = tt->base)
- {
- // Search scopes outwards, stopping at the project root.
- //
- for (const scope* s (&bs);
- s != nullptr;
- s = s->root () ? global_scope : s->parent_scope ())
- {
- const operation_rule_map* om (s->rules[a.meta_operation ()]);
-
- if (om == nullptr)
- continue; // No entry for this meta-operation id.
-
- // First try the map for the actual operation. If that
- // doesn't yeld anything, try the wildcard map.
- //
- for (size_t oi (o), oip (o); oip != 0; oip = oi, oi = 0)
- {
- const target_type_rule_map* ttm ((*om)[oi]);
-
- if (ttm == nullptr)
- continue; // No entry for this operation id.
-
- if (ttm->empty ())
- continue; // Empty map for this operation id.
-
- auto i (ttm->find (tt));
-
- if (i == ttm->end () || i->second.empty ())
- continue; // No rules registered for this target type.
-
- const auto& rules (i->second); // Hint map.
-
- // @@ 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)
- {
- const string& n (i->first);
- const rule& ru (i->second);
-
- match_result m;
- {
- auto g (
- make_exception_guard (
- [ra, &t, &n]()
- {
- info << "while matching rule " << n << " to "
- << diag_do (ra, t);
- }));
-
- if (!(m = ru.match (ra, t, hint)))
- continue;
-
- if (!m.recipe_action.valid ())
- m.recipe_action = ra; // Default, if not set.
- }
-
- // Do the ambiguity test.
- //
- bool ambig (false);
-
- diag_record dr;
-
- for (++i; i != rs.second; ++i)
- {
- const string& n1 (i->first);
- const rule& ru1 (i->second);
-
- {
- auto g (
- make_exception_guard (
- [ra, &t, &n1]()
- {
- info << "while matching rule " << n1 << " to "
- << diag_do (ra, t);
- }));
-
- if (!ru1.match (ra, t, hint))
- continue;
- }
-
- if (!ambig)
- {
- dr << fail << "multiple rules matching "
- << diag_doing (ra, t)
- << info << "rule " << n << " matches";
- ambig = true;
- }
-
- dr << info << "rule " << n1 << " also matches";
- }
-
- if (!ambig)
- {
- ra = m.recipe_action; // Use custom, if set.
-
- if (apply)
- {
- auto g (
- make_exception_guard (
- [ra, &t, &n]()
- {
- info << "while applying rule " << n << " to "
- << diag_do (ra, t);
- }));
-
- // @@ We could also allow the rule to change the recipe
- // action in apply(). Could be useful with delegates.
- //
- t.recipe (ra, ru.apply (ra, t, m));
- }
- else
- {
- r.first = &ru;
- r.second = move (m);
- }
-
- return r;
- }
- else
- dr << info << "use rule hint to disambiguate this match";
- }
- }
- }
- }
- }
-
- diag_record dr;
- dr << fail << "no rule to " << diag_do (a, t);
-
- if (verb < 4)
- dr << info << "re-run with --verbose 4 for more information";
-
- return r;
- }
-
- group_view
- resolve_group_members_impl (action a, target& g)
- {
- group_view r;
-
- // Unless we already have a recipe, try matching the target to
- // the rule.
- //
- if (!g.recipe (a))
- {
- auto rp (match_impl (a, g, false));
-
- r = g.group_members (a);
- if (r.members != nullptr)
- return r;
-
- // That didn't help, so apply the rule and go to the building
- // phase.
- //
- const match_result& mr (rp.second);
- g.recipe (mr.recipe_action, rp.first->apply (mr.recipe_action, g, mr));
- }
-
- // Note that we use execute_direct() rather than execute() here to
- // sidestep the dependents count logic. In this context, this is by
- // definition the first attempt to execute this rule (otherwise we
- // would have already known the members list) and we really do need
- // to execute it now.
- //
- execute_direct (a, g);
-
- r = g.group_members (a);
- return r; // Might still be unresolved.
- }
-
- void
- search_and_match_prerequisites (action a, target& t, const dir_path& d)
- {
- const bool e (d.empty ());
-
- for (prerequisite p: group_prerequisites (t))
- {
- target& pt (search (p));
-
- if (e || pt.dir.sub (d))
- {
- match (a, pt);
- t.prerequisite_targets.push_back (&pt);
- }
- }
- }
-
- void
- search_and_match_prerequisite_members (action a,
- target& t,
- const dir_path& d)
- {
- const bool e (d.empty ());
-
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- target& pt (p.search ());
-
- if (e || pt.dir.sub (d))
- {
- match (a, pt);
- t.prerequisite_targets.push_back (&pt);
- }
- }
- }
-
- void
- inject_parent_fsdir (action a, target& t)
- {
- tracer trace ("inject_parent_fsdir");
-
- scope& s (t.base_scope ());
- scope* rs (s.root_scope ());
-
- if (rs == nullptr) // Could be outside any project.
- return;
-
- const dir_path& out_root (rs->out_path ());
-
- // If t is a directory (name is empty), say foo/bar/, then
- // t is bar and its parent directory is foo/.
- //
- const dir_path& d (t.name.empty () ? t.dir.directory () : t.dir);
-
- if (!d.sub (out_root) || d == out_root)
- return;
-
- level6 ([&]{trace << "for " << t;});
-
- fsdir& dt (search<fsdir> (d, string (), nullptr, &s));
- match (a, dt);
- t.prerequisite_targets.emplace_back (&dt);
- }
-
- target_state
- execute_impl (action a, target& t)
- {
- // Implementation with some multi-threading ideas in mind.
- //
- switch (t.raw_state)
- {
- case target_state::group: // Means group's state is unknown.
- case target_state::unknown:
- case target_state::postponed:
- {
- auto g (
- make_exception_guard (
- [a, &t]()
- {
- t.raw_state = target_state::failed;
- info << "while " << diag_doing (a, t);
- }));
-
- target_state ts (t.recipe (a) (a, t));
- assert (ts != target_state::unknown && ts != target_state::failed);
-
- // Set the target's state unless it should be the group's state.
- //
- if (t.raw_state != target_state::group)
- t.raw_state = ts;
-
- return ts;
- }
- case target_state::unchanged:
- case target_state::changed:
- // Should have been handled by inline execute().
- assert (false);
- case target_state::failed:
- break;
- }
-
- throw failed ();
- }
-
- target_state
- execute_prerequisites (action a, target& t)
- {
- target_state r (target_state::unchanged);
-
- for (target* pt: t.prerequisite_targets)
- {
- if (pt == nullptr) // Skipped.
- continue;
-
- r |= execute (a, *pt);
- }
-
- return r;
- }
-
- target_state
- reverse_execute_prerequisites (action a, target& t)
- {
- target_state r (target_state::unchanged);
-
- for (target* pt: reverse_iterate (t.prerequisite_targets))
- {
- if (pt == nullptr) // Skipped.
- continue;
-
- r |= execute (a, *pt);
- }
-
- return r;
- }
-
- bool
- execute_prerequisites (action a, target& t, const timestamp& mt)
- {
- bool e (mt == timestamp_nonexistent);
-
- for (target* pt: t.prerequisite_targets)
- {
- if (pt == nullptr) // Skipped.
- continue;
-
- target_state ts (execute (a, *pt));
-
- if (!e)
- {
- // If this is an mtime-based target, then compare timestamps.
- //
- if (auto mpt = dynamic_cast<const mtime_target*> (pt))
- {
- timestamp mp (mpt->mtime ());
-
- // 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 changed in this run which means the
- // action must be executed on the target as well.
- //
- if (mt < mp || (mt == mp && ts == target_state::changed))
- e = true;
- }
- else
- {
- // Otherwise we assume the prerequisite is newer if it was changed.
- //
- if (ts == target_state::changed)
- e = true;
- }
- }
- }
-
- return e;
- }
-
- target_state
- noop_action (action, target&)
- {
- assert (false); // We shouldn't be called, see target::recipe().
- return target_state::unchanged;
- }
-
- target_state
- group_action (action a, target& t)
- {
- target_state r (execute (a, *t.group));
-
- // Indicate to the standard execute() logic that this target's
- // state comes from the group.
- //
- t.raw_state = target_state::group;
-
- return r;
- }
-
- target_state
- default_action (action a, target& t)
- {
- return current_mode == execution_mode::first
- ? execute_prerequisites (a, t)
- : reverse_execute_prerequisites (a, t);
- }
-
- target_state
- perform_clean (action a, target& t)
- {
- // The reverse order of update: first delete the file, then clean
- // prerequisites.
- //
- file& ft (dynamic_cast<file&> (t));
-
- target_state r (rmfile (ft.path (), ft)
- ? target_state::changed
- : target_state::unchanged);
-
- // Update timestamp in case there are operations after us that
- // could use the information.
- //
- ft.mtime (timestamp_nonexistent);
-
- // Clean prerequisites.
- //
- r |= reverse_execute_prerequisites (a, t);
-
- return r;
- }
-}
diff --git a/build/algorithm.ixx b/build/algorithm.ixx
deleted file mode 100644
index 309f99b..0000000
--- a/build/algorithm.ixx
+++ /dev/null
@@ -1,197 +0,0 @@
-// file : build/algorithm.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <utility> // pair
-
-#include <build/rule>
-#include <build/prerequisite>
-#include <build/context>
-
-namespace build
-{
- inline target&
- search (prerequisite& p)
- {
- if (p.target == nullptr)
- p.target = &search (p.key ());
-
- return *p.target;
- }
-
- inline target&
- search (const target_type& t, const prerequisite_key& k)
- {
- return search (
- prerequisite_key
- {k.proj, {&t, k.tk.dir, k.tk.name, k.tk.ext}, k.scope});
- }
-
- inline target&
- search (const target_type& type,
- const dir_path& dir,
- const std::string& name,
- const std::string* ext,
- scope* scope)
- {
- return search (
- prerequisite_key {nullptr, {&type, &dir, &name, &ext}, scope});
- }
-
- template <typename T>
- inline T&
- search (const dir_path& dir,
- const std::string& name,
- const std::string* ext,
- scope* scope)
- {
- return static_cast<T&> (search (T::static_type, dir, name, ext, scope));
- }
-
- std::pair<const rule*, match_result>
- match_impl (action, target&, bool apply);
-
- inline void
- match (action a, target& t)
- {
- if (!t.recipe (a))
- match_impl (a, t, true);
-
- t.dependents++;
- dependency_count++;
-
- // text << "M " << t << ": " << t.dependents << " " << dependency_count;
- }
-
- inline void
- unmatch (action, target& t)
- {
- // text << "U " << t << ": " << t.dependents << " " << dependency_count;
-
- assert (t.dependents != 0 && dependency_count != 0);
- t.dependents--;
- dependency_count--;
- }
-
- inline void
- match_only (action a, target& t)
- {
- if (!t.recipe (a))
- match_impl (a, t, false);
- }
-
- inline std::pair<recipe, action>
- match_delegate (action a, target& t)
- {
- auto rp (match_impl (a, t, false));
- const match_result& mr (rp.second);
- return std::make_pair (rp.first->apply (mr.recipe_action, t, mr),
- mr.recipe_action);
- }
-
- group_view
- resolve_group_members_impl (action, target&);
-
- inline group_view
- resolve_group_members (action a, target& g)
- {
- group_view r (g.group_members (a));
- return r.members != nullptr ? r : resolve_group_members_impl (a, g);
- }
-
- inline void
- search_and_match_prerequisites (action a, target& t)
- {
- search_and_match_prerequisites (
- a,
- t,
- a.operation () != clean_id
- ? dir_path ()
- : t.strong_scope ().out_path ());
- }
-
- inline void
- search_and_match_prerequisite_members (action a, target& t)
- {
- if (a.operation () != clean_id)
- search_and_match_prerequisite_members (a, t, dir_path ());
- else
- // Note that here we don't iterate over members even for see-
- // through groups since the group target should clean eveything
- // up. A bit of an optimization.
- //
- search_and_match_prerequisites (a, t, t.strong_scope ().out_path ());
- }
-
- target_state
- execute_impl (action, target&);
-
- inline target_state
- execute (action a, target& t)
- {
- // text << "E " << t << ": " << t.dependents << " " << dependency_count;
-
- if (dependency_count != 0) // Re-examination of a postponed target?
- {
- assert (t.dependents != 0);
- t.dependents--;
- dependency_count--;
- }
-
- switch (target_state ts = t.state ())
- {
- case target_state::unchanged:
- case target_state::changed:
- return ts;
- default:
- {
- // Handle the "last" execution mode.
- //
- // This gets interesting when we consider interaction with
- // groups. It seem to make sense to treat group members as
- // dependents of the group, so, for example, if we try to
- // clean the group via three of its members, only the last
- // attempt will actually execute the clean. This means that
- // when we match a group member, inside we should also match
- // the group in order to increment the dependents count. This
- // seems to be a natural requirement: if we are delegating to
- // the group, we need to find a recipe for it, just like we
- // would for a prerequisite.
- //
- // Note that below we are going to change the group state
- // to postponed. This is not a mistake: until we execute
- // the recipe, we want to keep returning postponed. And
- // once the recipe is executed, it will reset the state
- // to group (see group_action()). To put it another way,
- // the execution of this member is postponed, not of the
- // group.
- //
- // One important invariant to keep in mind: the return
- // value from execute() should always be the same as what
- // would get returned by a subsequent call to state().
- //
- if (current_mode == execution_mode::last && t.dependents != 0)
- return (t.raw_state = target_state::postponed);
-
- return execute_impl (a, t);
- }
- }
- }
-
- inline target_state
- execute_delegate (const recipe& r, action a, target& t)
- {
- return r (a, t);
- }
-
- inline target_state
- execute_direct (action a, target& t)
- {
- switch (target_state ts = t.state ())
- {
- case target_state::unchanged:
- case target_state::changed: return ts;
- default: return execute_impl (a, t);
- }
- }
-}
diff --git a/build/algorithm.txx b/build/algorithm.txx
deleted file mode 100644
index 2490962..0000000
--- a/build/algorithm.txx
+++ /dev/null
@@ -1,58 +0,0 @@
-// file : build/algorithm.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace build
-{
- template <typename T>
- T*
- 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
- // or can scan the target type info myself. I think latter.
- //
- bool e (mt == timestamp_nonexistent);
- T* r (nullptr);
-
- for (target* pt: t.prerequisite_targets)
- {
- if (pt == nullptr) // Skip ignored.
- continue;
-
- target_state ts (execute (a, *pt));
-
- if (!e)
- {
- // If this is an mtime-based target, then compare timestamps.
- //
- if (auto mpt = dynamic_cast<const mtime_target*> (pt))
- {
- timestamp mp (mpt->mtime ());
-
- // 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 changed in this run which means the
- // action must be executed on the target as well.
- //
- if (mt < mp || (mt == mp && ts == target_state::changed))
- e = true;
- }
- else
- {
- // Otherwise we assume the prerequisite is newer if it was changed.
- //
- if (ts == target_state::changed)
- e = true;
- }
- }
-
- if (T* tmp = dynamic_cast<T*> (pt))
- r = tmp;
- }
-
- assert (r != nullptr);
- return e ? r : nullptr;
- }
-}
diff --git a/build/b.cxx b/build/b.cxx
deleted file mode 100644
index d72fdfb..0000000
--- a/build/b.cxx
+++ /dev/null
@@ -1,855 +0,0 @@
-// file : build/b.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <time.h> // tzset()
-#include <string.h> // strerror()
-
-#include <stdlib.h> // getenv()
-#include <unistd.h> // getuid()
-#include <sys/types.h> // uid_t
-#include <pwd.h> // struct passwd, getpwuid()
-
-#include <sstream>
-#include <cassert>
-#include <typeinfo>
-#include <iostream>
-#include <system_error>
-
-#include <butl/filesystem>
-
-#include <build/version>
-
-#include <build/types>
-#include <build/spec>
-#include <build/operation>
-#include <build/scope>
-#include <build/target>
-#include <build/prerequisite>
-#include <build/rule>
-#include <build/file>
-#include <build/module>
-#include <build/algorithm>
-#include <build/diagnostics>
-#include <build/context>
-#include <build/utility>
-#include <build/variable>
-
-#include <build/token>
-#include <build/lexer>
-#include <build/parser>
-
-#include <build/options>
-
-using namespace std;
-
-#include <build/config/module>
-#include <build/dist/module>
-#include <build/bin/module>
-#include <build/cxx/module>
-#include <build/cli/module>
-#include <build/test/module>
-#include <build/install/module>
-
-using namespace build;
-
-int
-main (int argc, char* argv[])
-{
- try
- {
- tracer trace ("main");
-
- cl::argv_scanner scan (argc, argv, true);
- options ops (scan);
-
- // Version.
- //
- if (ops.version ())
- {
- cout << "build2 " << BUILD_VERSION_STR<< endl
- << "libbutl " << LIBBUTL_VERSION_STR << endl
- << "Copyright (c) 2014-2015 Code Synthesis Ltd" << endl
- << "This is free software released under the MIT license." << endl;
- return 0;
- }
-
- // Help.
- //
- if (ops.help ())
- {
- ostream& o (cout);
-
- o << "Usage: " << argv[0] << " [options] [variables] [buildspec]" << endl
- << "Options:" << endl;
-
- options::print_usage (o);
- return 0;
- }
-
- // Diagnostics verbosity.
- //
- verb = ops.verbose_specified ()
- ? ops.verbose ()
- : ops.v () ? 2 : ops.q () ? 0 : 1;
-
- // Initialize time conversion data that is used by localtime_r().
- //
- tzset ();
-
- // Register builtin modules.
- //
- builtin_modules["config"] = module_functions {&config::config_boot,
- &config::config_init};
- builtin_modules["dist"] = module_functions {&dist::dist_boot,
- &dist::dist_init};
- builtin_modules["test"] = module_functions {&test::test_boot,
- &test::test_init};
- builtin_modules["install"] = module_functions {&install::install_boot,
- &install::install_init};
-
- builtin_modules["bin"] = module_functions {nullptr, &bin::bin_init};
- builtin_modules["cxx"] = module_functions {nullptr, &cxx::cxx_init};
- builtin_modules["cli"] = module_functions {nullptr, &cli::cli_init};
-
- // Figure out work and home directories.
- //
- work = dir_path::current ();
-
- if (const char* h = getenv ("HOME"))
- home = dir_path (h);
- else
- {
- struct passwd* pw (getpwuid (getuid ()));
-
- if (pw == nullptr)
- {
- const char* msg (strerror (errno));
- fail << "unable to determine home directory: " << msg;
- }
-
- home = dir_path (pw->pw_dir);
- }
-
- if (verb >= 5)
- {
- trace << "work dir: " << work;
- trace << "home dir: " << home;
- }
-
- // Initialize the dependency state.
- //
- reset ();
-
- // Parse command line variables. They should come before the
- // buildspec.
- //
- int argi (1);
- for (; argi != argc; argi++)
- {
- const char* s (argv[argi]);
-
- istringstream is (s);
- is.exceptions (istringstream::failbit | istringstream::badbit);
- lexer l (is, "<cmdline>");
- token t (l.next ());
-
- if (t.type == token_type::eos)
- continue; // Whitespace-only argument.
-
- // Unless this is a name followed by = or +=, assume it is
- // a start of the buildspec.
- //
- if (t.type != token_type::name)
- break;
-
- token_type tt (l.next ().type);
-
- if (tt != token_type::equal &&
- tt != token_type::equal_plus &&
- tt != token_type::plus_equal)
- break;
-
- parser p;
- t = p.parse_variable (l, *global_scope, t.value, tt);
-
- if (t.type != token_type::eos)
- fail << "unexpected " << t << " in variable " << s;
- }
-
- // Parse the buildspec.
- //
- buildspec bspec;
- {
- // Merge all the individual buildspec arguments into a single
- // string. Instead, we could also parse them individually (
- // and merge the result). The benefit of doing it this way
- // is potentially better diagnostics (i.e., we could have
- // used <buildspec-1>, <buildspec-2> to give the idea about
- // which argument is invalid).
- //
- string s;
- for (; argi != argc;)
- {
- s += argv[argi];
- if (++argi != argc)
- s += ' ';
- }
-
- try
- {
- istringstream is (s);
- is.exceptions (istringstream::failbit | istringstream::badbit);
-
- parser p;
- bspec = p.parse_buildspec (is, "<buildspec>");
- }
- catch (const istringstream::failure&)
- {
- fail << "unable to parse buildspec '" << s << "'";
- }
- }
-
- level5 ([&]{trace << "buildspec: " << bspec;});
-
- if (bspec.empty ())
- bspec.push_back (metaopspec ()); // Default meta-operation.
-
- for (metaopspec& ms: bspec)
- {
- if (ms.empty ())
- ms.push_back (opspec ()); // Default operation.
-
- meta_operation_id mid (0); // Not yet translated.
- const meta_operation_info* mif (nullptr);
-
- bool lifted (false); // See below.
-
- for (opspec& os: ms)
- {
- const location l ("<buildspec>", 1, 0); //@@ TODO
-
- if (os.empty ()) // Default target: dir{}.
- os.push_back (targetspec (name ("dir", string ())));
-
- operation_id oid (0); // Not yet translated.
- const operation_info* oif (nullptr);
-
- operation_id pre_oid (0);
- const operation_info* pre_oif (nullptr);
-
- operation_id post_oid (0);
- const operation_info* post_oif (nullptr);
-
- // We do meta-operation and operation batches sequentially (no
- // parallelism). But multiple targets in an operation batch
- // can be done in parallel.
- //
- action_targets tgs;
- tgs.reserve (os.size ());
-
- // If the previous operation was lifted to meta-operation,
- // end the meta-operation batch.
- //
- if (lifted)
- {
- if (mif->meta_operation_post != nullptr)
- mif->meta_operation_post ();
-
- level5 ([&]{trace << "end meta-operation batch " << mif->name
- << ", id " << static_cast<uint16_t> (mid);});
-
- mid = 0;
- lifted = false;
- }
-
- for (targetspec& ts: os)
- {
- name& tn (ts.name);
-
- // First figure out the out_base of this target. The logic
- // is as follows: if a directory was specified in any form,
- // then that's the out_base. Otherwise, we check if the name
- // value has a directory prefix. This has a good balance of
- // control and the expected result in most cases.
- //
- dir_path out_base (tn.dir);
- if (out_base.empty ())
- {
- const string& v (tn.value);
-
- // Handle a few common cases as special: empty name, '.',
- // '..', as well as dir{foo/bar} (without trailing '/').
- // This code must be consistent with find_target_type().
- //
- if (v.empty () || v == "." || v == ".." || tn.type == "dir")
- out_base = dir_path (v);
- //
- // Otherwise, if this is a simple name, see if there is a
- // directory part in value.
- //
- else if (tn.untyped ())
- {
- // We cannot assume it is a valid filesystem name so we
- // will have to do the splitting manually.
- //
- path::size_type i (path::traits::rfind_separator (v));
-
- if (i != string::npos)
- out_base = dir_path (v, i != 0 ? i : 1); // Special case: "/".
- }
- }
-
- if (out_base.relative ())
- out_base = work / out_base;
-
- out_base.normalize ();
-
- // The order in which we determine the roots depends on whether
- // src_base was specified explicitly. There will also be a few
- // cases where we are guessing things that can turn out wrong.
- // Keep track of that so that we can issue more extensive
- // diagnostics for such cases.
- //
- bool guessing (false);
- dir_path src_root;
- dir_path out_root;
-
- dir_path& src_base (ts.src_base); // Update it in buildspec.
-
- if (!src_base.empty ())
- {
- // Make sure it exists. While we will fail further down
- // if it doesn't, the diagnostics could be confusing (e.g.,
- // unknown operation because we don't load bootstrap.build).
- //
- if (!dir_exists (src_base))
- fail << "src_base directory " << src_base << " does not exist";
-
- if (src_base.relative ())
- src_base = work / src_base;
-
- src_base.normalize ();
-
- // If the src_base was explicitly specified, search for src_root.
- //
- src_root = find_src_root (src_base);
-
- // If not found, assume this is a simple project with src_root
- // being the same as src_base.
- //
- if (src_root.empty ())
- {
- src_root = src_base;
- out_root = out_base;
- }
- else
- // Calculate out_root based on src_root/src_base.
- //
- try
- {
- out_root = out_base.directory (src_base.leaf (src_root));
- }
- catch (const invalid_path&)
- {
- fail << "out_base suffix does not match src_root" <<
- info << "src_root: " << src_root <<
- info << "out_base: " << out_base;
- }
- }
- else
- {
- // If no src_base was explicitly specified, search for out_root.
- //
- bool 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
- // simple project or a fresh out-of-tree build. To test for
- // the latter, try to find src_root starting from work. If
- // we can't, then assume it is the former case.
- //
- if (out_root.empty ())
- {
- src_root = find_src_root (work);
-
- if (!src_root.empty ())
- {
- src_base = work;
-
- if (src_root != src_base)
- {
- try
- {
- out_root = out_base.directory (src_base.leaf (src_root));
- }
- catch (const invalid_path&)
- {
- fail << "out_base directory suffix does not match src_base"
- << info << "src_base is " << src_base
- << info << "src_root is " << src_root
- << info << "out_base is " << out_base
- << info << "consider explicitly specifying src_base "
- << "for " << tn;
- }
- }
- else
- out_root = out_base;
- }
- else
- src_root = src_base = out_root = out_base;
-
- guessing = true;
- }
- else if (src)
- src_root = out_root;
- }
-
- // Now we know out_root and, if it was explicitly specified
- // or the same as out_root, src_root. The next step is to
- // create the root scope and load the out_root bootstrap
- // files, if any. Note that we might already have done this
- // as a result of one of the preceding target processing.
- //
- // If we know src_root, set that variable as well. This could
- // be of use to the bootstrap files (other than src-root.build,
- // which, BTW, doesn't need to exist if src_root == out_root).
- //
- scope& rs (create_root (out_root, src_root));
-
- bootstrap_out (rs);
-
- // See if the bootstrap process set/changed src_root.
- //
- value& v (rs.assign ("src_root"));
-
- if (v)
- {
- // If we also have src_root specified by the user, make
- // sure they match.
- //
- const dir_path& p (as<dir_path> (v));
-
- if (src_root.empty ())
- src_root = p;
- else if (src_root != p)
- fail << "bootstrapped src_root " << p << " does not match "
- << "specified " << src_root;
- }
- else
- {
- // Neither bootstrap nor the user produced src_root.
- //
- if (src_root.empty ())
- {
- // If it also wasn't explicitly specified, see if it is
- // the same as out_root.
- //
- if (is_src_root (out_root))
- src_root = out_root;
- else
- {
- // If not, then assume we are running from src_base
- // and calculate src_root based on out_root/out_base.
- //
- src_base = work;
- src_root = src_base.directory (out_base.leaf (out_root));
- guessing = true;
- }
- }
-
- v = src_root;
- }
-
- setup_root (rs);
-
- // At this stage we should have both roots and out_base figured
- // out. If src_base is still undetermined, calculate it.
- //
- if (src_base.empty ())
- src_base = src_root / out_base.leaf (out_root);
-
- // Now that we have src_root, load the src_root bootstrap file,
- // if there is one.
- //
- bool bootstrapped (bootstrap_src (rs));
-
- // Check that out_root that we have found is the innermost root
- // for this project. If it is not, then it means we are trying
- // to load a disfigured sub-project and that we do not support.
- // Why don't we support it? Because things are already complex
- // enough here.
- //
- if (auto l = rs.vars["subprojects"])
- {
- for (const name& n: *l)
- {
- if (n.pair != '\0')
- continue; // Skip project names.
-
- if (out_base.sub (out_root / n.dir))
- fail << tn << " is in a subproject of " << out_root <<
- info << "explicitly specify src_base for this target";
- }
- }
-
- // Create and bootstrap outer roots if any. Loading is done
- // by load_root_pre() (that would normally be called by the
- // meta-operation's load() callback below).
- //
- 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
- // all be known. We store the combined action id in uint8_t;
- // see <operation> for details.
- //
- assert (operation_table.size () <= 128);
- assert (meta_operation_table.size () <= 128);
-
- // Since we now know all the names of meta-operations and
- // operations, "lift" names that we assumed (from buildspec
- // syntax) were operations but are actually meta-operations.
- // Also convert empty names (which means they weren't explicitly
- // specified) to the defaults and verify that all the names are
- // known.
- //
- {
- const auto& mn (ms.name);
- const auto& on (os.name);
-
- meta_operation_id m (0);
- operation_id o (0);
-
- if (!on.empty ())
- {
- m = meta_operation_table.find (on);
-
- if (m != 0)
- {
- if (!mn.empty ())
- fail (l) << "nested meta-operation " << mn
- << '(' << on << ')';
-
- if (!lifted) // If this is the first target.
- {
- // End the previous meta-operation batch if there was one
- // and start a new one.
- //
- if (mid != 0)
- {
- assert (oid == 0);
-
- if (mif->meta_operation_post != nullptr)
- mif->meta_operation_post ();
-
- level5 ([&]{trace << "end meta-operation batch "
- << mif->name << ", id "
- << static_cast<uint16_t> (mid);});
-
- mid = 0;
- }
-
- lifted = true; // Flag to also end it; see above.
- }
- }
- else
- {
- o = operation_table.find (on);
-
- if (o == 0)
- {
- diag_record dr;
- dr << fail (l) << "unknown operation " << on;
-
- // If we guessed src_root and didn't load anything during
- // bootstrap, then this is probably a meta-operation that
- // would have been added by the module if src_root was
- // correct.
- //
- if (guessing && !bootstrapped)
- dr << info << "consider explicitly specifying src_base "
- << "for " << tn;
- }
- }
- }
-
- if (!mn.empty ())
- {
- m = meta_operation_table.find (mn);
-
- if (m == 0)
- {
- diag_record dr;
- dr << fail (l) << "unknown meta-operation " << mn;
-
- // Same idea as for the operation case above.
- //
- if (guessing && !bootstrapped)
- dr << info << "consider explicitly specifying src_base "
- << "for " << tn;
- }
- }
-
- // The default meta-operation is perform. The default
- // operation is assigned by the meta-operation below.
- //
- if (m == 0)
- m = perform_id;
-
- // If this is the first target in the meta-operation batch,
- // then set the batch meta-operation id.
- //
- if (mid == 0)
- {
- mid = m;
- mif = rs.meta_operations[m];
-
- if (mif == nullptr)
- fail (l) << "target " << tn << " does not support meta-"
- << "operation " << meta_operation_table[m];
-
- level5 ([&]{trace << "start meta-operation batch " << mif->name
- << ", id " << static_cast<uint16_t> (mid);});
-
- if (mif->meta_operation_pre != nullptr)
- mif->meta_operation_pre ();
-
- current_mif = mif;
- }
- //
- // Otherwise, check that all the targets in a meta-operation
- // batch have the same meta-operation implementation.
- //
- else
- {
- const meta_operation_info* mi (rs.meta_operations[mid]);
-
- if (mi == nullptr)
- fail (l) << "target " << tn << " does not support meta-"
- << "operation " << meta_operation_table[mid];
-
- if (mi != mif)
- fail (l) << "different implementations of meta-operation "
- << mif->name << " in the same meta-operation batch";
- }
-
- // If this is the first target in the operation batch, then set
- // the batch operation id.
- //
- if (oid == 0)
- {
- auto lookup =
- [&rs, &l, &tn] (operation_id o) -> const operation_info*
- {
- const operation_info* r (rs.operations[o]);
-
- if (r == nullptr)
- fail (l) << "target " << tn << " does not support "
- << "operation " << operation_table[o];
- return r;
- };
-
- if (o == 0)
- o = default_id;
-
- oif = lookup (o);
-
- level5 ([&]{trace << "start operation batch " << oif->name
- << ", id " << static_cast<uint16_t> (o);});
-
- // Allow the meta-operation to translate the operation.
- //
- if (mif->operation_pre != nullptr)
- oid = mif->operation_pre (o);
- else // Otherwise translate default to update.
- oid = (o == default_id ? update_id : o);
-
- if (o != oid)
- {
- oif = lookup (oid);
- level5 ([&]{trace << "operation translated to " << oif->name
- << ", id " << static_cast<uint16_t> (oid);});
- }
-
- // Handle pre/post operations.
- //
- if (oif->pre != nullptr && (pre_oid = oif->pre (mid)) != 0)
- {
- assert (pre_oid != default_id);
- pre_oif = lookup (pre_oid);
- }
-
- if (oif->post != nullptr && (post_oid = oif->post (mid)) != 0)
- {
- assert (post_oid != default_id);
- post_oif = lookup (post_oid);
- }
- }
- //
- // Similar to meta-operations, check that all the targets in
- // an operation batch have the same operation implementation.
- //
- else
- {
- auto check =
- [&rs, &l, &tn] (operation_id o, const operation_info* i)
- {
- const operation_info* r (rs.operations[o]);
-
- if (r == nullptr)
- fail (l) << "target " << tn << " does not support "
- << "operation " << operation_table[o];
-
- if (r != i)
- fail (l) << "different implementations of operation "
- << i->name << " in the same operation batch";
- };
-
- check (oid, oif);
-
- if (pre_oid != 0)
- check (pre_oid, pre_oif);
-
- if (post_oid != 0)
- check (post_oid, post_oif);
- }
- }
-
- if (verb >= 5)
- {
- trace << "target " << tn << ':';
- trace << " out_base: " << out_base;
- trace << " src_base: " << src_base;
- trace << " out_root: " << out_root;
- trace << " src_root: " << src_root;
- }
-
- path bf (src_base / path ("buildfile"));
-
- // If we were guessing src_base, check that the buildfile
- // exists and if not, issue more detailed diagnostics.
- //
- if (guessing && !file_exists (bf))
- fail << bf << " does not exist"
- << info << "consider explicitly specifying src_base "
- << "for " << tn;
-
- // Load the buildfile.
- //
- mif->load (bf, rs, out_base, src_base, l);
-
- // Next search and match the targets. We don't want to start
- // building before we know how to for all the targets in this
- // operation batch.
- //
- {
- scope& bs (scopes.find (out_base));
-
- const string* e;
- const target_type* ti (bs.find_target_type (tn, e));
-
- if (ti == nullptr)
- fail (l) << "unknown target type " << tn.type;
-
- // If the directory is relative, assume it is relative to work
- // (must be consistent with how we derived out_base above).
- //
- dir_path& d (tn.dir);
-
- if (d.relative ())
- d = work / d;
-
- d.normalize ();
-
- mif->search (rs, target_key {ti, &d, &tn.value, &e}, l, tgs);
- }
- }
-
- if (pre_oid != 0)
- {
- level5 ([&]{trace << "start pre-operation batch " << pre_oif->name
- << ", id " << static_cast<uint16_t> (pre_oid);});
-
- if (mif->operation_pre != nullptr)
- mif->operation_pre (pre_oid); // Cannot be translated.
-
- current_inner_oif = pre_oif;
- current_outer_oif = oif;
- current_mode = pre_oif->mode;
- dependency_count = 0;
-
- action a (mid, pre_oid, oid);
-
- mif->match (a, tgs);
- mif->execute (a, tgs, true); // Run quiet.
-
- if (mif->operation_post != nullptr)
- mif->operation_post (pre_oid);
-
- level5 ([&]{trace << "end pre-operation batch " << pre_oif->name
- << ", id " << static_cast<uint16_t> (pre_oid);});
- }
-
- current_inner_oif = oif;
- current_outer_oif = nullptr;
- current_mode = oif->mode;
- dependency_count = 0;
-
- action a (mid, oid, 0);
-
- mif->match (a, tgs);
- mif->execute (a, tgs, verb == 0);
-
- if (post_oid != 0)
- {
- level5 ([&]{trace << "start post-operation batch " << post_oif->name
- << ", id " << static_cast<uint16_t> (post_oid);});
-
- if (mif->operation_pre != nullptr)
- mif->operation_pre (post_oid); // Cannot be translated.
-
- current_inner_oif = post_oif;
- current_outer_oif = oif;
- current_mode = post_oif->mode;
- dependency_count = 0;
-
- action a (mid, post_oid, oid);
-
- mif->match (a, tgs);
- mif->execute (a, tgs, true); // Run quiet.
-
- if (mif->operation_post != nullptr)
- mif->operation_post (post_oid);
-
- level5 ([&]{trace << "end post-operation batch " << post_oif->name
- << ", id " << static_cast<uint16_t> (post_oid);});
- }
-
- if (mif->operation_post != nullptr)
- mif->operation_post (oid);
-
- level5 ([&]{trace << "end operation batch " << oif->name
- << ", id " << static_cast<uint16_t> (oid);});
- }
-
- if (mif->meta_operation_post != nullptr)
- mif->meta_operation_post ();
-
- level5 ([&]{trace << "end meta-operation batch " << mif->name
- << ", id " << static_cast<uint16_t> (mid);});
- }
- }
- catch (const failed&)
- {
- return 1; // Diagnostics has already been issued.
- }
- /*
- catch (const std::exception& e)
- {
- error << e.what ();
- return 1;
- }
- */
-}
diff --git a/build/bin/module b/build/bin/module
deleted file mode 100644
index 5dd720f..0000000
--- a/build/bin/module
+++ /dev/null
@@ -1,23 +0,0 @@
-// file : build/bin/module -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_BIN_MODULE
-#define BUILD_BIN_MODULE
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/module>
-
-namespace build
-{
- namespace bin
- {
- extern "C" bool
- bin_init (
- scope&, scope&, const location&, unique_ptr<module>&, bool, bool);
- }
-}
-
-#endif // BUILD_BIN_MODULE
diff --git a/build/bin/module.cxx b/build/bin/module.cxx
deleted file mode 100644
index 5f65b8d..0000000
--- a/build/bin/module.cxx
+++ /dev/null
@@ -1,188 +0,0 @@
-// file : build/bin/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/bin/module>
-
-#include <build/scope>
-#include <build/variable>
-#include <build/diagnostics>
-
-#include <build/config/utility>
-#include <build/install/utility>
-
-#include <build/bin/rule>
-#include <build/bin/target>
-
-using namespace std;
-
-namespace build
-{
- namespace bin
- {
- static obj_rule obj_;
- static lib_rule lib_;
-
- // Default config.bin.*.lib values.
- //
- static const strings exe_lib {"shared", "static"};
- static const strings liba_lib {"static"};
- static const strings libso_lib {"shared"};
-
- extern "C" bool
- bin_init (scope& r,
- scope& b,
- const location&,
- std::unique_ptr<module>&,
- bool first,
- bool)
- {
- tracer trace ("bin::init");
- level5 ([&]{trace << "for " << b.out_path ();});
-
- // Enter module variables.
- //
- if (first)
- {
- auto& v (var_pool);
-
- v.find ("config.bin.lib", string_type);
- v.find ("config.bin.exe.lib", strings_type);
- v.find ("config.bin.liba.lib", strings_type);
- v.find ("config.bin.libso.lib", strings_type);
- v.find ("config.bin.rpath", strings_type); //@@ VAR paths_type
-
- v.find ("bin.lib", string_type);
- v.find ("bin.exe.lib", strings_type);
- v.find ("bin.liba.lib", strings_type);
- v.find ("bin.libso.lib", strings_type);
- v.find ("bin.rpath", strings_type); //@@ VAR paths_type
-
- v.find ("bin.libprefix", string_type);
- }
-
- // Register target types.
- //
- {
- auto& t (b.target_types);
-
- t.insert<obja> ();
- t.insert<objso> ();
- t.insert<obj> ();
- t.insert<exe> ();
- t.insert<liba> ();
- t.insert<libso> ();
- t.insert<lib> ();
- }
-
- // Register rules.
- //
- {
- auto& r (b.rules);
-
- r.insert<obj> (perform_update_id, "bin.obj", obj_);
- r.insert<obj> (perform_clean_id, "bin.obj", obj_);
-
- r.insert<lib> (perform_update_id, "bin.lib", lib_);
- r.insert<lib> (perform_clean_id, "bin.lib", lib_);
-
- // Configure member.
- //
- r.insert<lib> (configure_update_id, "bin.lib", lib_);
-
- //@@ Should we check if the install module was loaded
- // (by checking if install operation is registered
- // for this project)? If we do that, then install
- // will have to be loaded before bin. Perhaps we
- // should enforce loading of all operation-defining
- // modules before all others?
- //
- r.insert<lib> (perform_install_id, "bin.lib", lib_);
- }
-
- // Configure.
- //
- using config::required;
-
- // The idea here is as follows: if we already have one of
- // the bin.* variables set, then we assume this is static
- // project configuration and don't bother setting the
- // corresponding config.bin.* variable.
- //
- //@@ Need to validate the values. Would be more efficient
- // to do it once on assignment than every time on query.
- // Custom var type?
- //
-
- // config.bin.lib
- //
- {
- value& v (b.assign ("bin.lib"));
- if (!v)
- v = required (r, "config.bin.lib", "both").first;
- }
-
- // config.bin.exe.lib
- //
- {
- value& v (b.assign ("bin.exe.lib"));
- if (!v)
- v = required (r, "config.bin.exe.lib", exe_lib).first;
- }
-
- // config.bin.liba.lib
- //
- {
- value& v (b.assign ("bin.liba.lib"));
- if (!v)
- v = required (r, "config.bin.liba.lib", liba_lib).first;
- }
-
- // config.bin.libso.lib
- //
- {
- value& v (b.assign ("bin.libso.lib"));
- if (!v)
- v = required (r, "config.bin.libso.lib", libso_lib).first;
- }
-
- // config.bin.rpath
- //
- // This one is optional and we merge it into bin.rpath, if any.
- // See the cxx module for details on merging.
- //
- if (const value& v = config::optional (r, "config.bin.rpath"))
- b.assign ("bin.rpath") += as<strings> (v);
-
-
- // Configure "installability" of our target types.
- //
- install::path<exe> (b, dir_path ("bin")); // Install into install.bin.
-
- // Should shared libraries have executable bit? That depends on
- // who you ask. In Debian, for example, it should not unless, it
- // really is executable (i.e., has main()). On the other hand, on
- // some systems, this may be required in order for the dynamic
- // linker to be able to load the library. So, by default, we will
- // keep it executable, especially seeing that this is also the
- // behavior of autotools. At the same time, it is easy to override
- // this, for example:
- //
- // config.install.lib.mode=644
- //
- // And a library that wants to override any such overrides (e.g.,
- // because it does have main()) can do:
- //
- // libso{foo}: install.mode=755
- //
- // Everyone is happy then?
- //
- install::path<libso> (b, dir_path ("lib")); // Install into install.lib.
-
- install::path<liba> (b, dir_path ("lib")); // Install into install.lib.
- install::mode<liba> (b, "644");
-
- return true;
- }
- }
-}
diff --git a/build/bin/rule b/build/bin/rule
deleted file mode 100644
index 743b1ca..0000000
--- a/build/bin/rule
+++ /dev/null
@@ -1,39 +0,0 @@
-// file : build/bin/rule -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_BIN_RULE
-#define BUILD_BIN_RULE
-
-#include <build/rule>
-
-namespace build
-{
- namespace bin
- {
- class obj_rule: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
- };
-
- class lib_rule: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform (action, target&);
- };
- }
-}
-
-#endif // BUILD_BIN_RULE
diff --git a/build/bin/rule.cxx b/build/bin/rule.cxx
deleted file mode 100644
index 21db183..0000000
--- a/build/bin/rule.cxx
+++ /dev/null
@@ -1,145 +0,0 @@
-// file : build/bin/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/bin/rule>
-
-#include <build/scope>
-#include <build/target>
-#include <build/algorithm>
-#include <build/diagnostics>
-
-#include <build/bin/target>
-
-using namespace std;
-
-namespace build
-{
- namespace bin
- {
- // obj
- //
- match_result obj_rule::
- match (action a, target& t, const std::string&) const
- {
- fail << diag_doing (a, t) << " target group" <<
- info << "explicitly select either obja{} or objso{} member";
-
- return nullptr;
- }
-
- recipe obj_rule::
- apply (action, target&, const match_result&) const {return empty_recipe;}
-
- // lib
- //
- // The whole logic is pretty much as if we had our two group
- // members as our prerequisites.
- //
- match_result lib_rule::
- match (action a, target& xt, const std::string&) const
- {
- lib& t (static_cast<lib&> (xt));
-
- // @@ We have to re-query it on each match_only()!
-
- // Get the library type to build. If not set for a target, this
- // should be configured at the project scope by init_lib().
- //
- const string& type (as<string> (*t["bin.lib"]));
-
- bool ar (type == "static" || type == "both");
- bool so (type == "shared" || type == "both");
-
- if (!ar && !so)
- fail << "unknown library type: " << type <<
- info << "'static', 'shared', or 'both' expected";
-
- // Search and pre-match the members. The pre-match here is part
- // of the "library meta-information protocol" that could be used
- // by the module that actually builds the members. The idea is
- // that pre-matching members may populate our prerequisite_targets
- // with prerequisite libraries from which others can extract the
- // meta-information about the library, such as the options to use
- // when linking it, etc.
- //
- if (ar)
- {
- if (t.a == nullptr)
- t.a = &search<liba> (t.dir, t.name, t.ext, nullptr);
-
- match_only (a, *t.a);
- }
-
- if (so)
- {
- if (t.so == nullptr)
- t.so = &search<libso> (t.dir, t.name, t.ext, nullptr);
-
- match_only (a, *t.so);
- }
-
- match_result mr (t, &type);
-
- // If there is an outer operation, indicate that we match
- // unconditionally so that we don't override ourselves.
- //
- if (a.outer_operation () != 0)
- mr.recipe_action = action (a.meta_operation (), a.operation ());
-
- return mr;
- }
-
- recipe lib_rule::
- apply (action a, target& xt, const match_result& mr) const
- {
- lib& t (static_cast<lib&> (xt));
-
- const string& type (*static_cast<const string*> (mr.cpvalue));
-
- bool ar (type == "static" || type == "both");
- bool so (type == "shared" || type == "both");
-
- // Now we do full match.
- //
- if (ar)
- build::match (a, *t.a);
-
- if (so)
- build::match (a, *t.so);
-
- return &perform;
- }
-
- target_state lib_rule::
- perform (action a, target& xt)
- {
- lib& t (static_cast<lib&> (xt));
-
- //@@ Not cool we have to do this again. Looks like we need
- // some kind of a cache vs resolved pointer, like in
- // prerequisite vs prerequisite_target.
- //
- //
- const string& type (as<string> (*t["bin.lib"]));
- bool ar (type == "static" || type == "both");
- bool so (type == "shared" || type == "both");
-
- target* m1 (ar ? t.a : nullptr);
- target* m2 (so ? t.so : nullptr);
-
- if (current_mode == execution_mode::last)
- swap (m1, m2);
-
- target_state r (target_state::unchanged);
-
- if (m1 != nullptr)
- r |= execute (a, *m1);
-
- if (m2 != nullptr)
- r |= execute (a, *m2);
-
- return r;
- }
- }
-}
diff --git a/build/bin/target b/build/bin/target
deleted file mode 100644
index cbabcaf..0000000
--- a/build/bin/target
+++ /dev/null
@@ -1,99 +0,0 @@
-// file : build/bin/target -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_BIN_TARGET
-#define BUILD_BIN_TARGET
-
-#include <build/target>
-
-namespace build
-{
- namespace bin
- {
- // The obj{} target group.
- //
- class obja: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class objso: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class obj: public target
- {
- public:
- using target::target;
-
- obja* a {nullptr};
- objso* so {nullptr};
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class exe: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- // The lib{} target group.
- //
- class liba: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class libso: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class lib: public target
- {
- public:
- using target::target;
-
- liba* a {nullptr};
- libso* so {nullptr};
-
- virtual void
- reset (action_type);
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
- }
-}
-
-#endif // BUILD_BIN_TARGET
diff --git a/build/bin/target.cxx b/build/bin/target.cxx
deleted file mode 100644
index 735c581..0000000
--- a/build/bin/target.cxx
+++ /dev/null
@@ -1,190 +0,0 @@
-// file : build/bin/target.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/bin/target>
-
-using namespace std;
-
-namespace build
-{
- namespace bin
- {
- static target*
- obja_factory (const target_type&, dir_path d, string n, const string* e)
- {
- obj* o (targets.find<obj> (d, n));
- obja* a (new obja (move (d), move (n), e));
-
- if ((a->group = o))
- o->a = a;
-
- return a;
- }
-
- const target_type obja::static_type
- {
- "obja",
- &file::static_type,
- &obja_factory,
- nullptr,
- &search_target, // Note: not _file(); don't look for an existing file.
- false
- };
-
- static target*
- objso_factory (const target_type&, dir_path d, string n, const string* e)
- {
- obj* o (targets.find<obj> (d, n));
- objso* so (new objso (move (d), move (n), e));
-
- if ((so->group = o))
- o->so = so;
-
- return so;
- }
-
- const target_type objso::static_type
- {
- "objso",
- &file::static_type,
- &objso_factory,
- nullptr,
- &search_target, // Note: not _file(); don't look for an existing file.
- false
- };
-
- static target*
- obj_factory (const target_type&, dir_path d, string n, const string* e)
- {
- obja* a (targets.find<obja> (d, n));
- objso* so (targets.find<objso> (d, n));
- obj* o (new obj (move (d), move (n), e));
-
- if ((o->a = a))
- a->group = o;
-
- if ((o->so = so))
- so->group = o;
-
- return o;
- }
-
- const target_type obj::static_type
- {
- "obj",
- &target::static_type,
- &obj_factory,
- nullptr,
- &search_target,
- false
- };
-
- const target_type exe::static_type
- {
- "exe",
- &file::static_type,
- &target_factory<exe>,
- nullptr,
- &search_file,
- false
- };
-
- static target*
- liba_factory (const target_type& t, dir_path d, string n, const string* e)
- {
- // Only link-up to the group if the types match exactly.
- //
- lib* l (t == liba::static_type ? targets.find<lib> (d, n) : nullptr);
- liba* a (new liba (move (d), move (n), e));
-
- if ((a->group = l))
- l->a = a;
-
- return a;
- }
-
- // @@
- //
- // What extensions should we use? At the outset, this is platform-
- // dependent. And if we consider cross-compilation, is it build or
- // host-dependent? Feels like it should be host-dependent so that
- // we can copy things between cross and native environments. So
- // these will have to be determined based on what we are building.
- // As if this is not complicated enough, the bin module doesn't
- // know anything about building. So perhaps the extension should
- // come from a variable that is set not by bin but by the module
- // whose rule matched the target (e.g., cxx::link).
- //
- constexpr const char a_ext[] = "a";
- const target_type liba::static_type
- {
- "liba",
- &file::static_type,
- &liba_factory,
- &target_extension_fix<a_ext>,
- &search_file,
- false
- };
-
- static target*
- libso_factory (const target_type& t, dir_path d, string n, const string* e)
- {
- // Only link-up to the group if the types match exactly.
- //
- lib* l (t == libso::static_type ? targets.find<lib> (d, n) : nullptr);
- libso* so (new libso (move (d), move (n), e));
-
- if ((so->group = l))
- l->so = so;
-
- return so;
- }
-
- constexpr const char so_ext[] = "so";
- const target_type libso::static_type
- {
- "libso",
- &file::static_type,
- &libso_factory,
- &target_extension_fix<so_ext>,
- &search_file,
- false
- };
-
- // lib
- //
- void lib::
- reset (action_type)
- {
- // Don't clear prerequisite_targets since it is "given" to our
- // members to implement "library meta-information protocol".
- }
-
- static target*
- lib_factory (const target_type&, dir_path d, string n, const string* e)
- {
- liba* a (targets.find<liba> (d, n));
- libso* so (targets.find<libso> (d, n));
- lib* l (new lib (move (d), move (n), e));
-
- if ((l->a = a))
- a->group = l;
-
- if ((l->so = so))
- so->group = l;
-
- return l;
- }
-
- const target_type lib::static_type
- {
- "lib",
- &target::static_type,
- &lib_factory,
- nullptr,
- &search_target,
- false
- };
- }
-}
diff --git a/build/buildfile b/build/buildfile
deleted file mode 100644
index 936524e..0000000
--- a/build/buildfile
+++ /dev/null
@@ -1,64 +0,0 @@
-# file : build/buildfile
-# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-# license : MIT; see accompanying LICENSE file
-
-import libs = libbutl%lib{butl}
-
-exe{b}: \
- {hxx ixx txx cxx}{ algorithm } \
- { cxx}{ b } \
- {hxx txx cxx}{ context } \
- {hxx cxx}{ diagnostics } \
- {hxx cxx}{ dump } \
- {hxx ixx cxx}{ file } \
- {hxx cxx}{ lexer } \
- {hxx cxx}{ module } \
- {hxx cxx}{ name } \
- {hxx cxx}{ operation } \
- {hxx ixx cxx}{ options } \
- {hxx cxx}{ parser } \
- {hxx cxx}{ path-io } \
- {hxx cxx}{ prerequisite } \
- {hxx cxx}{ rule } \
- {hxx }{ rule-map } \
- {hxx cxx}{ scope } \
- {hxx cxx}{ search } \
- {hxx cxx}{ spec } \
- {hxx ixx txx cxx}{ target } \
- {hxx }{ target-key } \
- {hxx }{ target-type } \
- {hxx cxx}{ token } \
- {hxx }{ types } \
- {hxx cxx}{ utility } \
- {hxx ixx txx cxx}{ variable } \
- {hxx }{ version } \
- bin/{hxx cxx}{ module } \
- bin/{hxx cxx}{ rule } \
- bin/{hxx cxx}{ target } \
- cli/{hxx cxx}{ module } \
- cli/{hxx cxx}{ rule } \
- cli/{hxx cxx}{ target } \
- config/{hxx cxx}{ module } \
- config/{hxx cxx}{ operation } \
- config/{hxx ixx txx cxx}{ utility } \
- cxx/{hxx cxx}{ compile } \
- cxx/{hxx cxx}{ install } \
- cxx/{hxx cxx}{ link } \
- cxx/{hxx cxx}{ module } \
- cxx/{hxx cxx}{ target } \
- cxx/{hxx txx cxx}{ utility } \
- dist/{hxx cxx}{ module } \
- dist/{hxx cxx}{ operation } \
- dist/{hxx cxx}{ rule } \
-install/{hxx cxx}{ module } \
-install/{hxx cxx}{ operation } \
-install/{hxx cxx}{ rule } \
-install/{hxx }{ utility } \
- test/{hxx cxx}{ module } \
- test/{hxx cxx}{ operation } \
- test/{hxx cxx}{ rule } \
-$libs
-
-#@@ TODO
-#
-# cli --generate-specifier --cli-namespace cl --include-with-brackets --include-prefix build --guard-prefix BUILD --hxx-suffix "" options.cli
diff --git a/build/cli/module b/build/cli/module
deleted file mode 100644
index 221e6a0..0000000
--- a/build/cli/module
+++ /dev/null
@@ -1,23 +0,0 @@
-// file : build/cli/module -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CLI_MODULE
-#define BUILD_CLI_MODULE
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/module>
-
-namespace build
-{
- namespace cli
- {
- extern "C" bool
- cli_init (
- scope&, scope&, const location&, unique_ptr<module>&, bool, bool);
- }
-}
-
-#endif // BUILD_CLI_MODULE
diff --git a/build/cli/module.cxx b/build/cli/module.cxx
deleted file mode 100644
index de7d576..0000000
--- a/build/cli/module.cxx
+++ /dev/null
@@ -1,244 +0,0 @@
-// file : build/cli/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cli/module>
-
-#include <butl/process>
-#include <butl/fdstream>
-
-#include <build/scope>
-#include <build/target>
-#include <build/variable>
-#include <build/diagnostics>
-
-#include <build/cxx/target>
-
-#include <build/config/utility>
-
-#include <build/cli/target>
-#include <build/cli/rule>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace cli
- {
- static compile compile_;
-
- extern "C" bool
- cli_init (scope& root,
- scope& base,
- const location& loc,
- std::unique_ptr<module>&,
- bool first,
- bool optional)
- {
- tracer trace ("cli::init");
- level5 ([&]{trace << "for " << base.out_path ();});
-
- // Make sure the cxx module has been loaded since we need its
- // targets types (?xx{}). Note that we don't try to load it
- // ourselves because of the non-trivial variable merging
- // semantics. So it is better to let the user load cxx
- // explicitly.
- //
- {
- auto l (base["cxx.loaded"]);
-
- if (!l || !as<bool> (*l))
- fail (loc) << "cxx module must be loaded before cli";
- }
-
- // Enter module variables.
- //
- if (first)
- {
- auto& v (var_pool);
-
- v.find ("config.cli.configured", bool_type);
-
- v.find ("config.cli", string_type); //@@ VAR type
-
- v.find ("config.cli.options", strings_type);
- v.find ("cli.options", strings_type);
- }
-
- // Register target types.
- //
- {
- auto& t (base.target_types);
-
- t.insert<cli> ();
- t.insert<cli_cxx> ();
- }
-
- // Configure.
- //
- // The plan is as follows: try to configure the module. If this fails,
- // we are using default values, and the module is optional, leave it
- // unconfigured.
- //
-
- // We will only honor optional if the user didn't specify any cli
- // configuration explicitly.
- //
- optional = optional && !config::specified (root, "config.cli");
-
- // Don't re-run tests if the configuration says we are unconfigured.
- //
- if (optional)
- {
- auto l (root["config.cli.configured"]);
-
- if (l && !as<bool> (*l))
- return false;
- }
-
- // config.cli
- //
- if (first)
- {
- // Return version or empty string if unable to execute (e.g.,
- // the cli executable is not found).
- //
- auto test = [optional] (const char* cli) -> string
- {
- const char* args[] = {cli, "--version", nullptr};
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "test " << cli;
-
- string ver;
- try
- {
- process pr (args, 0, -1); // Open pipe to stdout.
- ifdstream is (pr.in_ofd);
-
- // The version should be the last word on the first line.
- //
- getline (is, ver);
- auto p (ver.rfind (' '));
- if (p != string::npos)
- ver = string (ver, p + 1);
-
- is.close (); // Don't block the other end.
-
- if (!pr.wait ())
- return string (); // Not found.
-
- if (ver.empty ())
- fail << "unexpected output from " << cli;
-
- return ver;
- }
- catch (const process_error& e)
- {
- // In some cases this is not enough (e.g., the runtime linker
- // will print scary errors if some shared libraries are not
- // found. So it would be good to redirect child's STDERR.
- //
- if (!optional)
- error << "unable to execute " << cli << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- };
-
- string ver;
- const char* cli ("cli"); // Default.
-
- if (optional)
- {
- // Test the default value before setting any config.cli.* values
- // so that if we fail to configure, nothing will be written to
- // config.build.
- //
- ver = test (cli);
-
- if (ver.empty ())
- {
- // Note that we are unconfigured so that we don't keep re-testing
- // this on each run.
- //
- root.assign ("config.cli.configured") = false;
-
- if (verb >= 2)
- text << cli << " not found, leaving cli module unconfigured";
-
- return false;
- }
- else
- {
- auto p (config::required (root, "config.cli", cli));
- assert (p.second && as<string> (p.first) == cli);
- }
- }
- else
- {
- auto p (config::required (root, "config.cli", cli));
-
- // If we actually set a new value, test it by trying to execute.
- //
- if (p.second)
- {
- cli = as<string> (p.first).c_str ();
- ver = test (cli);
-
- if (ver.empty ())
- throw failed ();
- }
- }
-
- // Clear the unconfigured flag, if any.
- //
- root.assign ("config.cli.configured") = true;
-
- if (!ver.empty () && verb >= 2)
- text << cli << " " << ver;
- }
-
- // config.cli.options
- //
- // This one is optional. We also merge it into the corresponding
- // cli.* variables. See the cxx module for more information on
- // this merging semantics and some of its tricky aspects.
- //
- if (const value& v = config::optional (root, "config.cli.options"))
- base.assign ("cli.options") += as<strings> (v);
-
- // Register our rules.
- //
- {
- auto& r (base.rules);
-
- r.insert<cli_cxx> (perform_update_id, "cli.compile", compile_);
- r.insert<cli_cxx> (perform_clean_id, "cli.compile", compile_);
-
- r.insert<cxx::hxx> (perform_update_id, "cli.compile", compile_);
- r.insert<cxx::hxx> (perform_clean_id, "cli.compile", compile_);
-
- r.insert<cxx::cxx> (perform_update_id, "cli.compile", compile_);
- r.insert<cxx::cxx> (perform_clean_id, "cli.compile", compile_);
-
- r.insert<cxx::ixx> (perform_update_id, "cli.compile", compile_);
- r.insert<cxx::ixx> (perform_clean_id, "cli.compile", compile_);
-
- // Other rules (e.g., cxx::compile) may need to have the group
- // members resolved. Looks like a general pattern: groups should
- // resolve on configure(update).
- //
- r.insert<cli_cxx> (configure_update_id, "cli.compile", compile_);
- }
-
- return true;
- }
- }
-}
diff --git a/build/cli/rule b/build/cli/rule
deleted file mode 100644
index 21fcf8a..0000000
--- a/build/cli/rule
+++ /dev/null
@@ -1,32 +0,0 @@
-// file : build/cli/rule -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CLI_RULE
-#define BUILD_CLI_RULE
-
-#include <build/rule>
-
-namespace build
-{
- namespace cli
- {
- class compile: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_update (action, target&);
-
- static target_state
- perform_clean (action, target&);
- };
- }
-}
-
-#endif // BUILD_CLI_RULE
diff --git a/build/cli/rule.cxx b/build/cli/rule.cxx
deleted file mode 100644
index 274f193..0000000
--- a/build/cli/rule.cxx
+++ /dev/null
@@ -1,305 +0,0 @@
-// file : build/cli/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cli/rule>
-
-#include <butl/process>
-
-#include <build/types>
-#include <build/scope>
-#include <build/target>
-#include <build/context>
-#include <build/algorithm>
-#include <build/diagnostics>
-
-#include <build/cli/target>
-
-#include <build/config/utility>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace cli
- {
- match_result compile::
- match (action a, target& xt, const std::string&) const
- {
- tracer trace ("cli::compile::match");
-
- if (cli_cxx* pt = xt.is_a<cli_cxx> ())
- {
- // The cli.cxx{} group.
- //
- cli_cxx& t (*pt);
-
- // See if we have a .cli source file.
- //
- match_result r;
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- if (p.is_a<cli> ())
- {
- // Check that the stems match.
- //
- if (t.name != p.name ())
- {
- level4 ([&]{trace << ".cli file stem '" << p.name () << "' "
- << "doesn't match target " << t;});
- return r;
- }
-
- r = p;
- break;
- }
- }
-
- if (!r)
- {
- level4 ([&]{trace << "no .cli source file for target " << t;});
- return r;
- }
-
- // If we still haven't figured out the member list, we can do
- // that now. Specifically, at this stage, no further changes to
- // cli.options are possible and we can determine whether the
- // --suppress-inline option is present.
- //
- if (t.h == nullptr)
- {
- t.h = &search<cxx::hxx> (t.dir, t.name, nullptr, nullptr);
- t.h->group = &t;
-
- t.c = &search<cxx::cxx> (t.dir, t.name, nullptr, nullptr);
- t.c->group = &t;
-
- if (!config::find_option ("--suppress-inline", t, "cli.options"))
- {
- t.i = &search<cxx::ixx> (t.dir, t.name, nullptr, nullptr);
- t.i->group = &t;
- }
- }
-
- return r;
- }
- else
- {
- // One of the ?xx{} members.
- //
- target& t (xt);
-
- // First see if we are already linked-up to the cli.cxx{} group.
- // If it is some other group, then we are definitely not a match.
- //
- if (t.group != nullptr)
- return t.group->is_a<cli_cxx> ();
-
- // Then check if there is a corresponding cli.cxx{} group.
- //
- cli_cxx* g (targets.find<cli_cxx> (t.dir, t.name));
-
- // If not or if it has no prerequisites (happens when we use it to
- // set cli.options) and this target has a cli{} prerequisite, then
- // synthesize the group.
- //
- if (g == nullptr || !g->has_prerequisites ())
- {
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- if (p.is_a<cli> ())
- {
- // Check that the stems match.
- //
- if (t.name == p.name ())
- {
- if (g == nullptr)
- g = &targets.insert<cli_cxx> (t.dir, t.name, trace);
-
- g->prerequisites.emplace_back (p.as_prerequisite (trace));
- }
- else
- level4 ([&]{trace << ".cli file stem '" << p.name () << "' "
- << "doesn't match target " << t;});
- break;
- }
- }
- }
-
- if (g != nullptr)
- {
- // Resolve the group's members. This should link us up to
- // the group.
- //
- resolve_group_members (a, *g);
-
- // For ixx{}, verify it is part of the group.
- //
- if (t.is_a<cxx::ixx> () && g->i == nullptr)
- {
- level4 ([&]{trace << "generation of inline file " << t
- << " is disabled with --suppress-inline";});
- g = nullptr;
- }
- }
-
- assert (t.group == g);
- return g;
- }
- }
-
- recipe compile::
- apply (action a, target& xt, const match_result& mr) const
- {
- if (cli_cxx* pt = xt.is_a<cli_cxx> ())
- {
- cli_cxx& t (*pt);
-
- // Derive file names for the members.
- //
- t.h->derive_path ();
- t.c->derive_path ();
- if (t.i != nullptr)
- t.i->derive_path ();
-
- // Inject dependency on the output directory.
- //
- inject_parent_fsdir (a, t);
-
- // Search and match prerequisite members.
- //
- search_and_match_prerequisite_members (a, t);
-
- switch (a)
- {
- case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean;
- default: return noop_recipe; // Configure update.
- }
- }
- else
- {
- cli_cxx& g (*static_cast<cli_cxx*> (mr.target));
- build::match (a, g);
- return group_recipe; // Execute the group's recipe.
- }
- }
-
- static void
- append_extension (cstrings& args,
- path_target& t,
- const char* option,
- const char* default_extension)
- {
- assert (t.ext != nullptr); // Should have been figured out in apply().
-
- if (*t.ext != default_extension)
- {
- // CLI needs the extension with the leading dot (unless it is empty)
- // while we store the extension without. But if there is an extension,
- // then we can get it (with the dot) from the file name.
- //
- args.push_back (option);
- args.push_back (t.ext->empty ()
- ? t.ext->c_str ()
- : t.path ().extension () - 1);
- }
- }
-
- target_state compile::
- perform_update (action a, target& xt)
- {
- cli_cxx& t (static_cast<cli_cxx&> (xt));
-
- // Execute our prerequsites and check if we are out of date.
- //
- cli* s (execute_prerequisites<cli> (a, t, t.mtime ()));
-
- if (s == nullptr)
- return target_state::unchanged;
-
- // Translate paths to relative (to working directory). This
- // results in easier to read diagnostics.
- //
- path relo (relative (t.dir));
- path rels (relative (s->path ()));
-
- scope& rs (t.root_scope ());
- const string& cli (as<string> (*rs["config.cli"]));
-
- cstrings args {cli.c_str ()};
-
- // See if we need to pass any --?xx-suffix options.
- //
- append_extension (args, *t.h, "--hxx-suffix", "hxx");
- append_extension (args, *t.c, "--cxx-suffix", "cxx");
- if (t.i != nullptr)
- append_extension (args, *t.i, "--ixx-suffix", "ixx");
-
- config::append_options (args, t, "cli.options");
-
- if (!relo.empty ())
- {
- args.push_back ("-o");
- args.push_back (relo.string ().c_str ());
- }
-
- args.push_back (rels.string ().c_str ());
- args.push_back (nullptr);
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "cli " << *s;
-
- try
- {
- process pr (args.data ());
-
- if (!pr.wait ())
- throw failed ();
-
- t.mtime (system_clock::now ());
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
-
- return target_state::changed;
- }
-
- target_state compile::
- perform_clean (action a, target& xt)
- {
- cli_cxx& t (static_cast<cli_cxx&> (xt));
-
- // The reverse order of update: first delete the files, then clean
- // prerequisites. Also update timestamp in case there are operations
- // after us that could use the information.
- //
- bool r (false);
-
- if (t.i != nullptr)
- r = rmfile (t.i->path (), *t.i) || r;
- r = rmfile (t.c->path (), *t.c) || r;
- r = rmfile (t.h->path (), *t.h) || r;
-
- t.mtime (timestamp_nonexistent);
-
- target_state ts (r ? target_state::changed : target_state::unchanged);
-
- // Clean prerequisites.
- //
- ts |= reverse_execute_prerequisites (a, t);
-
- return ts;
- }
- }
-}
diff --git a/build/cli/target b/build/cli/target
deleted file mode 100644
index dbb05bd..0000000
--- a/build/cli/target
+++ /dev/null
@@ -1,62 +0,0 @@
-// file : build/cli/target -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CLI_TARGET
-#define BUILD_CLI_TARGET
-
-#include <build/target>
-
-#include <build/cxx/target>
-
-namespace build
-{
- namespace cli
- {
- class cli: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class cli_cxx: public mtime_target
- {
- public:
- using mtime_target::mtime_target;
-
- union
- {
- // It is theoretically possible that the compiler will add
- // padding between the members of this struct. This would
- // mean that the optimal alignment for a pointer is greater
- // than its size (and that an array of pointers is sub-
- // optimally aligned). We will deal with such a beast of
- // an architecture when we see it.
- //
- struct
- {
- cxx::hxx* h;
- cxx::cxx* c;
- cxx::ixx* i;
- };
- target* m[3] = {nullptr, nullptr, nullptr};
- };
-
- virtual group_view
- group_members (action_type) const;
-
- virtual timestamp
- load_mtime () const;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
- }
-}
-
-#endif // BUILD_CLI_TARGET
diff --git a/build/cli/target.cxx b/build/cli/target.cxx
deleted file mode 100644
index 6eef99b..0000000
--- a/build/cli/target.cxx
+++ /dev/null
@@ -1,77 +0,0 @@
-// file : build/cli/target.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cli/target>
-
-#include <butl/filesystem>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace cli
- {
- // cli
- //
- constexpr const char cli_ext_var[] = "extension";
- constexpr const char cli_ext_def[] = "cli";
-
- const target_type cli::static_type
- {
- "cli",
- &file::static_type,
- &target_factory<cli>,
- &target_extension_var<cli_ext_var, cli_ext_def>,
- &search_file,
- false
- };
-
- // cli.cxx
- //
- group_view cli_cxx::
- group_members (action_type) const
- {
- return h != nullptr
- ? group_view {m, (i != nullptr ? 3U : 2U)}
- : group_view {nullptr, 0};
- }
-
- timestamp cli_cxx::
- load_mtime () const
- {
- // The rule has been matched which means the members should
- // be resolved and paths assigned.
- //
- return file_mtime (h->path ());
- }
-
- static target*
- cli_cxx_factory (const target_type&, dir_path d, string n, const string* e)
- {
- tracer trace ("cli::cli_cxx_factory");
-
- // Pre-enter (potential) members as targets. The main purpose
- // of doing this is to avoid searching for existing files in
- // src_base if the buildfile mentions some of them explicitly
- // as prerequisites.
- //
- targets.insert<cxx::hxx> (d, n, trace);
- targets.insert<cxx::cxx> (d, n, trace);
- targets.insert<cxx::ixx> (d, n, trace);
-
- return new cli_cxx (move (d), move (n), e);
- }
-
- const target_type cli_cxx::static_type
- {
- "cli.cxx",
- &mtime_target::static_type,
- &cli_cxx_factory,
- nullptr,
- &search_target,
- true // "See through" default iteration mode.
- };
- }
-}
diff --git a/build/config/module b/build/config/module
deleted file mode 100644
index 3e43c7b..0000000
--- a/build/config/module
+++ /dev/null
@@ -1,26 +0,0 @@
-// file : build/config/module -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CONFIG_MODULE
-#define BUILD_CONFIG_MODULE
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/module>
-
-namespace build
-{
- namespace config
- {
- extern "C" void
- config_boot (scope&, const location&, unique_ptr<module>&);
-
- extern "C" bool
- config_init (
- scope&, scope&, const location&, unique_ptr<module>&, bool, bool);
- }
-}
-
-#endif // BUILD_CONFIG_MODULE
diff --git a/build/config/module.cxx b/build/config/module.cxx
deleted file mode 100644
index c9139ed..0000000
--- a/build/config/module.cxx
+++ /dev/null
@@ -1,90 +0,0 @@
-// file : build/config/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/config/module>
-
-#include <butl/filesystem>
-
-#include <build/file>
-#include <build/rule>
-#include <build/scope>
-#include <build/diagnostics>
-
-#include <build/config/operation>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace config
- {
- //@@ Same as in operation.cxx
- //
- static const path config_file ("build/config.build");
-
- extern "C" void
- config_boot (scope& root, const location&, unique_ptr<module>&)
- {
- tracer trace ("config::boot");
-
- const dir_path& out_root (root.out_path ());
- level5 ([&]{trace << "for " << out_root;});
-
- // Register meta-operations.
- //
- root.meta_operations.insert (configure_id, configure);
- root.meta_operations.insert (disfigure_id, disfigure);
-
- // Load config.build if one exists.
- //
- // Note that we have to do this during bootstrap since the order in
- // which the modules will be initialized is unspecified. So it is
- // possible that some module which needs the configuration will get
- // called first.
- //
- path f (out_root / config_file);
-
- if (file_exists (f))
- source (f, root, root);
- }
-
- extern "C" bool
- config_init (scope& root,
- scope&,
- const location& l,
- std::unique_ptr<module>&,
- bool first,
- bool)
- {
- tracer trace ("config::init");
-
- if (!first)
- {
- warn (l) << "multiple config module initializations";
- return true;
- }
-
- level5 ([&]{trace << "for " << root.out_path ();});
-
- // Register alias and fallback rule for the configure meta-operation.
- //
- {
- // We need this rule for out-of-any-project dependencies (e.g.,
- // libraries imported from /usr/lib).
- //
- global_scope->rules.insert<file> (
- configure_id, 0, "config.file", file_rule::instance);
-
- auto& r (root.rules);
-
- r.insert<target> (configure_id, 0, "config", fallback_rule::instance);
- r.insert<file> (configure_id, 0, "config.file", fallback_rule::instance);
- r.insert<alias> (configure_id, 0, "config.alias", alias_rule::instance);
- }
-
- return true;
- }
- }
-}
diff --git a/build/config/operation b/build/config/operation
deleted file mode 100644
index ee5161d..0000000
--- a/build/config/operation
+++ /dev/null
@@ -1,19 +0,0 @@
-// file : build/config/operation -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CONFIG_OPERATION
-#define BUILD_CONFIG_OPERATION
-
-#include <build/operation>
-
-namespace build
-{
- namespace config
- {
- extern meta_operation_info configure;
- extern meta_operation_info disfigure;
- }
-}
-
-#endif // BUILD_CONFIG_OPERATION
diff --git a/build/config/operation.cxx b/build/config/operation.cxx
deleted file mode 100644
index abe4e22..0000000
--- a/build/config/operation.cxx
+++ /dev/null
@@ -1,455 +0,0 @@
-// file : build/config/operation.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/config/operation>
-
-#include <fstream>
-
-#include <butl/filesystem>
-
-#include <build/file>
-#include <build/scope>
-#include <build/target>
-#include <build/context>
-#include <build/algorithm>
-#include <build/diagnostics>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace config
- {
- static const path config_file ("build/config.build");
-
- // configure
- //
- static operation_id
- configure_operation_pre (operation_id o)
- {
- // Don't translate default to update. In our case unspecified
- // means configure everything.
- //
- return o;
- }
-
- static void
- save_src_root (const dir_path& out_root, const dir_path& src_root)
- {
- path f (out_root / src_root_file);
-
- if (verb)
- text << (verb >= 2 ? "config::save_src_root " : "save ") << f;
-
- try
- {
- ofstream ofs (f.string ());
- if (!ofs.is_open ())
- fail << "unable to open " << f;
-
- ofs.exceptions (ofstream::failbit | ofstream::badbit);
-
- //@@ TODO: quote path
- //
- ofs << "# Created automatically by the config module." << endl
- << "#" << endl
- << "src_root = " << src_root << endl;
- }
- catch (const ofstream::failure&)
- {
- fail << "unable to write " << f;
- }
- }
-
- static void
- save_config (scope& root)
- {
- const dir_path& out_root (root.out_path ());
- path f (out_root / config_file);
-
- if (verb)
- text << (verb >= 2 ? "config::save_config " : "save ") << f;
-
- try
- {
- ofstream ofs (f.string ());
- if (!ofs.is_open ())
- fail << "unable to open " << f;
-
- ofs.exceptions (ofstream::failbit | ofstream::badbit);
-
- ofs << "# Created automatically by the config module, but" << endl
- << "# feel free to edit." << endl
- << "#" << endl;
-
- if (auto l = root.vars["amalgamation"])
- {
- const dir_path& d (as<dir_path> (*l));
-
- ofs << "# Base configuration inherited from " << d << endl
- << "#" << endl;
- }
-
- // Save all the variables in the config namespace that are set
- // on the project's root scope.
- //
- for (auto p (root.vars.find_namespace ("config"));
- p.first != p.second;
- ++p.first)
- {
- const variable& var (p.first->first);
- const value& val (p.first->second);
- const string& n (var.name);
-
- // Skip special variables.
- //
- if (n == "config.loaded" ||
- n == "config.configured")
- continue;
-
- // We will only write config.*.configured if it is false
- // (true is implied by its absence).
- //
- if (n.size () > 11 &&
- n.compare (n.size () - 11, 11, ".configured") == 0)
- {
- if (val == nullptr || as<bool> (val))
- continue;
- }
-
- // Warn the user if the value that we are saving differs
- // from the one they specified on the command line.
- //
- auto l ((*global_scope)[var]);
- if (l.defined () && *l != val)
- {
- warn << "variable " << var.name << " configured value "
- << "differs from command line value" <<
- info << "reconfigure the project to use command line value";
- }
-
- if (val)
- {
- ofs << var.name << " = " << val.data_ << endl;
- //text << var.name << " = " << val.data_;
- }
- else
- {
- ofs << var.name << " = #[null]" << endl; // @@ TODO: [null]
- //text << var.name << " = [null]";
- }
- }
- }
- catch (const ofstream::failure&)
- {
- fail << "unable to write " << f;
- }
- }
-
- static void
- configure_project (action a, scope& root)
- {
- tracer trace ("configure_project");
-
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- // Make sure the directories exist.
- //
- if (out_root != src_root)
- {
- mkdir_p (out_root);
- mkdir (out_root / build_dir);
- mkdir (out_root / bootstrap_dir);
- }
-
- // We distinguish between a complete configure and operation-
- // specific.
- //
- if (a.operation () == default_id)
- {
- level5 ([&]{trace << "completely configuring " << out_root;});
-
- // Save src-root.build unless out_root is the same as src.
- //
- if (out_root != src_root)
- save_src_root (out_root, src_root);
-
- // Save config.build.
- //
- save_config (root);
- }
- else
- {
- }
-
- // Configure subprojects that have been loaded.
- //
- if (auto l = root.vars["subprojects"])
- {
- for (auto p: as<subprojects> (*l))
- {
- const dir_path& pd (p.second);
- dir_path out_nroot (out_root / pd);
- scope& nroot (scopes.find (out_nroot));
-
- // @@ Strictly speaking we need to check whether the config
- // module was loaded for this subproject.
- //
- if (nroot.out_path () != out_nroot) // This subproject not loaded.
- continue;
-
- configure_project (a, nroot);
- }
- }
- }
-
- static void
- configure_match (action, action_targets&)
- {
- // Don't match anything -- see execute ().
- }
-
- static void
- configure_execute (action a, const action_targets& ts, bool)
- {
- // Match rules to configure every operation supported by each
- // project. Note that we are not calling operation_pre/post()
- // callbacks here since the meta operation is configure and we
- // know what we are doing.
- //
- for (void* v: ts)
- {
- target& t (*static_cast<target*> (v));
- scope* rs (t.base_scope ().root_scope ());
-
- if (rs == nullptr)
- fail << "out of project target " << t;
-
- for (operations::size_type id (default_id + 1); // Skip default_id
- id < rs->operations.size ();
- ++id)
- {
- const operation_info* oi (rs->operations[id]);
- if (oi == nullptr)
- continue;
-
- current_inner_oif = oi;
- current_outer_oif = nullptr;
- current_mode = oi->mode;
- dependency_count = 0;
-
- match (action (configure_id, id), t);
- }
-
- configure_project (a, *rs);
- }
- }
-
- meta_operation_info configure {
- "configure",
- "configure",
- "configuring",
- "is configured",
- nullptr, // meta-operation pre
- &configure_operation_pre,
- &load, // normal load
- &search, // normal search
- &configure_match,
- &configure_execute,
- nullptr, // operation post
- nullptr // meta-operation post
- };
-
- // disfigure
- //
- static operation_id
- disfigure_operation_pre (operation_id o)
- {
- // Don't translate default to update. In our case unspecified
- // means disfigure everything.
- //
- return o;
- }
-
- static void
- disfigure_load (const path& bf,
- scope&,
- const dir_path&,
- const dir_path&,
- const location&)
- {
- tracer trace ("disfigure_load");
- level6 ([&]{trace << "skipping " << bf;});
- }
-
- static void
- disfigure_search (scope& root,
- const target_key&,
- const location&,
- action_targets& ts)
- {
- tracer trace ("disfigure_search");
- level6 ([&]{trace << "collecting " << root.out_path ();});
- ts.push_back (&root);
- }
-
- static void
- disfigure_match (action, action_targets&) {}
-
- static bool
- disfigure_project (action a, scope& root)
- {
- tracer trace ("disfigure_project");
-
- bool m (false); // Keep track of whether we actually did anything.
-
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- // Disfigure subprojects. Since we don't load buildfiles during
- // disfigure, we do it for all known subprojects.
- //
- if (auto l = root.vars["subprojects"])
- {
- for (auto p: as<subprojects> (*l))
- {
- const dir_path& pd (p.second);
-
- // Create and bootstrap subproject's root scope.
- //
- dir_path out_nroot (out_root / pd);
-
- // The same logic for src_root as in create_bootstrap_inner().
- //
- scope& nroot (create_root (out_nroot, dir_path ()));
- bootstrap_out (nroot);
-
- value& val (nroot.assign ("src_root"));
-
- if (!val)
- val = is_src_root (out_nroot) ? out_nroot : (src_root / pd);
-
- setup_root (nroot);
-
- bootstrap_src (nroot);
-
- m = disfigure_project (a, nroot) || m;
-
- // We use mkdir_p() to create the out_root of a subproject
- // which means there could be empty parent directories left
- // behind. Clean them up.
- //
- if (!pd.simple () && out_root != src_root)
- {
- for (dir_path d (pd.directory ());
- !d.empty ();
- d = d.directory ())
- {
- rmdir_status s (rmdir (out_root / d));
-
- if (s == rmdir_status::not_empty)
- break; // No use trying do remove parent ones.
-
- m = (s == rmdir_status::success) || m;
- }
- }
- }
- }
-
- // We distinguish between a complete disfigure and operation-
- // specific.
- //
- if (a.operation () == default_id)
- {
- level5 ([&]{trace << "completely disfiguring " << out_root;});
-
- m = rmfile (out_root / config_file) || m;
-
- if (out_root != src_root)
- {
- m = rmfile (out_root / src_root_file) || m;
-
- // Clean up the directories.
- //
- m = rmdir (out_root / bootstrap_dir) || m;
- m = rmdir (out_root / build_dir) || m;
-
- switch (rmdir (out_root))
- {
- case rmdir_status::not_empty:
- {
- warn << "directory " << out_root << " is "
- << (out_root == work
- ? "current working directory"
- : "not empty") << ", not removing";
- break;
- }
- case rmdir_status::success:
- m = true;
- default:
- break;
- }
- }
- }
- else
- {
- }
-
- return m;
- }
-
- static void
- disfigure_execute (action a, const action_targets& ts, bool quiet)
- {
- tracer trace ("disfigure_execute");
-
- for (void* v: ts)
- {
- scope& root (*static_cast<scope*> (v));
-
- if (!disfigure_project (a, root))
- {
- // Create a dir{$out_root/} target to signify the project's
- // root in diagnostics. Not very clean but seems harmless.
- //
- target& t (
- targets.insert (
- dir::static_type, root.out_path (), "", nullptr, trace).first);
-
- if (!quiet)
- info << diag_done (a, t);
- }
- }
- }
-
- static void
- disfigure_meta_operation_post ()
- {
- tracer trace ("disfigure_meta_operation_post");
-
- // Reset the dependency state since anything that could have been
- // loaded earlier using a previous configuration is now invalid.
- //
- level6 ([&]{trace << "resetting dependency state";});
- reset ();
- }
-
- meta_operation_info disfigure {
- "disfigure",
- "disfigure",
- "disfiguring",
- "is disfigured",
- nullptr, // meta-operation pre
- &disfigure_operation_pre,
- &disfigure_load,
- &disfigure_search,
- &disfigure_match,
- &disfigure_execute,
- nullptr, // operation post
- &disfigure_meta_operation_post
- };
- }
-}
diff --git a/build/config/utility b/build/config/utility
deleted file mode 100644
index ee5f50b..0000000
--- a/build/config/utility
+++ /dev/null
@@ -1,128 +0,0 @@
-// file : build/config/utility -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CONFIG_UTILITY
-#define BUILD_CONFIG_UTILITY
-
-#include <string>
-#include <utility> // pair
-#include <functional> // reference_wrapper
-
-#include <build/types>
-#include <build/variable>
-#include <build/diagnostics>
-
-namespace build
-{
- class scope;
-
- namespace config
- {
- // Set, if necessary, a required config.* variable.
- //
- // If override is true and the variable doesn't come from this root
- // scope or from the command line, then its value is "overridden"
- // for this root scope.
- //
- // Return the reference to the value as well as the indication of
- // whether the variable has actually been set.
- //
- template <typename T>
- std::pair<std::reference_wrapper<const value>, bool>
- required (scope& root,
- const variable&,
- const T& default_value,
- bool override = false);
-
- template <typename T>
- inline std::pair<std::reference_wrapper<const value>, bool>
- required (scope& root,
- const std::string& name,
- const T& default_value,
- bool override = false)
- {
- return required (root, var_pool.find (name), default_value, override);
- }
-
- inline std::pair<std::reference_wrapper<const value>, bool>
- required (scope& root,
- const std::string& name,
- const char* default_value,
- bool override = false)
- {
- return required (root, name, std::string (default_value), override);
- }
-
- // Set, if necessary, an optional config.* variable. In particular,
- // an unspecified variable is set to NULL which is used to distinguish
- // between the "configured as unspecified" and "not yet configured"
- // cases.
- //
- // Return the value, which can be NULL.
- //
- const value&
- optional (scope& root, const variable&);
-
- inline const value&
- optional (scope& root, const std::string& var)
- {
- return optional (root, var_pool.find (var));
- }
-
- // As above but assumes the value is dir_path and makes it
- // absolute if the value specified on the command line is
- // relative.
- //
- const value&
- optional_absolute (scope& root, const variable&);
-
- inline const value&
- optional_absolute (scope& root, const std::string& var)
- {
- return optional_absolute (root, var_pool.find (var));
- }
-
- // Check whether there are any variables specified from the config
- // namespace. The idea is that we can check if there are any, say,
- // config.install.* values. If there are none, then we can assume
- // this functionality is not (yet) used and omit writing a whole
- // bunch of NULL config.install.* values to the config.build file.
- // We call it omitted/delayed configuration.
- //
- // Note that this function detects and ignores the special
- // config.*.configured variable which may be used by a module to
- // "remember" that it is unconfigured.
- //
- bool
- specified (scope& root, const std::string& ns);
-
- // @@ Why are these here?
- //
-
- // Add all the values from a variable to the C-string list. T is
- // either target or scope. The variable is expected to be of type
- // strings.
- //
- template <typename T>
- void
- append_options (cstrings& args, T& s, const char* var);
-
- // As above but from the strings value directly.
- //
- void
- append_options (cstrings& args, const const_strings_value&);
-
- // Check if a specified option is present in the variable value.
- // T is either target or scope.
- //
- template <typename T>
- bool
- find_option (const char* option, T& s, const char* var);
- }
-}
-
-#include <build/config/utility.txx>
-#include <build/config/utility.ixx>
-
-#endif // BUILD_CONFIG_UTILITY
diff --git a/build/config/utility.cxx b/build/config/utility.cxx
deleted file mode 100644
index b81cb04..0000000
--- a/build/config/utility.cxx
+++ /dev/null
@@ -1,92 +0,0 @@
-// file : build/config/utility.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/config/utility>
-
-#include <build/context>
-
-using namespace std;
-
-namespace build
-{
- namespace config
- {
- const value&
- optional (scope& root, const variable& var)
- {
- auto l (root[var]);
-
- return l.defined ()
- ? l.belongs (*global_scope) ? (root.assign (var) = *l) : *l
- : root.assign (var); // NULL
- }
-
- const value&
- optional_absolute (scope& root, const variable& var)
- {
- auto l (root[var]);
-
- if (!l.defined ())
- return root.assign (var); // NULL
-
- if (!l.belongs (*global_scope)) // Value from (some) root scope.
- return *l;
-
- // Make the command-line value absolute. This is necessary to avoid
- // a warning issued by the config module about global/root scope
- // value mismatch.
- //
- value& v (const_cast<value&> (*l));
-
- if (v && !v.empty ())
- {
- dir_path& d (as<dir_path> (v));
-
- if (d.relative ())
- {
- d = work / d;
- d.normalize ();
- }
- }
-
- return root.assign (var) = v;
- }
-
- bool
- specified (scope& r, const string& ns)
- {
- // Search all outer scopes for any value in this namespace.
- //
- for (scope* s (&r); s != nullptr; s = s->parent_scope ())
- {
- for (auto p (s->vars.find_namespace (ns));
- p.first != p.second;
- ++p.first)
- {
- const variable& var (p.first->first);
-
- // Ignore config.*.configured.
- //
- if (var.name.size () < 11 ||
- var.name.compare (var.name.size () - 11, 11, ".configured") != 0)
- return true;
- }
- }
-
- return false;
- }
-
- void
- append_options (cstrings& args, const const_strings_value& sv)
- {
- if (!sv.empty ())
- {
- args.reserve (args.size () + sv.size ());
-
- for (const string& s: sv)
- args.push_back (s.c_str ());
- }
- }
- }
-}
diff --git a/build/config/utility.ixx b/build/config/utility.ixx
deleted file mode 100644
index 4e32119..0000000
--- a/build/config/utility.ixx
+++ /dev/null
@@ -1,17 +0,0 @@
-// file : build/config/utility.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace build
-{
- namespace config
- {
- template <typename T>
- inline void
- append_options (cstrings& args, T& s, const char* var)
- {
- if (auto l = s[var])
- append_options (args, as<strings> (*l));
- }
- }
-}
diff --git a/build/config/utility.txx b/build/config/utility.txx
deleted file mode 100644
index 06cb2eb..0000000
--- a/build/config/utility.txx
+++ /dev/null
@@ -1,45 +0,0 @@
-// file : build/config/utility.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/scope>
-
-namespace build
-{
- namespace config
- {
- template <typename T>
- std::pair<std::reference_wrapper<const value>, bool>
- required (scope& root, const variable& var, const T& def_value, bool ovr)
- {
- using result = std::pair<std::reference_wrapper<const value>, bool>;
-
- if (auto l = root[var])
- {
- if (l.belongs (*global_scope))
- return result (root.assign (var) = *l, true);
-
- if (!ovr || l.belongs (root))
- return result (*l, false);
- }
-
- return result (root.assign (var) = def_value, true);
- }
-
- template <typename T>
- bool
- find_option (const char* option, T& s, const char* var)
- {
- if (auto l = s[var])
- {
- for (const std::string& s: as<strings> (*l))
- {
- if (s == option)
- return true;
- }
- }
-
- return false;
- }
- }
-}
diff --git a/build/context b/build/context
deleted file mode 100644
index b0ac6dd..0000000
--- a/build/context
+++ /dev/null
@@ -1,165 +0,0 @@
-// file : build/context -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CONTEXT
-#define BUILD_CONTEXT
-
-#include <string>
-#include <ostream>
-#include <cstdint> // uint64_t
-
-#include <butl/filesystem>
-
-#include <build/types>
-#include <build/utility>
-#include <build/operation>
-
-namespace build
-{
- class scope;
- class file;
-
- extern dir_path work;
- extern dir_path home;
-
- extern string_pool path_pool;
- extern string_pool extension_pool;
- extern string_pool project_name_pool;
-
- // Current action (meta/operation).
- //
- extern const meta_operation_info* current_mif;
- extern const operation_info* current_inner_oif;
- extern const operation_info* current_outer_oif;
-
- extern execution_mode current_mode;
-
- // Total number of dependency relationships in the current action.
- // Together with the target::dependents count it is incremented
- // during the rule search & match phase and is decremented during
- // execution with the expectation of it reaching 0. Used as a sanity
- // check.
- //
- extern std::uint64_t dependency_count;
-
- // Reset the dependency state. In particular, this removes all the
- // targets, scopes, and variable names.
- //
- void
- reset ();
-
- // The dual interface wrapper for the {mk,rm}{file,dir}() functions
- // below that allows you to use it as a true/false return or a more
- // detailed enum from <butl/filesystem>
- //
- template <typename T>
- struct fs_status
- {
- T v;
- fs_status (T v): v (v) {};
- operator T () const {return v;}
- explicit operator bool () const {return v == T::success;}
- };
-
- // Create the directory and print the standard diagnostics. Note that
- // this implementation is not suitable if it is expected that the
- // directory will exist in the majority of case and performance is
- // important. See the fsdir{} rule for details.
- //
- fs_status<butl::mkdir_status>
- mkdir (const dir_path&);
-
- fs_status<butl::mkdir_status>
- mkdir_p (const dir_path&);
-
- // Remove the file and print the standard diagnostics. The second
- // argument is only used in diagnostics, to print the target name.
- // Passing the path for target will result in the relative path
- // being printed.
- //
- template <typename T>
- fs_status<butl::rmfile_status>
- rmfile (const path&, const T& target);
-
- inline fs_status<butl::rmfile_status>
- rmfile (const path& f) {return rmfile (f, f);}
-
- // Similar to rmfile() but for directories.
- //
- template <typename T>
- fs_status<butl::rmdir_status>
- rmdir (const dir_path&, const T& target);
-
- inline fs_status<butl::rmdir_status>
- rmdir (const dir_path& d) {return rmdir (d, d);}
-
- // Note that this function returns not_empty if we try to remove
- // a working directory.
- //
- fs_status<butl::rmdir_status>
- rmdir_r (const dir_path&);
-
- // Return the src/out directory corresponding to the given out/src. The
- // passed directory should be a sub-directory of out/src_root.
- //
- dir_path
- src_out (const dir_path& out, scope&);
-
- dir_path
- src_out (const dir_path& out,
- const dir_path& out_root, const dir_path& src_root);
-
- dir_path
- out_src (const dir_path& src, scope&);
-
- dir_path
- out_src (const dir_path& src,
- const dir_path& out_root, const dir_path& src_root);
-
- // If possible and beneficial, translate an absolute, normalized path
- // into relative to the relative_base directory, which is normally
- // work. Note that if the passed path is the same as relative_base,
- // then this function returns empty path.
- //
- template <typename K>
- basic_path<char, K>
- relative (const basic_path<char, K>&);
-
- // By default this points to work. Setting this to something else
- // should only be done in tightly controlled, non-parallel
- // situations (see dump). If base is empty, then relative()
- // returns the original path.
- //
- extern const dir_path* relative_base;
-
- // In addition to calling relative(), this function also uses shorter
- // notations such as '~/'.
- //
- std::string
- diag_relative (const path&);
-
- // As above but also adds trailing '/'. If the path is the same as
- // base, returns "./" if current is true and empty string otherwise.
- //
- std::string
- diag_relative (const dir_path&, bool current = true);
-
- // Action phrases, e.g., "configure update exe{foo}", "updating exe{foo}",
- // and "updating exe{foo} is configured".
- //
- class target;
-
- std::string
- diag_do (const action&, const target&);
-
- std::string
- diag_doing (const action&, const target&);
-
- std::string
- diag_done (const action&, const target&);
-}
-
-#include <build/context.txx>
-
-#endif // BUILD_CONTEXT
diff --git a/build/context.cxx b/build/context.cxx
deleted file mode 100644
index a4815fb..0000000
--- a/build/context.cxx
+++ /dev/null
@@ -1,391 +0,0 @@
-// file : build/context.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/context>
-
-#include <ostream>
-#include <sstream>
-#include <cassert>
-#include <system_error>
-
-#include <build/scope>
-#include <build/target>
-#include <build/rule>
-#include <build/diagnostics>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- dir_path work;
- dir_path home;
-
- string_pool path_pool;
- string_pool extension_pool;
- string_pool project_name_pool;
-
- const meta_operation_info* current_mif;
- const operation_info* current_inner_oif;
- const operation_info* current_outer_oif;
- execution_mode current_mode;
- uint64_t dependency_count;
-
- void
- reset ()
- {
- path_pool.clear ();
- extension_pool.clear ();
- project_name_pool.clear ();
-
- targets.clear ();
- scopes.clear ();
- var_pool.clear ();
-
- // Reset meta/operation tables. Note that the order should match
- // the id constants in <build/operation>.
- //
- meta_operation_table.clear ();
- meta_operation_table.insert ("perform");
- meta_operation_table.insert ("configure");
- meta_operation_table.insert ("disfigure");
- meta_operation_table.insert ("dist");
-
- operation_table.clear ();
- operation_table.insert ("default");
- operation_table.insert ("update");
- operation_table.insert ("clean");
- operation_table.insert ("test");
- operation_table.insert ("install");
-
- // Enter builtin variables.
- //
- {
- auto& v (var_pool);
-
- v.find ("work", dir_path_type);
- v.find ("home", dir_path_type);
-
- v.find ("src_root", dir_path_type);
- v.find ("out_root", dir_path_type);
- v.find ("src_base", dir_path_type);
- v.find ("out_base", dir_path_type);
-
- v.find ("project", string_type);
- v.find ("amalgamation", dir_path_type);
-
- // Shouldn't be typed since the value requires pre-processing.
- //
- v.find ("subprojects", nullptr, '=');
-
- v.find ("extension", string_type);
- }
-
- // Create global scope. For Win32 this is not a "real" root path.
- // On POSIX, however, this is a real path. See the comment in
- // <build/path-map> for details.
- //
- global_scope = scopes.insert (
- dir_path ("/"), nullptr, true, false)->second;
-
- global_scope->assign ("work") = work;
- global_scope->assign ("home") = home;
-
- // Register builtin target types.
- //
- {
- target_type_map& t (global_scope->target_types);
-
- t.insert<file> ();
- t.insert<alias> ();
- t.insert<dir> ();
- t.insert<fsdir> ();
- t.insert<doc> ();
- t.insert<man> ();
- t.insert<man1> ();
- }
-
- // Register builtin rules.
- //
- {
- rule_map& r (global_scope->rules);
-
- r.insert<alias> (perform_id, 0, "alias", alias_rule::instance);
-
- r.insert<fsdir> (perform_update_id, "fsdir", fsdir_rule::instance);
- r.insert<fsdir> (perform_clean_id, "fsdir", fsdir_rule::instance);
-
- r.insert<file> (perform_update_id, "file", file_rule::instance);
- r.insert<file> (perform_clean_id, "file", file_rule::instance);
- }
- }
-
- fs_status<mkdir_status>
- mkdir (const dir_path& d)
- {
- // We don't want to print the command if the directory already
- // exists. This makes the below code a bit ugly.
- //
- mkdir_status ms;
-
- try
- {
- ms = try_mkdir (d);
- }
- catch (const system_error& e)
- {
- if (verb)
- text << "mkdir " << d;
-
- fail << "unable to create directory " << d << ": " << e.what ();
- }
-
- if (ms == mkdir_status::success)
- {
- if (verb)
- text << "mkdir " << d;
- }
-
- return ms;
- }
-
- fs_status<mkdir_status>
- mkdir_p (const dir_path& d)
- {
- // We don't want to print the command if the directory already
- // exists. This makes the below code a bit ugly.
- //
- mkdir_status ms;
-
- try
- {
- ms = try_mkdir_p (d);
- }
- catch (const system_error& e)
- {
- if (verb)
- text << "mkdir -p " << d;
-
- fail << "unable to create directory " << d << ": " << e.what ();
- }
-
- if (ms == mkdir_status::success)
- {
- if (verb)
- text << "mkdir -p " << d;
- }
-
- return ms;
- }
-
- fs_status<butl::rmdir_status>
- rmdir_r (const dir_path& d)
- {
- using namespace butl;
-
- if (work.sub (d)) // Don't try to remove working directory.
- return rmdir_status::not_empty;
-
- if (!dir_exists (d))
- return rmdir_status::not_exist;
-
- if (verb)
- text << "rmdir -r " << d;
-
- try
- {
- butl::rmdir_r (d);
- }
- catch (const std::system_error& e)
- {
- fail << "unable to remove directory " << d << ": " << e.what ();
- }
-
- return rmdir_status::success;
- }
-
- dir_path
- src_out (const dir_path& out, scope& s)
- {
- scope& rs (*s.root_scope ());
- return src_out (out, rs.out_path (), rs.src_path ());
- }
-
- dir_path
- out_src (const dir_path& src, scope& s)
- {
- scope& rs (*s.root_scope ());
- return out_src (src, rs.out_path (), rs.src_path ());
- }
-
- dir_path
- src_out (const dir_path& o,
- const dir_path& out_root, const dir_path& src_root)
- {
- assert (o.sub (out_root));
- return src_root / o.leaf (out_root);
- }
-
- dir_path
- out_src (const dir_path& s,
- const dir_path& out_root, const dir_path& src_root)
- {
- assert (s.sub (src_root));
- return out_root / s.leaf (src_root);
- }
-
- // relative()
- //
- const dir_path* relative_base = &work;
-
- string
- diag_relative (const path& p)
- {
- const path& b (*relative_base);
-
- if (p.absolute ())
- {
- if (p == b)
- return ".";
-
-#ifndef _WIN32
- if (p == home)
- return "~";
-#endif
-
- path rb (relative (p));
-
-#ifndef _WIN32
- if (rb.relative ())
- {
- // See if the original path with the ~/ shortcut is better
- // that the relative to base.
- //
- if (p.sub (home))
- {
- path rh (p.leaf (home));
- if (rb.string ().size () > rh.string ().size () + 2) // 2 for '~/'
- return "~/" + rh.string ();
- }
- }
- else if (rb.sub (home))
- return "~/" + rb.leaf (home).string ();
-#endif
-
- return rb.string ();
- }
-
- return p.string ();
- }
-
- string
- diag_relative (const dir_path& d, bool cur)
- {
- string r (diag_relative (static_cast<const path&> (d)));
-
- // Translate "." to empty.
- //
- if (!cur && d.absolute () && r == ".")
- r.clear ();
-
- // Add trailing '/'.
- //
- if (!r.empty () && !dir_path::traits::is_separator (r.back ()))
- r += '/';
-
- return r;
- }
-
- // diag_do(), etc.
- //
- string
- diag_do (const action&, const target& t)
- {
- const meta_operation_info& m (*current_mif);
- const operation_info& io (*current_inner_oif);
- const operation_info* oo (current_outer_oif);
-
- ostringstream os;
-
- // perform(update(x)) -> "update x"
- // configure(update(x)) -> "configure updating x"
- //
- if (m.name_do.empty ())
- os << io.name_do << ' ';
- else
- {
- os << m.name_do << ' ';
-
- if (!io.name_doing.empty ())
- os << io.name_doing << ' ';
- }
-
- if (oo != nullptr)
- os << "(for " << oo->name << ") ";
-
- os << t;
- return os.str ();
- }
-
- string
- diag_doing (const action&, const target& t)
- {
- const meta_operation_info& m (*current_mif);
- const operation_info& io (*current_inner_oif);
- const operation_info* oo (current_outer_oif);
-
- ostringstream os;
-
- // perform(update(x)) -> "updating x"
- // configure(update(x)) -> "configuring updating x"
- //
- if (!m.name_doing.empty ())
- os << m.name_doing << ' ';
-
- if (!io.name_doing.empty ())
- os << io.name_doing << ' ';
-
- if (oo != nullptr)
- os << "(for " << oo->name << ") ";
-
- os << t;
- return os.str ();
- }
-
- string
- diag_done (const action&, const target& t)
- {
- const meta_operation_info& m (*current_mif);
- const operation_info& io (*current_inner_oif);
- const operation_info* oo (current_outer_oif);
-
- ostringstream os;
-
- // perform(update(x)) -> "x is up to date"
- // configure(update(x)) -> "updating x is configured"
- //
- if (m.name_done.empty ())
- {
- os << t;
-
- if (!io.name_done.empty ())
- os << " " << io.name_done;
-
- if (oo != nullptr)
- os << "(for " << oo->name << ") ";
- }
- else
- {
- if (!io.name_doing.empty ())
- os << io.name_doing << ' ';
-
- if (oo != nullptr)
- os << "(for " << oo->name << ") ";
-
- os << t << " " << m.name_done;
- }
-
- return os.str ();
- }
-}
diff --git a/build/context.txx b/build/context.txx
deleted file mode 100644
index a0660f9..0000000
--- a/build/context.txx
+++ /dev/null
@@ -1,141 +0,0 @@
-// file : build/context.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <system_error>
-
-#include <build/diagnostics>
-
-namespace build
-{
- template <typename T>
- fs_status<butl::rmfile_status>
- rmfile (const path& f, const T& t)
- {
- using namespace butl;
-
- // We don't want to print the command if we couldn't remove the
- // file because it does not exist (just like we don't print the
- // update command if the file is up to date). This makes the
- // below code a bit ugly.
- //
- rmfile_status rs;
-
- try
- {
- rs = try_rmfile (f);
- }
- catch (const std::system_error& e)
- {
- if (verb >= 2)
- text << "rm " << f;
- else if (verb)
- text << "rm " << t;
-
- fail << "unable to remove file " << f << ": " << e.what ();
- }
-
- if (rs == rmfile_status::success)
- {
- if (verb >= 2)
- text << "rm " << f;
- else if (verb)
- text << "rm " << t;
- }
-
- return rs;
- }
-
- template <typename T>
- fs_status<butl::rmdir_status>
- rmdir (const dir_path& d, const T& t)
- {
- using namespace butl;
-
- bool w (work.sub (d)); // Don't try to remove working directory.
- rmdir_status rs;
-
- // We don't want to print the command if we couldn't remove the
- // directory because it does not exist (just like we don't print
- // mkdir if it already exists) or if it is not empty. This makes
- // the below code a bit ugly.
- //
- try
- {
- rs = !w ? try_rmdir (d) : rmdir_status::not_empty;
- }
- catch (const std::system_error& e)
- {
- if (verb >= 2)
- text << "rmdir " << d;
- else if (verb)
- text << "rmdir " << t;
-
- fail << "unable to remove directory " << d << ": " << e.what ();
- }
-
- switch (rs)
- {
- case rmdir_status::success:
- {
- if (verb >= 2)
- text << "rmdir " << d;
- else if (verb)
- text << "rmdir " << t;
-
- break;
- }
- case rmdir_status::not_empty:
- {
- if (verb >= 2)
- text << "directory " << d << " is "
- << (w ? "current working directory" : "not empty")
- << ", not removing";
-
- break;
- }
- case rmdir_status::not_exist:
- break;
- }
-
- return rs;
- }
-
- template <typename K>
- basic_path<char, K>
- relative (const basic_path<char, K>& p)
- {
- typedef basic_path<char, K> path;
-
- const dir_path& b (*relative_base);
-
- if (b.empty ())
- return p;
-
- if (p.sub (b))
- return p.leaf (b);
-
- // If base is a sub-path of {src,out}_root and this path is also a
- // sub-path of it, then use '..' to form a relative path.
- //
- // Don't think this is a good heuristic. For example, why shouldn't
- // we display paths from imported projects as relative if they are
- // more readable than absolute?
- //
- /*
- if ((work.sub (src_root) && p.sub (src_root)) ||
- (work.sub (out_root) && p.sub (out_root)))
- return p.relative (work);
- */
-
- if (p.root_directory () == b.root_directory ())
- {
- path r (p.relative (b));
-
- if (r.string ().size () < p.string ().size ())
- return r;
- }
-
- return p;
- }
-}
diff --git a/build/cxx/compile b/build/cxx/compile
deleted file mode 100644
index cb44829..0000000
--- a/build/cxx/compile
+++ /dev/null
@@ -1,32 +0,0 @@
-// file : build/cxx/compile -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CXX_COMPILE
-#define BUILD_CXX_COMPILE
-
-#include <build/types>
-#include <build/rule>
-
-namespace build
-{
- namespace cxx
- {
- class compile: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_update (action, target&);
-
- static compile instance;
- };
- }
-}
-
-#endif // BUILD_CXX_COMPILE
diff --git a/build/cxx/compile.cxx b/build/cxx/compile.cxx
deleted file mode 100644
index 2481b62..0000000
--- a/build/cxx/compile.cxx
+++ /dev/null
@@ -1,794 +0,0 @@
-// file : build/cxx/compile.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cxx/compile>
-
-#include <map>
-#include <string>
-#include <cstddef> // size_t
-#include <cstdlib> // exit()
-#include <utility> // move()
-
-#include <butl/process>
-#include <butl/utility> // reverse_iterate
-#include <butl/fdstream>
-#include <butl/path-map>
-
-#include <build/types>
-#include <build/scope>
-#include <build/variable>
-#include <build/algorithm>
-#include <build/diagnostics>
-#include <build/context>
-
-#include <build/bin/target>
-#include <build/cxx/target>
-
-#include <build/cxx/utility>
-#include <build/cxx/link>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace cxx
- {
- using namespace bin;
-
- match_result compile::
- match (action a, target& t, const string&) const
- {
- tracer trace ("cxx::compile::match");
-
- // @@ TODO:
- //
- // - check prerequisites: single source file
- // - if path already assigned, verify extension?
- //
-
- // See if we have a C++ source file. Iterate in reverse so that
- // a source file specified for an obj*{} member overrides the one
- // specified for the group. Also "see through" groups.
- //
- for (prerequisite_member p: reverse_group_prerequisite_members (a, t))
- {
- if (p.is_a<cxx> ())
- return p;
- }
-
- level4 ([&]{trace << "no c++ source file for target " << t;});
- return nullptr;
- }
-
- static void
- inject_prerequisites (action, target&, cxx&, scope&);
-
- recipe compile::
- apply (action a, target& xt, const match_result& mr) const
- {
- path_target& t (static_cast<path_target&> (xt));
-
- // Derive file name from target name.
- //
- if (t.path ().empty ())
- t.derive_path ("o", nullptr, (t.is_a<objso> () ? "-so" : nullptr));
-
- // Inject dependency on the output directory.
- //
- inject_parent_fsdir (a, t);
-
- // Search and match all the existing prerequisites. The injection
- // code (below) takes care of the ones it is adding.
- //
- // When cleaning, ignore prerequisites that are not in the same
- // or a subdirectory of our strong amalgamation.
- //
- const dir_path* amlg (
- a.operation () != clean_id
- ? nullptr
- : &t.strong_scope ().out_path ());
-
- link::search_paths_cache lib_paths; // Extract lazily.
-
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- // A dependency on a library is there so that we can get its
- // cxx.export.poptions. In particular, making sure it is
- // executed before us will only restrict parallelism. But we
- // do need to pre-match it in order to get its
- // prerequisite_targets populated. This is the "library
- // meta-information protocol". See also append_lib_options()
- // above.
- //
- if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libso> ())
- {
- if (a.operation () == update_id)
- {
- // Handle imported libraries. We know that for such libraries
- // we don't need to do match() in order to get options (if
- // any, they would be set by search_library()).
- //
- if (p.proj () == nullptr ||
- link::search_library (lib_paths, p.prerequisite) == nullptr)
- {
- match_only (a, p.search ());
- }
- }
-
- continue;
- }
-
- target& pt (p.search ());
-
- if (a.operation () == clean_id && !pt.dir.sub (*amlg))
- continue;
-
- build::match (a, pt);
- t.prerequisite_targets.push_back (&pt);
- }
-
- // Inject additional prerequisites. We only do it when
- // performing update since chances are we will have to
- // update some of our prerequisites in the process (auto-
- // generated source code).
- //
- if (a == perform_update_id)
- {
- // The cached prerequisite target should be the same as what
- // is in t.prerequisite_targets since we used standard
- // search() and match() above.
- //
- // @@ Ugly.
- //
- cxx& st (
- dynamic_cast<cxx&> (
- mr.target != nullptr ? *mr.target : *mr.prerequisite->target));
- inject_prerequisites (a, t, st, mr.prerequisite->scope);
- }
-
- switch (a)
- {
- case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean;
- default: return noop_recipe; // Configure update.
- }
- }
-
- // Reverse-lookup target type from extension.
- //
- static const target_type*
- map_extension (scope& s, const string& n, const string& e)
- {
- // We will just have to try all of the possible ones, in the
- // "most likely to match" order.
- //
- const variable& var (var_pool.find ("extension"));
-
- auto test = [&s, &n, &e, &var] (const target_type& tt)
- -> const target_type*
- {
- if (auto l = s.lookup (tt, n, var))
- if (as<string> (*l) == e)
- return &tt;
-
- return nullptr;
- };
-
- if (auto r = test (hxx::static_type)) return r;
- if (auto r = test (h::static_type)) return r;
- if (auto r = test (ixx::static_type)) return r;
- if (auto r = test (txx::static_type)) return r;
- if (auto r = test (cxx::static_type)) return r;
- if (auto r = test (c::static_type)) return r;
-
- return nullptr;
- }
-
- // Mapping of include prefixes (e.g., foo in <foo/bar>) for auto-
- // generated headers to directories where they will be generated.
- //
- // We are using a prefix map of directories (dir_path_map) instead
- // of just a map in order also cover sub-paths (e.g., <foo/more/bar>
- // if we continue with the example). Specifically, we need to make
- // sure we don't treat foobar as a sub-directory of foo.
- //
- // @@ The keys should be canonicalized.
- //
- using prefix_map = dir_path_map<dir_path>;
-
- static void
- append_prefixes (prefix_map& m, target& t, const char* var)
- {
- tracer trace ("cxx::append_prefixes");
-
- // If this target does not belong to any project (e.g, an
- // "imported as installed" library), then it can't possibly
- // generate any headers for us.
- //
- scope* rs (t.base_scope ().root_scope ());
- if (rs == nullptr)
- return;
-
- const dir_path& out_base (t.dir);
- const dir_path& out_root (rs->out_path ());
-
- if (auto l = t[var])
- {
- const auto& v (as<strings> (*l));
-
- for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
- {
- // -I can either be in the "-Ifoo" or "-I foo" form.
- //
- dir_path d;
- if (*i == "-I")
- {
- if (++i == e)
- break; // Let the compiler complain.
-
- d = dir_path (*i);
- }
- else if (i->compare (0, 2, "-I") == 0)
- d = dir_path (*i, 2, string::npos);
- else
- continue;
-
- level6 ([&]{trace << "-I '" << d << "'";});
-
- // If we are relative or not inside our project root, then
- // ignore.
- //
- if (d.relative () || !d.sub (out_root))
- continue;
-
- // If the target directory is a sub-directory of the include
- // directory, then the prefix is the difference between the
- // two. Otherwise, leave it empty.
- //
- // The idea here is to make this "canonical" setup work auto-
- // magically:
- //
- // 1. We include all files with a prefix, e.g., <foo/bar>.
- // 2. The library target is in the foo/ sub-directory, e.g.,
- // /tmp/foo/.
- // 3. The poptions variable contains -I/tmp.
- //
- dir_path p (out_base.sub (d) ? out_base.leaf (d) : dir_path ());
-
- auto j (m.find (p));
-
- if (j != m.end ())
- {
- if (j->second != d)
- {
- // We used to reject duplicates but it seems this can
- // be reasonably expected to work according to the order
- // of the -I options.
- //
- if (verb >= 4)
- trace << "overriding dependency prefix '" << p << "'\n"
- << " old mapping to " << j->second << "\n"
- << " new mapping to " << d;
-
- j->second = d;
- }
- }
- else
- {
- level6 ([&]{trace << "'" << p << "' = '" << d << "'";});
- m.emplace (move (p), move (d));
- }
- }
- }
- }
-
- // Append library prefixes based on the cxx.export.poptions variables
- // recursively, prerequisite libraries first.
- //
- static void
- append_lib_prefixes (prefix_map& m, target& l)
- {
- for (target* t: l.prerequisite_targets)
- {
- if (t == nullptr)
- continue;
-
- if (t->is_a<lib> () || t->is_a<liba> () || t->is_a<libso> ())
- append_lib_prefixes (m, *t);
- }
-
- append_prefixes (m, l, "cxx.export.poptions");
- }
-
- static prefix_map
- build_prefix_map (target& t)
- {
- prefix_map m;
-
- // First process the include directories from prerequisite
- // libraries. Note that here we don't need to see group
- // members (see apply()).
- //
- for (prerequisite& p: group_prerequisites (t))
- {
- target& pt (*p.target); // Already searched and matched.
-
- if (pt.is_a<lib> () || pt.is_a<liba> () || pt.is_a<libso> ())
- append_lib_prefixes (m, pt);
- }
-
- // Then process our own.
- //
- append_prefixes (m, t, "cxx.poptions");
-
- return m;
- }
-
- // Return the next make prerequisite starting from the specified
- // position and update position to point to the start of the
- // following prerequisite or l.size() if there are none left.
- //
- static string
- next (const string& l, size_t& p)
- {
- size_t n (l.size ());
-
- // Skip leading spaces.
- //
- for (; p != n && l[p] == ' '; p++) ;
-
- // Lines containing multiple prerequisites are 80 characters max.
- //
- string r;
- r.reserve (n);
-
- // Scan the next prerequisite while watching out for escape sequences.
- //
- for (; p != n && l[p] != ' '; p++)
- {
- char c (l[p]);
-
- if (c == '\\')
- c = l[++p];
-
- r += c;
- }
-
- // Skip trailing spaces.
- //
- for (; p != n && l[p] == ' '; p++) ;
-
- // Skip final '\'.
- //
- if (p == n - 1 && l[p] == '\\')
- p++;
-
- return r;
- }
-
- static void
- inject_prerequisites (action a, target& t, cxx& s, scope& ds)
- {
- tracer trace ("cxx::compile::inject_prerequisites");
-
- scope& rs (t.root_scope ());
- const string& cxx (as<string> (*rs["config.cxx"]));
-
- cstrings args {cxx.c_str ()};
-
- // Add cxx.export.poptions from prerequisite libraries. Note
- // that here we don't need to see group members (see apply()).
- //
- for (prerequisite& p: group_prerequisites (t))
- {
- target& pt (*p.target); // Already searched and matched.
-
- if (pt.is_a<lib> () || pt.is_a<liba> () || pt.is_a<libso> ())
- append_lib_options (args, pt, "cxx.export.poptions");
- }
-
- append_options (args, t, "cxx.poptions");
-
- // @@ Some C++ options (e.g., -std, -m) affect the preprocessor.
- // Or maybe they are not C++ options? Common options?
- //
- append_options (args, t, "cxx.coptions");
-
- string std; // Storage.
- append_std (args, t, std);
-
- if (t.is_a<objso> ())
- args.push_back ("-fPIC");
-
- args.push_back ("-M"); // Note: -MM -MG skips missing <>-included.
- args.push_back ("-MG"); // Treat missing headers as generated.
- args.push_back ("-MQ"); // Quoted target name.
- args.push_back ("*"); // Old versions can't handle empty target name.
-
- // We are using absolute source file path in order to get absolute
- // paths in the result. Any relative paths in the result are non-
- // existent, potentially auto-generated headers.
- //
- // @@ We will also have to use absolute -I paths to guarantee
- // that. Or just detect relative paths and error out?
- //
- args.push_back (s.path ().string ().c_str ());
- args.push_back (nullptr);
-
- level6 ([&]{trace << "target: " << t;});
-
- // Build the prefix map lazily only if we have non-existent files.
- // Also reuse it over restarts since it doesn't change.
- //
- prefix_map pm;
-
- // If any prerequisites that we have extracted changed, then we
- // have to redo the whole thing. The reason for this is auto-
- // generated headers: the updated header may now include a yet-
- // non-existent header. Unless we discover this and generate it
- // (which, BTW, will trigger another restart since that header,
- // in turn, can also include auto-generated headers), we will
- // end up with an error during compilation proper.
- //
- // One complication with this restart logic is that we will see
- // a "prefix" of prerequisites that we have already processed
- // (i.e., they are already in our prerequisite_targets list) and
- // we don't want to keep redoing this over and over again. One
- // thing to note, however, is that the prefix that we have seen
- // on the previous run must appear exactly the same in the
- // subsequent run. The reason for this is that none of the files
- // that it can possibly be based on have changed and thus it
- // should be exactly the same. To put it another way, the
- // presence or absence of a file in the dependency output can
- // only depend on the previous files (assuming the compiler
- // outputs them as it encounters them and it is hard to think
- // of a reason why would someone do otherwise). And we have
- // already made sure that all those files are up to date. And
- // here is the way we are going to exploit this: we are going
- // to keep track of how many prerequisites we have processed so
- // far and on restart skip right to the next one.
- //
- // Also, before we do all that, make sure the source file itself
- // if up to date.
- //
- execute_direct (a, s);
-
- size_t skip_count (0);
- for (bool restart (true); restart; )
- {
- restart = false;
-
- if (verb >= 3)
- print_process (args);
-
- try
- {
- process pr (args.data (), 0, -1); // Open pipe to stdout.
- ifdstream is (pr.in_ofd);
-
- size_t skip (skip_count);
- for (bool first (true), second (true); !(restart || is.eof ()); )
- {
- string l;
- getline (is, l);
-
- if (is.fail () && !is.eof ())
- fail << "error reading C++ compiler -M output";
-
- size_t pos (0);
-
- if (first)
- {
- // Empty output should mean the wait() call below will return
- // false.
- //
- if (l.empty ())
- break;
-
- assert (l[0] == '*' && l[1] == ':' && l[2] == ' ');
-
- first = false;
-
- // While normally we would have the source file on the
- // first line, if too long, it will be moved to the next
- // line and all we will have on this line is "*: \".
- //
- if (l.size () == 4 && l[3] == '\\')
- continue;
- else
- pos = 3; // Skip "*: ".
-
- // Fall through to the 'second' block.
- }
-
- if (second)
- {
- second = false;
- next (l, pos); // Skip the source file.
- }
-
- // If things go wrong (and they often do in this area), give
- // the user a bit extra context.
- //
- auto g (
- make_exception_guard (
- [&s]()
- {
- info << "while extracting dependencies from " << s;
- }));
-
- while (pos != l.size ())
- {
- string fs (next (l, pos));
-
- // Skip until where we left off.
- //
- if (skip != 0)
- {
- skip--;
- continue;
- }
-
- path f (move (fs));
- f.normalize ();
-
- if (!f.absolute ())
- {
- // This is probably as often an error as an auto-generated
- // file, so trace at level 4.
- //
- level4 ([&]{trace << "non-existent header '" << f << "'";});
-
- // If we already did it and build_prefix_map() returned empty,
- // then we would have failed below.
- //
- if (pm.empty ())
- pm = build_prefix_map (t);
-
- // First try the whole file. Then just the directory.
- //
- // @@ Has to be a separate map since the prefix can be
- // the same as the file name.
- //
- // auto i (pm.find (f));
-
- // Find the most qualified prefix of which we are a
- // sub-path.
- //
- auto i (pm.end ());
-
- if (!pm.empty ())
- {
- const dir_path& d (f.directory ());
- i = pm.upper_bound (d);
-
- // Get the greatest less than, if any. We might
- // still not be a sub. Note also that we still
- // have to check the last element is upper_bound()
- // returned end().
- //
- if (i == pm.begin () || !d.sub ((--i)->first))
- i = pm.end ();
- }
-
- if (i == pm.end ())
- fail << "unable to map presumably auto-generated header '"
- << f << "' to a project";
-
- f = i->second / f;
- }
-
- level6 ([&]{trace << "injecting " << f;});
-
- // Split the name into its directory part, the name part, and
- // extension. Here we can assume the name part is a valid
- // filesystem name.
- //
- // Note that if the file has no extension, we record an empty
- // extension rather than NULL (which would signify that the
- // default extension should be added).
- //
- dir_path d (f.directory ());
- string n (f.leaf ().base ().string ());
- const char* es (f.extension ());
- const string* e (&extension_pool.find (es != nullptr ? es : ""));
-
- // Determine the target type.
- //
- const target_type* tt (nullptr);
-
- // See if this directory is part of any project out_root
- // hierarchy. Note that this will miss all the headers
- // that come from src_root (so they will be treated as
- // generic C headers below). Generally, we don't have
- // the ability to determine that some file belongs to
- // src_root of some project. But that's not a problem
- // for our purposes: it is only important for us to
- // accurately determine target types for headers that
- // could be auto-generated.
- //
- scope& b (scopes.find (d));
- if (b.root_scope () != nullptr)
- tt = map_extension (b, n, *e);
-
- // If it is outside any project, or the project doesn't have
- // such an extension, assume it is a plain old C header.
- //
- if (tt == nullptr)
- tt = &h::static_type;
-
- // Find or insert target.
- //
- path_target& pt (
- static_cast<path_target&> (search (*tt, d, n, e, &ds)));
-
- // Assign path.
- //
- if (pt.path ().empty ())
- pt.path (move (f));
-
- // Match to a rule.
- //
- build::match (a, pt);
-
- // Update it.
- //
- // There would normally be a lot of headers for every source
- // file (think all the system headers) and this can get
- // expensive. At the same time, most of these headers are
- // existing files that we will never be updated (again,
- // system headers, for example) and the rule that will match
- // them is fallback file_rule. That rule has an optimization
- // in that it returns noop_recipe (which causes the target
- // state to be automatically set to unchanged) if the file
- // is known to be up to date.
- //
- if (pt.state () != target_state::unchanged)
- {
- // We only want to restart if our call to execute() actually
- // caused an update. In particular, the target could already
- // have been in target_state::changed because of a dependency
- // extraction run for some other source file.
- //
- target_state os (pt.state ());
- target_state ns (execute_direct (a, pt));
-
- if (ns != os && ns != target_state::unchanged)
- {
- level6 ([&]{trace << "updated " << pt << ", restarting";});
- restart = true;
- }
- }
-
- // Add to our prerequisite target list.
- //
- t.prerequisite_targets.push_back (&pt);
- skip_count++;
- }
- }
-
- // We may not have read all the output (e.g., due to a restart),
- // so close the file descriptor before waiting to avoid blocking
- // the other end.
- //
- is.close ();
-
- // We assume the child process issued some diagnostics.
- //
- if (!pr.wait ())
- {
- // In case of a restarts, we closed our end of the pipe early
- // which might have caused the other end to fail. So far we
- // experienced this on Fedora 23 with GCC 5.3.1 and there were
- // no diagnostics issued, just the non-zero exit status. If we
- // do get diagnostics, then we will have to read and discard the
- // output until eof.
- //
- if (!restart)
- throw failed ();
- }
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- // In a multi-threaded program that fork()'ed but did not exec(),
- // it is unwise to try to do any kind of cleanup (like unwinding
- // the stack and running destructors).
- //
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- }
- }
-
- target_state compile::
- perform_update (action a, target& xt)
- {
- path_target& t (static_cast<path_target&> (xt));
- cxx* s (execute_prerequisites<cxx> (a, t, t.mtime ()));
-
- if (s == nullptr)
- return target_state::unchanged;
-
- // Translate paths to relative (to working directory) ones. This
- // results in easier to read diagnostics.
- //
- path relo (relative (t.path ()));
- path rels (relative (s->path ()));
-
- scope& rs (t.root_scope ());
- const string& cxx (as<string> (*rs["config.cxx"]));
-
- cstrings args {cxx.c_str ()};
-
- // Add cxx.export.poptions from prerequisite libraries. Note that
- // here we don't need to see group members (see apply()).
- //
- for (prerequisite& p: group_prerequisites (t))
- {
- target& pt (*p.target); // Already searched and matched.
-
- if (pt.is_a<lib> () || pt.is_a<liba> () || pt.is_a<libso> ())
- append_lib_options (args, pt, "cxx.export.poptions");
- }
-
- append_options (args, t, "cxx.poptions");
- append_options (args, t, "cxx.coptions");
-
- string std; // Storage.
- append_std (args, t, std);
-
- if (t.is_a<objso> ())
- args.push_back ("-fPIC");
-
- args.push_back ("-o");
- args.push_back (relo.string ().c_str ());
-
- args.push_back ("-c");
- args.push_back (rels.string ().c_str ());
-
- args.push_back (nullptr);
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "c++ " << *s;
-
- try
- {
- process pr (args.data ());
-
- if (!pr.wait ())
- throw failed ();
-
- // Should we go to the filesystem and get the new mtime? We
- // know the file has been modified, so instead just use the
- // current clock time. It has the advantage of having the
- // subseconds precision.
- //
- t.mtime (system_clock::now ());
- return target_state::changed;
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- // In a multi-threaded program that fork()'ed but did not exec(),
- // it is unwise to try to do any kind of cleanup (like unwinding
- // the stack and running destructors).
- //
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- }
-
- compile compile::instance;
- }
-}
diff --git a/build/cxx/install b/build/cxx/install
deleted file mode 100644
index 9df8408..0000000
--- a/build/cxx/install
+++ /dev/null
@@ -1,29 +0,0 @@
-// file : build/cxx/install -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CXX_INSTALL
-#define BUILD_CXX_INSTALL
-
-#include <build/types>
-#include <build/install/rule>
-
-namespace build
-{
- namespace cxx
- {
- class install: public build::install::file_rule
- {
- public:
- virtual target*
- filter (action, target&, prerequisite_member) const;
-
- virtual match_result
- match (action, target&, const std::string&) const;
-
- static install instance;
- };
- }
-}
-
-#endif // BUILD_CXX_INSTALL
diff --git a/build/cxx/install.cxx b/build/cxx/install.cxx
deleted file mode 100644
index f8c2c06..0000000
--- a/build/cxx/install.cxx
+++ /dev/null
@@ -1,66 +0,0 @@
-// file : build/cxx/install.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cxx/install>
-
-#include <build/bin/target>
-
-#include <build/cxx/target>
-#include <build/cxx/link>
-
-using namespace std;
-
-namespace build
-{
- namespace cxx
- {
- using namespace bin;
-
- target* install::
- filter (action a, target& t, prerequisite_member p) const
- {
- if (t.is_a<exe> ())
- {
- // Don't install executable's prerequisite headers.
- //
- if (p.is_a<hxx> () || p.is_a<ixx> () || p.is_a<txx> () || p.is_a<h> ())
- return nullptr;
- }
-
- // If this is a shared library prerequisite, install it as long as it
- // is in the same amalgamation as we are.
- //
- if ((t.is_a<exe> () || t.is_a<libso> ()) &&
- (p.is_a<lib> () || p.is_a<libso> ()))
- {
- target* pt (&p.search ());
-
- // If this is the lib{} group, pick a member which we would link.
- //
- if (lib* l = pt->is_a<lib> ())
- pt = &link::link_member (*l, link::link_order (t));
-
- if (pt->is_a<libso> ()) // Can be liba{}.
- return pt->in (t.weak_scope ()) ? pt : nullptr;
- }
-
- return file_rule::filter (a, t, p);
- }
-
- match_result install::
- match (action a, target& t, const std::string& hint) const
- {
- // @@ How do we split the hint between the two?
- //
-
- // We only want to handle installation if we are also the
- // ones building this target. So first run link's match().
- //
- match_result r (link::instance.match (a, t, hint));
- return r ? install::file_rule::match (a, t, "") : r;
- }
-
- install install::instance;
- }
-}
diff --git a/build/cxx/link b/build/cxx/link
deleted file mode 100644
index 5a5aafa..0000000
--- a/build/cxx/link
+++ /dev/null
@@ -1,70 +0,0 @@
-// file : build/cxx/link -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CXX_LINK
-#define BUILD_CXX_LINK
-
-#include <vector>
-
-#include <butl/optional>
-
-#include <build/types>
-#include <build/rule>
-
-#include <build/bin/target>
-
-namespace build
-{
- namespace cxx
- {
- class link: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_update (action, target&);
-
- static link instance;
-
- public:
- enum class type {e, a, so};
- enum class order {a, so, a_so, so_a};
-
- static type
- link_type (target& t)
- {
- return t.is_a<bin::exe> ()
- ? type::e
- : (t.is_a<bin::liba> () ? type::a : type::so);
- }
-
- static order
- link_order (target&);
-
- // Determine the library member (liba or libso) to link.
- //
- static target&
- link_member (bin::lib&, order);
-
- private:
- friend class compile;
-
- using search_paths = std::vector<dir_path>;
- using search_paths_cache = butl::optional<search_paths>;
-
- static target*
- search_library (search_paths_cache&, prerequisite&);
-
- static search_paths
- extract_library_paths (scope&);
- };
- }
-}
-
-#endif // BUILD_CXX_LINK
diff --git a/build/cxx/link.cxx b/build/cxx/link.cxx
deleted file mode 100644
index 81e6c0b..0000000
--- a/build/cxx/link.cxx
+++ /dev/null
@@ -1,875 +0,0 @@
-// file : build/cxx/link.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cxx/link>
-
-#include <vector>
-#include <string>
-#include <cstddef> // size_t
-#include <cstdlib> // exit()
-#include <utility> // move()
-
-#include <butl/process>
-#include <butl/utility> // reverse_iterate
-#include <butl/fdstream>
-#include <butl/optional>
-#include <butl/path-map>
-#include <butl/filesystem>
-
-#include <build/types>
-#include <build/scope>
-#include <build/variable>
-#include <build/algorithm>
-#include <build/diagnostics>
-#include <build/context>
-
-#include <build/bin/target>
-#include <build/cxx/target>
-
-#include <build/cxx/utility>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace cxx
- {
- using namespace bin;
-
- link::order link::
- link_order (target& t)
- {
- const char* var;
-
- switch (link_type (t))
- {
- case type::e: var = "bin.exe.lib"; break;
- case type::a: var = "bin.liba.lib"; break;
- case type::so: var = "bin.libso.lib"; break;
- }
-
- const auto& v (as<strings> (*t[var]));
- return v[0] == "shared"
- ? v.size () > 1 && v[1] == "static" ? order::so_a : order::so
- : v.size () > 1 && v[1] == "shared" ? order::a_so : order::a;
- }
-
- target& link::
- link_member (bin::lib& l, order lo)
- {
- bool lso (true);
- const string& at (as<string> (*l["bin.lib"])); // Available types.
-
- switch (lo)
- {
- case order::a:
- case order::a_so:
- lso = false; // Fall through.
- case order::so:
- case order::so_a:
- {
- if (lso ? at == "static" : at == "shared")
- {
- if (lo == order::a_so || lo == order::so_a)
- lso = !lso;
- else
- fail << (lso ? "shared" : "static") << " build of " << l
- << " is not available";
- }
- }
- }
-
- target* r (lso ? static_cast<target*> (l.so) : l.a);
-
- if (r == nullptr)
- r = &search (lso ? libso::static_type : liba::static_type,
- prerequisite_key {nullptr, l.key (), nullptr});
-
- return *r;
- }
-
- link::search_paths link::
- extract_library_paths (scope& bs)
- {
- search_paths r;
- scope& rs (*bs.root_scope ());
-
- // Extract user-supplied search paths (i.e., -L).
- //
- if (auto l = bs["cxx.loptions"])
- {
- const auto& v (as<strings> (*l));
-
- for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
- {
- // -L can either be in the "-Lfoo" or "-L foo" form.
- //
- dir_path d;
- if (*i == "-L")
- {
- if (++i == e)
- break; // Let the compiler complain.
-
- d = dir_path (*i);
- }
- else if (i->compare (0, 2, "-L") == 0)
- d = dir_path (*i, 2, string::npos);
- else
- continue;
-
- // Ignore relative paths. Or maybe we should warn?
- //
- if (!d.relative ())
- r.push_back (move (d));
- }
- }
-
- // Extract system search paths.
- //
- cstrings args;
- string std_storage;
-
- args.push_back (as<string> (*rs["config.cxx"]).c_str ());
- append_options (args, bs, "cxx.coptions");
- append_std (args, bs, std_storage);
- append_options (args, bs, "cxx.loptions");
- args.push_back ("-print-search-dirs");
- args.push_back (nullptr);
-
- if (verb >= 3)
- print_process (args);
-
- string l;
- try
- {
- process pr (args.data (), 0, -1); // Open pipe to stdout.
- ifdstream is (pr.in_ofd);
-
- while (!is.eof ())
- {
- string s;
- getline (is, s);
-
- if (is.fail () && !is.eof ())
- fail << "error reading C++ compiler -print-search-dirs output";
-
- if (s.compare (0, 12, "libraries: =") == 0)
- {
- l.assign (s, 12, string::npos);
- break;
- }
- }
-
- is.close (); // Don't block.
-
- if (!pr.wait ())
- throw failed ();
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
-
- if (l.empty ())
- fail << "unable to extract C++ compiler system library paths";
-
- // Now the fun part: figuring out which delimiter is used.
- // Normally it is ':' but on Windows it is ';' (or can be;
- // who knows for sure). Also note that these paths are
- // absolute (or should be). So here is what we are going
- // to do: first look for ';'. If found, then that's the
- // delimiter. If not found, then there are two cases:
- // it is either a single Windows path or the delimiter
- // is ':'. To distinguish these two cases we check if
- // the path starts with a Windows drive.
- //
- char d (';');
- string::size_type e (l.find (d));
-
- if (e == string::npos &&
- (l.size () < 2 || l[0] == '/' || l[1] != ':'))
- {
- d = ':';
- e = l.find (d);
- }
-
- // Now chop it up. We already have the position of the
- // first delimiter (if any).
- //
- for (string::size_type b (0);; e = l.find (d, (b = e + 1)))
- {
- r.emplace_back (l, b, (e != string::npos ? e - b : e));
- r.back ().normalize ();
-
- if (e == string::npos)
- break;
- }
-
- return r;
- }
-
- target* link::
- search_library (search_paths_cache& spc, prerequisite& p)
- {
- tracer trace ("cxx::link::search_library");
-
- // First check the cache.
- //
- if (p.target != nullptr)
- return p.target;
-
- bool l (p.is_a<lib> ());
- const string* ext (l ? nullptr : p.ext); // Only for liba/libso.
-
- // Then figure out what we need to search for.
- //
-
- // liba
- //
- path an;
- const string* ae;
-
- if (l || p.is_a<liba> ())
- {
- an = path ("lib" + p.name);
-
- // Note that p.scope should be the same as the target's for
- // which we are looking for this library. The idea here is
- // that we have to use the same "extension configuration" as
- // the target's.
- //
- ae = ext == nullptr
- ? &liba::static_type.extension (p.key ().tk, p.scope)
- : ext;
-
- if (!ae->empty ())
- {
- an += '.';
- an += *ae;
- }
- }
-
- // libso
- //
- path sn;
- const string* se;
-
- if (l || p.is_a<libso> ())
- {
- sn = path ("lib" + p.name);
- se = ext == nullptr
- ? &libso::static_type.extension (p.key ().tk, p.scope)
- : ext;
-
- if (!se->empty ())
- {
- sn += '.';
- sn += *se;
- }
- }
-
- // Now search.
- //
- if (!spc)
- spc = extract_library_paths (p.scope);
-
- liba* a (nullptr);
- libso* s (nullptr);
-
- path f; // Reuse the buffer.
- const dir_path* pd;
- for (const dir_path& d: *spc)
- {
- timestamp mt;
-
- // liba
- //
- if (!an.empty ())
- {
- f = d;
- f /= an;
-
- if ((mt = file_mtime (f)) != timestamp_nonexistent)
- {
- // Enter the target. Note that because the search paths are
- // normalized, the result is automatically normalized as well.
- //
- a = &targets.insert<liba> (d, p.name, ae, trace);
-
- if (a->path ().empty ())
- a->path (move (f));
-
- a->mtime (mt);
- }
- }
-
- // libso
- //
- if (!sn.empty ())
- {
- f = d;
- f /= sn;
-
- if ((mt = file_mtime (f)) != timestamp_nonexistent)
- {
- s = &targets.insert<libso> (d, p.name, se, trace);
-
- if (s->path ().empty ())
- s->path (move (f));
-
- s->mtime (mt);
- }
- }
-
- if (a != nullptr || s != nullptr)
- {
- pd = &d;
- break;
- }
- }
-
- if (a == nullptr && s == nullptr)
- return nullptr;
-
- if (l)
- {
- // Enter the target group.
- //
- lib& l (targets.insert<lib> (*pd, p.name, p.ext, trace));
-
- // It should automatically link-up to the members we have found.
- //
- assert (l.a == a);
- assert (l.so == s);
-
- // Set the bin.lib variable to indicate what's available.
- //
- const char* bl (a != nullptr
- ? (s != nullptr ? "both" : "static")
- : "shared");
- l.assign ("bin.lib") = bl;
-
- p.target = &l;
- }
- else
- p.target = p.is_a<liba> () ? static_cast<target*> (a) : s;
-
- return p.target;
- }
-
- match_result link::
- match (action a, target& t, const string& hint) const
- {
- tracer trace ("cxx::link::match");
-
- // @@ TODO:
- //
- // - if path already assigned, verify extension?
- //
- // @@ Q:
- //
- // - if there is no .o, are we going to check if the one derived
- // from target exist or can be built? A: No.
- // What if there is a library. Probably ok if .a, not if .so.
- // (i.e., a utility library).
- //
-
- type lt (link_type (t));
-
- // Scan prerequisites and see if we can work with what we've got.
- //
- bool seen_cxx (false), seen_c (false), seen_obj (false),
- seen_lib (false);
-
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- if (p.is_a<cxx> ())
- {
- seen_cxx = seen_cxx || true;
- }
- else if (p.is_a<c> ())
- {
- seen_c = seen_c || true;
- }
- else if (p.is_a<obja> ())
- {
- if (lt == type::so)
- fail << "shared library " << t << " prerequisite " << p
- << " is static object";
-
- seen_obj = seen_obj || true;
- }
- else if (p.is_a<objso> () ||
- p.is_a<obj> ())
- {
- seen_obj = seen_obj || true;
- }
- else if (p.is_a<liba> () ||
- p.is_a<libso> () ||
- p.is_a<lib> ())
- {
- seen_lib = seen_lib || true;
- }
- }
-
- // We will only chain a C source if there is also a C++ source or we
- // were explicitly told to.
- //
- if (seen_c && !seen_cxx && hint < "cxx")
- {
- level4 ([&]{trace << "c prerequisite(s) without c++ or hint";});
- return nullptr;
- }
-
- // If we have any prerequisite libraries (which also means that
- // we match), search/import and pre-match them to implement the
- // "library meta-information protocol". Don't do this if we are
- // called from the install rule just to check if we would match.
- //
- if (seen_lib && lt != type::e &&
- a.operation () != install_id && a.outer_operation () != install_id)
- {
- if (t.group != nullptr)
- t.group->prerequisite_targets.clear (); // lib{}'s
-
- search_paths_cache lib_paths; // Extract lazily.
-
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libso> ())
- {
- target* pt (nullptr);
-
- // Handle imported libraries.
- //
- if (p.proj () != nullptr)
- pt = search_library (lib_paths, p.prerequisite);
-
- if (pt == nullptr)
- {
- pt = &p.search ();
- match_only (a, *pt);
- }
-
- // If the prerequisite came from the lib{} group, then also
- // add it to lib's prerequisite_targets.
- //
- if (!p.prerequisite.belongs (t))
- t.group->prerequisite_targets.push_back (pt);
-
- t.prerequisite_targets.push_back (pt);
- }
- }
- }
-
- return seen_cxx || seen_c || seen_obj || seen_lib ? &t : nullptr;
- }
-
- recipe link::
- apply (action a, target& xt, const match_result&) const
- {
- tracer trace ("cxx::link::apply");
-
- path_target& t (static_cast<path_target&> (xt));
-
- type lt (link_type (t));
- bool so (lt == type::so);
- order lo (link_order (t));
-
- // Derive file name from target name.
- //
- if (t.path ().empty ())
- {
- auto l (t["extension"]);
- const char* e (l ? as<string> (*l).c_str () : nullptr);
-
- switch (lt)
- {
- case type::e:
- {
- t.derive_path (e != nullptr ? e : "");
- break;
- }
- case type::a:
- case type::so:
- {
- auto l (t["bin.libprefix"]);
- t.derive_path (e != nullptr ? e : (lt == type::a ? "a" : "so"),
- l ? as<string> (*l).c_str () : "lib");
- break;
- }
- }
- }
-
- t.prerequisite_targets.clear (); // See lib pre-match in match() above.
-
- // Inject dependency on the output directory.
- //
- inject_parent_fsdir (a, t);
-
- // We may need the project roots for rule chaining (see below).
- // We will resolve them lazily only if needed.
- //
- scope* root (nullptr);
- const dir_path* out_root (nullptr);
- const dir_path* src_root (nullptr);
-
- search_paths_cache lib_paths; // Extract lazily.
-
- // Process prerequisites: do rule chaining for C and C++ source
- // files as well as search and match.
- //
- // When cleaning, ignore prerequisites that are not in the same
- // or a subdirectory of our strong amalgamation.
- //
- const dir_path* amlg (
- a.operation () != clean_id
- ? nullptr
- : &t.strong_scope ().out_path ());
-
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- bool group (!p.prerequisite.belongs (t)); // Group's prerequisite.
- target* pt (nullptr);
-
- if (!p.is_a<c> () && !p.is_a<cxx> ())
- {
- // Handle imported libraries.
- //
- if (p.proj () != nullptr)
- pt = search_library (lib_paths, p.prerequisite);
-
- // The rest is the same basic logic as in search_and_match().
- //
- if (pt == nullptr)
- pt = &p.search ();
-
- if (a.operation () == clean_id && !pt->dir.sub (*amlg))
- continue; // Skip.
-
- // If this is the obj{} or lib{} target group, then pick the
- // appropriate member and make sure it is searched and matched.
- //
- if (obj* o = pt->is_a<obj> ())
- {
- pt = so ? static_cast<target*> (o->so) : o->a;
-
- if (pt == nullptr)
- pt = &search (so ? objso::static_type : obja::static_type,
- p.key ());
- }
- else if (lib* l = pt->is_a<lib> ())
- {
- pt = &link_member (*l, lo);
- }
-
- build::match (a, *pt);
- t.prerequisite_targets.push_back (pt);
- continue;
- }
-
- if (root == nullptr)
- {
- // Which scope shall we use to resolve the root? Unlikely,
- // but possible, the prerequisite is from a different project
- // altogether. So we are going to use the target's project.
- //
- root = &t.root_scope ();
- out_root = &root->out_path ();
- src_root = &root->src_path ();
- }
-
- const prerequisite_key& cp (p.key ()); // c(xx){} prerequisite key.
- const target_type& o_type (
- group
- ? obj::static_type
- : (so ? objso::static_type : obja::static_type));
-
- // Come up with the obj*{} target. The c(xx){} prerequisite
- // directory can be relative (to the scope) or absolute. If it is
- // relative, then use it as is. If it is absolute, then translate
- // it to the corresponding directory under out_root. While the
- // c(xx){} directory is most likely under src_root, it is also
- // possible it is under out_root (e.g., generated source).
- //
- dir_path d;
- {
- const dir_path& cpd (*cp.tk.dir);
-
- if (cpd.relative () || cpd.sub (*out_root))
- d = cpd;
- else
- {
- if (!cpd.sub (*src_root))
- fail << "out of project prerequisite " << cp <<
- info << "specify corresponding " << o_type.name << "{} "
- << "target explicitly";
-
- d = *out_root / cpd.leaf (*src_root);
- }
- }
-
- target& ot (search (o_type, d, *cp.tk.name, nullptr, cp.scope));
-
- // If we are cleaning, check that this target is in the same or
- // a subdirectory of our strong amalgamation.
- //
- if (a.operation () == clean_id && !ot.dir.sub (*amlg))
- {
- // If we shouldn't clean obj{}, then it is fair to assume
- // we shouldn't clean cxx{} either (generated source will
- // be in the same directory as obj{} and if not, well, go
- // find yourself another build system ;-)).
- //
- continue; // Skip.
- }
-
- // If we have created the obj{} target group, pick one of its
- // members; the rest would be primarily concerned with it.
- //
- if (group)
- {
- obj& o (static_cast<obj&> (ot));
- pt = so ? static_cast<target*> (o.so) : o.a;
-
- if (pt == nullptr)
- pt = &search (so ? objso::static_type : obja::static_type,
- o.dir, o.name, o.ext, nullptr);
- }
- else
- pt = &ot;
-
- // If this obj*{} target already exists, then it needs to be
- // "compatible" with what we are doing here.
- //
- // This gets a bit tricky. We need to make sure the source files
- // are the same which we can only do by comparing the targets to
- // which they resolve. But we cannot search the ot's prerequisites
- // -- only the rule that matches can. Note, however, that if all
- // this works out, then our next step is to match the obj*{}
- // target. If things don't work out, then we fail, in which case
- // searching and matching speculatively doesn't really hurt.
- //
- bool found (false);
- for (prerequisite_member p1:
- reverse_group_prerequisite_members (a, *pt))
- {
- // Ignore some known target types (fsdir, headers, libraries).
- //
- if (p1.is_a<fsdir> () ||
- p1.is_a<h> () ||
- (p.is_a<cxx> () && (p1.is_a<hxx> () ||
- p1.is_a<ixx> () ||
- p1.is_a<txx> ())) ||
- p1.is_a<lib> () ||
- p1.is_a<liba> () ||
- p1.is_a<libso> ())
- {
- continue;
- }
-
- if (!p1.is_a<cxx> ())
- fail << "synthesized target for prerequisite " << cp
- << " would be incompatible with existing target " << *pt <<
- info << "unexpected existing prerequisite type " << p1 <<
- info << "specify corresponding obj{} target explicitly";
-
- if (!found)
- {
- build::match (a, *pt); // Now p1 should be resolved.
-
- // Searching our own prerequisite is ok.
- //
- if (&p.search () != &p1.search ())
- fail << "synthesized target for prerequisite " << cp << " would "
- << "be incompatible with existing target " << *pt <<
- info << "existing prerequisite " << p1 << " does not match "
- << cp <<
- info << "specify corresponding " << o_type.name << "{} target "
- << "explicitly";
-
- found = true;
- // Check the rest of the prerequisites.
- }
- }
-
- if (!found)
- {
- // Note: add the source to the group, not the member.
- //
- ot.prerequisites.emplace_back (p.as_prerequisite (trace));
-
- // Add our lib*{} prerequisites to the object file (see
- // cxx.export.poptions above for details). Note: no need
- // to go into group members.
- //
- // Initially, we were only adding imported libraries, but
- // there is a problem with this approach: the non-imported
- // library might depend on the imported one(s) which we will
- // never "see" unless we start with this library.
- //
- for (prerequisite& p: group_prerequisites (t))
- {
- if (p.is_a<lib> () || p.is_a<liba> () || p.is_a<libso> ())
- ot.prerequisites.emplace_back (p);
- }
-
- build::match (a, *pt);
- }
-
- t.prerequisite_targets.push_back (pt);
- }
-
- switch (a)
- {
- case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean;
- default: return noop_recipe; // Configure update.
- }
- }
-
- target_state link::
- perform_update (action a, target& xt)
- {
- path_target& t (static_cast<path_target&> (xt));
-
- type lt (link_type (t));
- bool so (lt == type::so);
-
- if (!execute_prerequisites (a, t, t.mtime ()))
- return target_state::unchanged;
-
- // Translate paths to relative (to working directory) ones. This
- // results in easier to read diagnostics.
- //
- path relt (relative (t.path ()));
-
- scope& rs (t.root_scope ());
- cstrings args;
-
- // Storage.
- //
- string std;
- string soname;
- strings sargs;
-
- if (lt == type::a)
- {
- //@@ ranlib
- //
- args.push_back ("ar");
- args.push_back ("-rc");
- args.push_back (relt.string ().c_str ());
- }
- else
- {
- args.push_back (as<string> (*rs["config.cxx"]).c_str ());
- append_options (args, t, "cxx.coptions");
- append_std (args, t, std);
-
- if (so)
- args.push_back ("-shared");
-
- args.push_back ("-o");
- args.push_back (relt.string ().c_str ());
-
- // Set soname.
- //
- if (so)
- {
- soname = "-Wl,-soname," + relt.leaf ().string ();
- args.push_back (soname.c_str ());
- }
-
- // Add rpaths. First the ones specified by the user so that they
- // take precedence.
- //
- if (auto l = t["bin.rpath"])
- for (const string& p: as<strings> (*l))
- sargs.push_back ("-Wl,-rpath," + p);
-
- // Then the paths of the shared libraries we are linking to.
- //
- for (target* pt: t.prerequisite_targets)
- {
- if (libso* ls = pt->is_a<libso> ())
- sargs.push_back (
- "-Wl,-rpath," + ls->path ().directory ().string ());
- }
- }
-
- size_t oend (sargs.size ()); // Note the end of options.
-
- for (target* pt: t.prerequisite_targets)
- {
- path_target* ppt;
-
- if ((ppt = pt->is_a<obja> ()) ||
- (ppt = pt->is_a<objso> ()) ||
- (ppt = pt->is_a<liba> ()) ||
- (ppt = pt->is_a<libso> ()))
- {
- sargs.push_back (relative (ppt->path ()).string ()); // string()&&
- }
- }
-
- // Finish assembling args from sargs.
- //
- for (size_t i (0); i != sargs.size (); ++i)
- {
- if (lt != type::a && i == oend)
- append_options (args, t, "cxx.loptions");
-
- args.push_back (sargs[i].c_str ());
- }
-
- if (lt != type::a)
- append_options (args, t, "cxx.libs");
-
- args.push_back (nullptr);
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "ld " << t;
-
- try
- {
- process pr (args.data ());
-
- if (!pr.wait ())
- throw failed ();
-
- // Should we go to the filesystem and get the new mtime? We
- // know the file has been modified, so instead just use the
- // current clock time. It has the advantage of having the
- // subseconds precision.
- //
- t.mtime (system_clock::now ());
- return target_state::changed;
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- // In a multi-threaded program that fork()'ed but did not exec(),
- // it is unwise to try to do any kind of cleanup (like unwinding
- // the stack and running destructors).
- //
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- }
-
- link link::instance;
- }
-}
diff --git a/build/cxx/module b/build/cxx/module
deleted file mode 100644
index a3bd436..0000000
--- a/build/cxx/module
+++ /dev/null
@@ -1,23 +0,0 @@
-// file : build/cxx/module -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CXX_MODULE
-#define BUILD_CXX_MODULE
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/module>
-
-namespace build
-{
- namespace cxx
- {
- extern "C" bool
- cxx_init (
- scope&, scope&, const location&, unique_ptr<module>&, bool, bool);
- }
-}
-
-#endif // BUILD_CXX_MODULE
diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx
deleted file mode 100644
index c979328..0000000
--- a/build/cxx/module.cxx
+++ /dev/null
@@ -1,230 +0,0 @@
-// file : build/cxx/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cxx/module>
-
-#include <butl/process>
-#include <butl/fdstream>
-
-#include <build/scope>
-#include <build/diagnostics>
-
-#include <build/config/utility>
-#include <build/install/utility>
-
-#include <build/bin/target>
-
-#include <build/cxx/target>
-#include <build/cxx/compile>
-#include <build/cxx/link>
-#include <build/cxx/install>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace cxx
- {
- extern "C" bool
- cxx_init (scope& r,
- scope& b,
- const location& loc,
- std::unique_ptr<module>&,
- bool first,
- bool)
- {
- tracer trace ("cxx::init");
- level5 ([&]{trace << "for " << b.out_path ();});
-
- // Initialize the bin module. Only do this if it hasn't already
- // been loaded so that we don't overwrite user's bin.* settings.
- //
- {
- auto l (b["bin.loaded"]);
-
- if (!l || !as<bool> (*l))
- load_module (false, "bin", r, b, loc);
- }
-
- // Enter module variables.
- //
- // @@ Probably should only be done on load; make sure reset() unloads
- // modules.
- //
- // @@ Should probably cache the variable pointers so we don't have
- // to keep looking them up.
- //
- if (first)
- {
- auto& v (var_pool);
-
- v.find ("config.cxx", string_type); //@@ VAR type
-
- v.find ("config.cxx.poptions", strings_type);
- v.find ("config.cxx.coptions", strings_type);
- v.find ("config.cxx.loptions", strings_type);
- v.find ("config.cxx.libs", strings_type);
-
- v.find ("cxx.poptions", strings_type);
- v.find ("cxx.coptions", strings_type);
- v.find ("cxx.loptions", strings_type);
- v.find ("cxx.libs", strings_type);
-
- v.find ("cxx.export.poptions", strings_type);
- v.find ("cxx.export.coptions", strings_type);
- v.find ("cxx.export.loptions", strings_type);
- v.find ("cxx.export.libs", strings_type);
-
- v.find ("cxx.std", string_type);
- }
-
- // Register target types.
- //
- {
- auto& t (b.target_types);
-
- t.insert<h> ();
- t.insert<c> ();
-
- t.insert<cxx> ();
- t.insert<hxx> ();
- t.insert<ixx> ();
- t.insert<txx> ();
- }
-
- // Register rules.
- //
- {
- using namespace bin;
-
- auto& r (b.rules);
-
- r.insert<obja> (perform_update_id, "cxx.compile", compile::instance);
-
- r.insert<obja> (perform_update_id, "cxx.compile", compile::instance);
- r.insert<obja> (perform_clean_id, "cxx.compile", compile::instance);
-
- r.insert<objso> (perform_update_id, "cxx.compile", compile::instance);
- r.insert<objso> (perform_clean_id, "cxx.compile", compile::instance);
-
- r.insert<exe> (perform_update_id, "cxx.link", link::instance);
- r.insert<exe> (perform_clean_id, "cxx.link", link::instance);
-
- r.insert<liba> (perform_update_id, "cxx.link", link::instance);
- r.insert<liba> (perform_clean_id, "cxx.link", link::instance);
-
- r.insert<libso> (perform_update_id, "cxx.link", link::instance);
- r.insert<libso> (perform_clean_id, "cxx.link", link::instance);
-
- // Register for configure so that we detect unresolved imports
- // during configuration rather that later, e.g., during update.
- //
- r.insert<obja> (configure_update_id, "cxx.compile", compile::instance);
- r.insert<objso> (configure_update_id, "cxx.compile", compile::instance);
-
- r.insert<exe> (configure_update_id, "cxx.link", link::instance);
- r.insert<liba> (configure_update_id, "cxx.link", link::instance);
- r.insert<libso> (configure_update_id, "cxx.link", link::instance);
-
- //@@ Should we check if install module was loaded (see bin)?
- //
- r.insert<exe> (perform_install_id, "cxx.install", install::instance);
- r.insert<liba> (perform_install_id, "cxx.install", install::instance);
- r.insert<libso> (perform_install_id, "cxx.install", install::instance);
- }
-
- // Configure.
- //
-
- // config.cxx
- //
- if (first)
- {
- auto p (config::required (r, "config.cxx", "g++"));
-
- // If we actually set a new value, test it by trying to execute.
- //
- if (p.second)
- {
- const string& cxx (as<string> (p.first));
- const char* args[] = {cxx.c_str (), "-dumpversion", nullptr};
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "test " << cxx;
-
- string ver;
- try
- {
- process pr (args, 0, -1); // Open pipe to stdout.
- ifdstream is (pr.in_ofd);
-
- bool r (getline (is, ver));
-
- if (!r)
- fail << "unexpected output from " << cxx;
-
- if (!pr.wait ())
- throw failed ();
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << cxx << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
-
- if (verb >= 2)
- text << cxx << " " << ver;
- }
- }
-
- // config.cxx.{p,c,l}options
- // config.cxx.libs
- //
- // These are optional. We also merge them into the corresponding
- // cxx.* variables.
- //
- // The merging part gets a bit tricky if this module has already
- // been loaded in one of the outer scopes. By doing the straight
- // append we would just be repeating the same options over and
- // over. So what we are going to do is only append to a value if
- // it came from this scope. Then the usage for merging becomes:
- //
- // cxx.coptions = <overridable options> # Note: '='.
- // using cxx
- // cxx.coptions += <overriding options> # Note: '+='.
- //
- if (const value& v = config::optional (r, "config.cxx.poptions"))
- b.assign ("cxx.poptions") += as<strings> (v);
-
- if (const value& v = config::optional (r, "config.cxx.coptions"))
- b.assign ("cxx.coptions") += as<strings> (v);
-
- if (const value& v = config::optional (r, "config.cxx.loptions"))
- b.assign ("cxx.loptions") += as<strings> (v);
-
- if (const value& v = config::optional (r, "config.cxx.libs"))
- b.assign ("cxx.libs") += as<strings> (v);
-
- // Configure "installability" of our target types.
- //
- {
- using build::install::path;
-
- path<hxx> (b, dir_path ("include")); // Install into install.include.
- path<ixx> (b, dir_path ("include"));
- path<txx> (b, dir_path ("include"));
- path<h> (b, dir_path ("include"));
- }
-
- return true;
- }
- }
-}
diff --git a/build/cxx/target b/build/cxx/target
deleted file mode 100644
index fe27c79..0000000
--- a/build/cxx/target
+++ /dev/null
@@ -1,78 +0,0 @@
-// file : build/cxx/target -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CXX_TARGET
-#define BUILD_CXX_TARGET
-
-#include <build/target>
-
-namespace build
-{
- namespace cxx
- {
- class hxx: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class ixx: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class txx: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class cxx: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- //@@ TMP
- //
- class h: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class c: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
- }
-}
-
-#endif // BUILD_CXX_TARGET
diff --git a/build/cxx/target.cxx b/build/cxx/target.cxx
deleted file mode 100644
index 314f758..0000000
--- a/build/cxx/target.cxx
+++ /dev/null
@@ -1,81 +0,0 @@
-// file : build/cxx/target.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cxx/target>
-
-using namespace std;
-
-namespace build
-{
- namespace cxx
- {
- constexpr const char ext_var[] = "extension";
-
- constexpr const char hxx_ext_def[] = "hxx";
- const target_type hxx::static_type
- {
- "hxx",
- &file::static_type,
- &target_factory<hxx>,
- &target_extension_var<ext_var, hxx_ext_def>,
- &search_file,
- false
- };
-
- constexpr const char ixx_ext_def[] = "ixx";
- const target_type ixx::static_type
- {
- "ixx",
- &file::static_type,
- &target_factory<ixx>,
- &target_extension_var<ext_var, ixx_ext_def>,
- &search_file,
- false
- };
-
- constexpr const char txx_ext_def[] = "txx";
- const target_type txx::static_type
- {
- "txx",
- &file::static_type,
- &target_factory<txx>,
- &target_extension_var<ext_var, txx_ext_def>,
- &search_file,
- false
- };
-
- constexpr const char cxx_ext_def[] = "cxx";
- const target_type cxx::static_type
- {
- "cxx",
- &file::static_type,
- &target_factory<cxx>,
- &target_extension_var<ext_var, cxx_ext_def>,
- &search_file,
- false
- };
-
- constexpr const char h_ext_def[] = "h";
- const target_type h::static_type
- {
- "h",
- &file::static_type,
- &target_factory<h>,
- &target_extension_var<ext_var, h_ext_def>,
- &search_file,
- false
- };
-
- constexpr const char c_ext_def[] = "c";
- const target_type c::static_type
- {
- "c",
- &file::static_type,
- &target_factory<c>,
- &target_extension_var<ext_var, c_ext_def>,
- &search_file,
- false
- };
- }
-}
diff --git a/build/cxx/utility b/build/cxx/utility
deleted file mode 100644
index 53e80e1..0000000
--- a/build/cxx/utility
+++ /dev/null
@@ -1,37 +0,0 @@
-// file : build/cxx/utility -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_CXX_UTILITY
-#define BUILD_CXX_UTILITY
-
-#include <string>
-
-#include <build/types>
-#include <build/target>
-
-#include <build/config/utility>
-
-namespace build
-{
- namespace cxx
- {
- using config::append_options;
-
- // T is either target or scope.
- //
- template <typename T>
- void
- append_std (cstrings& args, T&, std::string& storage);
-
- // Append library options from one of the cxx.export.* variables
- // recursively, prerequisite libraries first.
- //
- void
- append_lib_options (cstrings& args, target&, const char* variable);
- }
-}
-
-#include <build/cxx/utility.txx>
-
-#endif // BUILD_CXX_UTILITY
diff --git a/build/cxx/utility.cxx b/build/cxx/utility.cxx
deleted file mode 100644
index 830ee76..0000000
--- a/build/cxx/utility.cxx
+++ /dev/null
@@ -1,29 +0,0 @@
-// file : build/cxx/utility.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/cxx/utility>
-
-#include <build/bin/target>
-
-using namespace std;
-
-namespace build
-{
- namespace cxx
- {
- void
- append_lib_options (cstrings& args, target& l, const char* var)
- {
- using namespace bin;
-
- for (target* t: l.prerequisite_targets)
- {
- if (t->is_a<lib> () || t->is_a<liba> () || t->is_a<libso> ())
- append_lib_options (args, *t, var);
- }
-
- append_options (args, l, var);
- }
- }
-}
diff --git a/build/cxx/utility.txx b/build/cxx/utility.txx
deleted file mode 100644
index 561a54e..0000000
--- a/build/cxx/utility.txx
+++ /dev/null
@@ -1,35 +0,0 @@
-// file : build/cxx/utility.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-using namespace std;
-
-namespace build
-{
- namespace cxx
- {
- template <typename T>
- void
- append_std (cstrings& args, T& t, std::string& s)
- {
- if (auto l = t["cxx.std"])
- {
- const std::string& v (as<string> (*l));
-
- // Translate 11 to 0x and 14 to 1y for compatibility with
- // older versions of the compiler.
- //
- s = "-std=c++";
-
- if (v == "11")
- s += "0x";
- else if (v == "14")
- s += "1y";
- else
- s += v;
-
- args.push_back (s.c_str ());
- }
- }
- }
-}
diff --git a/build/diagnostics b/build/diagnostics
deleted file mode 100644
index d5b0b3d..0000000
--- a/build/diagnostics
+++ /dev/null
@@ -1,402 +0,0 @@
-// file : build/diagnostics -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_DIAGNOSTICS
-#define BUILD_DIAGNOSTICS
-
-#include <cstddef> // size_t
-#include <cstdint>
-#include <utility>
-#include <cassert>
-#include <sstream>
-#include <ostream>
-#include <exception>
-#include <type_traits>
-
-#include <butl/path>
-
-#include <build/types>
-#include <build/path-io>
-
-namespace build
-{
- struct diag_record;
-
- // Throw this exception to terminate the build. The handler should
- // assume that the diagnostics has already been issued.
- //
- class failed: public std::exception {};
-
- // Flag that indicates whether paths should be inserted relative
- // into this stream.
- //
- extern const int relative_index;
-
- inline bool
- relative (std::ostream& os) {return os.iword (relative_index);}
-
- inline void
- relative (std::ostream& os, bool v) {os.iword (relative_index) = v ? 1 : 0;}
-
- // Print process commmand line. If the number of elements is specified
- // (or the second version is used), then it will print the piped multi-
- // process command line, if present. In this case, the expected format
- // is as follows:
- //
- // name1 arg arg ... nullptr
- // name2 arg arg ... nullptr
- // ...
- // nameN arg arg ... nullptr nullptr
- //
- void
- print_process (diag_record&, const char* const* args, std::size_t n = 0);
-
- void
- print_process (const char* const* args, std::size_t n = 0);
-
- inline void
- print_process (diag_record& dr, const cstrings& args)
- {
- print_process (dr, args.data (), args.size ());
- }
-
- inline void
- print_process (const cstrings& args)
- {
- print_process (args.data (), args.size ());
- }
-
- // Verbosity level.
- //
- // 0 - disabled
- // 1 - high-level information messages
- // 2 - essential underlying commands that are being executed
- // 3 - all underlying commands that are being executed
- // 4 - information helpful to the user (e.g., why a rule did not match)
- // 5 - information helpful to the developer
- // 6 - even more detailed information
- //
- // While uint8 is more than enough, use uint16 for the ease of printing.
- //
- extern std::uint16_t verb;
-
- template <typename F> inline void level1 (const F& f) {if (verb >= 1) f ();}
- template <typename F> inline void level2 (const F& f) {if (verb >= 2) f ();}
- template <typename F> inline void level3 (const F& f) {if (verb >= 3) f ();}
- template <typename F> inline void level4 (const F& f) {if (verb >= 4) f ();}
- template <typename F> inline void level5 (const F& f) {if (verb >= 5) f ();}
- template <typename F> inline void level6 (const F& f) {if (verb >= 6) f ();}
-
- // Diagnostic facility, base infrastructure (potentially reusable).
- //
- extern std::ostream* diag_stream;
-
- template <typename> struct diag_prologue;
- template <typename> struct diag_mark;
-
- typedef void (*diag_epilogue) (const diag_record&);
-
- struct diag_record
- {
- template <typename T>
- friend const diag_record&
- operator<< (const diag_record& r, const T& x)
- {
- r.os_ << x;
- return r;
- }
-
- diag_record (): empty_ (true), epilogue_ (nullptr) {}
-
- template <typename B>
- explicit
- diag_record (const diag_prologue<B>& p)
- : empty_ (true), epilogue_ (nullptr) { *this << p;}
-
- template <typename B>
- explicit
- diag_record (const diag_mark<B>& m)
- : empty_ (true), epilogue_ (nullptr) { *this << m;}
-
- ~diag_record () noexcept(false);
-
- void
- append (diag_epilogue e) const
- {
- if (e != nullptr)
- {
- assert (epilogue_ == nullptr); // No multiple epilogues support.
- epilogue_ = e;
- }
-
- if (empty_)
- empty_ = false;
- else
- os_ << "\n ";
- }
-
- // Move constructible-only type.
- //
- /*
- @@ libstdc++ doesn't yet have the ostringstream move support.
-
- diag_record (diag_record&& r)
- : os_ (std::move (r.os_))
- {
- empty_ = r.empty_;
- r.empty_ = true;
-
- epilogue_ = r.epilogue_;
- r.epilogue_ = nullptr;
- }
- */
-
- diag_record (diag_record&& r)
- {
- empty_ = r.empty_;
- epilogue_ = r.epilogue_;
-
- if (!empty_)
- {
- assert (false); //@@ Relative flag will not be transferred.
- os_ << r.os_.str ();
-
- r.empty_ = true;
- r.epilogue_ = nullptr;
- }
- }
-
- diag_record& operator= (diag_record&&) = delete;
-
- diag_record (const diag_record&) = delete;
- diag_record& operator= (const diag_record&) = delete;
-
- public:
- mutable std::ostringstream os_;
-
- private:
- mutable bool empty_;
- mutable diag_epilogue epilogue_;
- };
-
- template <typename B>
- struct diag_prologue: B
- {
- diag_prologue (diag_epilogue e = nullptr): B (), epilogue_ (e) {}
-
- template <typename... A>
- diag_prologue (A&&... a)
- : B (std::forward<A> (a)...), epilogue_ (nullptr) {}
-
- template <typename... A>
- diag_prologue (diag_epilogue e, A&&... a)
- : B (std::forward<A> (a)...), epilogue_ (e) {}
-
- template <typename T>
- diag_record
- operator<< (const T& x) const
- {
- diag_record r;
- r.append (epilogue_);
- B::operator() (r);
- r << x;
- return r;
- }
-
- friend const diag_record&
- operator<< (const diag_record& r, const diag_prologue& p)
- {
- r.append (p.epilogue_);
- p (r);
- return r;
- }
-
- private:
- diag_epilogue epilogue_;
- };
-
- template <typename B>
- struct diag_mark: B
- {
- diag_mark (): B () {}
-
- template <typename... A>
- diag_mark (A&&... a): B (std::forward<A> (a)...) {}
-
- template <typename T>
- diag_record
- operator<< (const T& x) const
- {
- return B::operator() () << x;
- }
-
- friend const diag_record&
- operator<< (const diag_record& r, const diag_mark& m)
- {
- return r << m ();
- }
- };
-
- // Diagnostic facility, project specifics.
- //
- struct simple_prologue_base
- {
- explicit
- simple_prologue_base (const char* type, const char* name, bool rel)
- : type_ (type), name_ (name), relative_ (rel) {}
-
- void
- operator() (const diag_record& r) const;
-
- private:
- const char* type_;
- const char* name_;
- const bool relative_;
- };
- typedef diag_prologue<simple_prologue_base> simple_prologue;
-
- class location
- {
- public:
- location () {}
- location (const char* f, std::uint64_t l, std::uint64_t c)
- : file (f), line (l), column (c) {}
-
- const char* file;
- std::uint64_t line;
- std::uint64_t column;
- };
-
- struct location_prologue_base
- {
- location_prologue_base (const char* type,
- const char* name,
- const location& l,
- bool rel)
- : type_ (type), name_ (name), loc_ (l), relative_ (rel) {}
-
- void
- operator() (const diag_record& r) const;
-
- private:
- const char* type_;
- const char* name_;
- const location loc_;
- const bool relative_;
- };
- typedef diag_prologue<location_prologue_base> location_prologue;
-
- // Here is the absolute/relative path rationale: we want it absolute
- // in the error/warning/info streams to give the user the complete
- // picture. But in the text stream (e.g., command lines), we print
- // relative unless verbosity is greater than 1.
- //
- struct basic_mark_base
- {
- explicit
- basic_mark_base (const char* type,
- const char* name = nullptr,
- const void* data = nullptr)
- : type_ (type), name_ (name), data_ (data) {}
-
- simple_prologue
- operator() () const
- {
- return simple_prologue (type_, name_, false);
- }
-
- location_prologue
- operator() (const location& l) const
- {
- return location_prologue (type_, name_, l, false);
- }
-
- template <typename L>
- location_prologue
- operator() (const L& l) const
- {
- return location_prologue (type_, name_, get_location (l, data_), false);
- }
-
- protected:
- const char* type_;
- const char* name_;
- const void* data_;
- };
- typedef diag_mark<basic_mark_base> basic_mark;
-
- extern const basic_mark error;
- extern const basic_mark warn;
- extern const basic_mark info;
-
- // text
- //
- struct text_mark_base: basic_mark_base
- {
- text_mark_base (): basic_mark_base (nullptr) {}
-
- simple_prologue
- operator() () const
- {
- return simple_prologue (type_, name_, verb <= 1);
- }
- };
- typedef diag_mark<text_mark_base> text_mark;
-
- extern const text_mark text;
-
- // trace
- //
- struct trace_mark_base: basic_mark_base
- {
- explicit
- trace_mark_base (const char* name, const void* data = nullptr)
- : basic_mark_base ("trace", name, data) {}
- };
- typedef diag_mark<trace_mark_base> trace_mark;
-
- typedef trace_mark tracer;
-
- // fail
- //
- template <typename E>
- struct fail_mark_base
- {
- explicit
- fail_mark_base (const void* data = nullptr): data_ (data) {}
-
- simple_prologue
- operator() () const
- {
- return simple_prologue (&epilogue, "error", nullptr, false);
- }
-
- location_prologue
- operator() (const location& l) const
- {
- return location_prologue (&epilogue, "error", nullptr, l, false);
- }
-
- template <typename L>
- location_prologue
- operator() (const L& l) const
- {
- return location_prologue (
- &epilogue, "error", nullptr, get_location (l, data_), false);
- }
-
- static void
- epilogue (const diag_record&) {throw E ();}
-
- private:
- const void* data_;
- };
-
- template <typename E>
- using fail_mark = diag_mark<fail_mark_base<E>>;
-
- extern const fail_mark<failed> fail;
-}
-
-#endif // BUILD_DIAGNOSTICS
diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx
deleted file mode 100644
index a450301..0000000
--- a/build/diagnostics.cxx
+++ /dev/null
@@ -1,125 +0,0 @@
-// file : build/diagnostics.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/diagnostics>
-
-#include <cstring> // strchr()
-#include <iostream>
-
-#include <build/utility>
-
-using namespace std;
-
-namespace build
-{
- // Relative stream.
- //
- const int relative_index = ostream::xalloc ();
-
- void
- print_process (const char* const* args, size_t n)
- {
- diag_record r (text);
- print_process (r, args, n);
- }
-
- void
- print_process (diag_record& r, const char* const* args, size_t n)
- {
- size_t m (0);
- const char* const* p (args);
- do
- {
- if (m != 0)
- r << " |"; // Trailing space will be added inside the loop.
-
- for (m++; *p != nullptr; p++, m++)
- {
- if (p != args)
- r << ' ';
-
- // Quote if empty or contains spaces.
- //
- bool q (**p == '\0' || strchr (*p, ' ') != nullptr);
-
- if (q)
- r << '"';
-
- r << *p;
-
- if (q)
- r << '"';
- }
-
- if (m < n) // Can we examine the next element?
- {
- p++;
- m++;
- }
-
- } while (*p != nullptr);
- }
-
- // Diagnostics verbosity level.
- //
- uint16_t verb;
-
- // Diagnostic facility, base infrastructure.
- //
- ostream* diag_stream = &cerr;
-
- diag_record::
- ~diag_record () noexcept(false)
- {
- // Don't flush the record if this destructor was called as part of
- // the stack unwinding. Right now this means we cannot use this
- // mechanism in destructors, which is not a big deal, except for
- // one place: exception_guard. So for now we are going to have
- // this ugly special check which we will be able to get rid of
- // once C++17 uncaught_exceptions() becomes available.
- //
- if (!empty_ && (!std::uncaught_exception () || exception_unwinding_dtor))
- {
- *diag_stream << os_.str () << std::endl;
-
- if (epilogue_ != nullptr)
- epilogue_ (*this); // Can throw.
- }
- }
-
- // Diagnostic facility, project specifics.
- //
-
- void simple_prologue_base::
- operator() (const diag_record& r) const
- {
- relative (r.os_, relative_);
-
- if (type_ != nullptr)
- r << type_ << ": ";
-
- if (name_ != nullptr)
- r << name_ << ": ";
- }
-
- void location_prologue_base::
- operator() (const diag_record& r) const
- {
- relative (r.os_, relative_);
-
- r << loc_.file << ':' << loc_.line << ':' << loc_.column << ": ";
-
- if (type_ != nullptr)
- r << type_ << ": ";
-
- if (name_ != nullptr)
- r << name_ << ": ";
- }
-
- const basic_mark error ("error");
- const basic_mark warn ("warning");
- const basic_mark info ("info");
- const text_mark text;
- const fail_mark<failed> fail;
-}
diff --git a/build/dist/module b/build/dist/module
deleted file mode 100644
index f2e024a..0000000
--- a/build/dist/module
+++ /dev/null
@@ -1,26 +0,0 @@
-// file : build/dist/module -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_DIST_MODULE
-#define BUILD_DIST_MODULE
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/module>
-
-namespace build
-{
- namespace dist
- {
- extern "C" void
- dist_boot (scope&, const location&, unique_ptr<module>&);
-
- extern "C" bool
- dist_init (
- scope&, scope&, const location&, unique_ptr<module>&, bool, bool);
- }
-}
-
-#endif // BUILD_DIST_MODULE
diff --git a/build/dist/module.cxx b/build/dist/module.cxx
deleted file mode 100644
index f39ef18..0000000
--- a/build/dist/module.cxx
+++ /dev/null
@@ -1,142 +0,0 @@
-// file : build/dist/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/dist/module>
-
-#include <build/scope>
-#include <build/file>
-#include <build/diagnostics>
-
-#include <build/config/utility>
-
-#include <build/dist/rule>
-#include <build/dist/operation>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace dist
- {
- static rule rule_;
-
- extern "C" void
- dist_boot (scope& r, const location&, unique_ptr<module>&)
- {
- tracer trace ("dist::boot");
-
- level5 ([&]{trace << "for " << r.out_path ();});
-
- // Register meta-operation.
- //
- r.meta_operations.insert (dist_id, dist);
- }
-
- extern "C" bool
- dist_init (scope& r,
- scope&,
- const location& l,
- unique_ptr<module>&,
- bool first,
- bool)
- {
- tracer trace ("dist::init");
-
- if (!first)
- {
- warn (l) << "multiple dist module initializations";
- return true;
- }
-
- const dir_path& out_root (r.out_path ());
- level5 ([&]{trace << "for " << out_root;});
-
- // Enter module variables.
- //
- if (first)
- {
- auto& v (var_pool);
-
- v.find ("dist", bool_type);
-
- v.find ("dist.package", string_type);
-
- v.find ("dist.root", dir_path_type);
- v.find ("config.dist.root", dir_path_type);
-
- //@@ VAR type
- //
- v.find ("dist.cmd", string_type);
- v.find ("config.dist.cmd", string_type);
-
- v.find ("dist.archives", strings_type);
- v.find ("config.dist.archives", strings_type);
- }
-
- // Register our wildcard rule. Do it explicitly for the alias
- // to prevent something like insert<target>(dist_id, test_id)
- // taking precedence.
- //
- r.rules.insert<target> (dist_id, 0, "dist", rule_);
- r.rules.insert<alias> (dist_id, 0, "dist.alias", rule_);
-
- // Configuration.
- //
- // Note that we don't use any defaults for root -- the location
- // must be explicitly specified or we will complain if and when
- // we try to dist.
- //
- using namespace config;
-
- bool s (specified (r, "config.dist"));
-
- // dist.root
- //
- {
- value& v (r.assign ("dist.root"));
-
- if (s)
- {
- const value& cv (optional_absolute (r, "config.dist.root"));
-
- if (cv && !cv.empty ())
- v = cv;
- }
- }
-
- // dist.cmd
- //
- {
- value& v (r.assign ("dist.cmd"));
-
- if (s)
- {
- const value& cv (required (r, "config.dist.cmd", "install").first);
-
- if (cv && !cv.empty ())
- v = cv;
- }
- else
- v = "install";
- }
-
- // dist.archives
- //
- {
- value& v (r.assign ("dist.archives"));
-
- if (s)
- {
- const value& cv (optional (r, "config.dist.archives"));
-
- if (cv && !cv.empty ())
- v = cv;
- }
- }
-
- return true;
- }
- }
-}
diff --git a/build/dist/operation b/build/dist/operation
deleted file mode 100644
index c7aa014..0000000
--- a/build/dist/operation
+++ /dev/null
@@ -1,18 +0,0 @@
-// file : build/dist/operation -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_DIST_OPERATION
-#define BUILD_DIST_OPERATION
-
-#include <build/operation>
-
-namespace build
-{
- namespace dist
- {
- extern meta_operation_info dist;
- }
-}
-
-#endif // BUILD_DIST_OPERATION
diff --git a/build/dist/operation.cxx b/build/dist/operation.cxx
deleted file mode 100644
index 28a64fa..0000000
--- a/build/dist/operation.cxx
+++ /dev/null
@@ -1,459 +0,0 @@
-// file : build/dist/operation.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/dist/operation>
-
-#include <cassert>
-
-#include <butl/process>
-#include <butl/filesystem>
-
-#include <build/file>
-#include <build/dump>
-#include <build/scope>
-#include <build/target>
-#include <build/context>
-#include <build/algorithm>
-#include <build/diagnostics>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace dist
- {
- static void
- dist_meta_operation_pre ()
- {
- // Reset the dependency state so that we don't end up with stray
- // files from previous batches.
- //
- // @@ This is called too late, after we have bootstrapped the
- // project.
- //
- //reset ();
- }
-
- static operation_id
- dist_operation_pre (operation_id o)
- {
- if (o != default_id)
- fail << "explicit operation specified for dist meta-operation";
-
- return o;
- }
-
- static void
- dist_match (action, action_targets&)
- {
- // Don't match anything -- see execute ().
- }
-
- // install -d <dir>
- //
- static void
- install (const string& cmd, const dir_path&);
-
- // install <file> <dir>
- //
- static void
- install (const string& cmd, file&, const dir_path&);
-
- // cd <root> && tar|zip ... <pkg>.<ext> <pkg>
- //
- static void
- archive (const dir_path& root, const string& pkg, const string& ext);
-
- static void
- dist_execute (action, const action_targets& ts, bool)
- {
- tracer trace ("dist_execute");
-
- // For now we assume all the targets are from the same project.
- //
- target& t (*static_cast<target*> (ts[0]));
- scope* rs (t.base_scope ().root_scope ());
-
- if (rs == nullptr)
- fail << "out of project target " << t;
-
- const dir_path& out_root (rs->out_path ());
- const dir_path& src_root (rs->src_path ());
-
- if (out_root == src_root)
- fail << "in-tree distribution of target " << t <<
- info << "distribution requires out-of-tree build";
-
- // Make sure we have the necessary configuration before
- // we get down to business.
- //
- auto l (rs->vars["dist.root"]);
-
- if (!l || l->empty ())
- fail << "unknown root distribution directory" <<
- info << "did you forget to specify config.dist.root?";
-
- const dir_path& dist_root (as<dir_path> (*l));
-
- if (!dir_exists (dist_root))
- fail << "root distribution directory " << dist_root
- << " does not exist";
-
- l = rs->vars["dist.package"];
-
- if (!l || l->empty ())
- fail << "unknown distribution package name" <<
- info << "did you forget to set dist.package?";
-
- const string& dist_package (as<string> (*l));
- const string& dist_cmd (as<string> (*rs->vars["dist.cmd"]));
-
- // Get the list of operations supported by this project. Skip
- // default_id.
- //
- for (operations::size_type id (default_id + 1);
- id < rs->operations.size ();
- ++id)
- {
- const operation_info* oi (rs->operations[id]);
- if (oi == nullptr)
- continue;
-
- // Note that we are not calling operation_pre/post() callbacks
- // here since the meta operation is dist and we know what we
- // are doing.
- //
- current_inner_oif = oi;
- current_outer_oif = nullptr;
- current_mode = oi->mode;
- dependency_count = 0;
-
- action a (dist_id, id);
-
- if (verb >= 6)
- dump (a);
-
- for (void* v: ts)
- {
- target& t (*static_cast<target*> (v));
-
- if (rs != t.base_scope ().root_scope ())
- fail << "out of project target " << t;
-
- level5 ([&]{trace << diag_doing (a, t);});
-
- match (a, t);
- }
-
- if (verb >= 6)
- dump (a);
- }
-
- // Add buildfiles that are not normally loaded as part of the
- // project, for example, the export stub. They will still be
- // ignored on the next step if the user explicitly marked them
- // nodist.
- //
- auto add_adhoc = [&src_root, &trace] (const dir_path& d, const char* f)
- {
- path p (d / path (f));
- if (file_exists (p))
- {
- const char* e (p.extension ());
- targets.insert<buildfile> (
- p.directory (),
- p.leaf ().base ().string (),
- &extension_pool.find (e == nullptr ? "" : e), // Specified.
- trace);
- }
- };
-
- add_adhoc (src_root, "build/export.build");
-
- // The same for subprojects that have been loaded.
- //
- if (auto l = rs->vars["subprojects"])
- {
- for (auto p: as<subprojects> (*l))
- {
- const dir_path& pd (p.second);
- dir_path out_nroot (out_root / pd);
- scope& nrs (scopes.find (out_nroot));
-
- if (nrs.out_path () != out_nroot) // This subproject not loaded.
- continue;
-
- const dir_path& src_nroot (nrs.src_path ());
-
- if (!src_nroot.sub (src_root)) // Not a strong amalgamation.
- continue;
-
- add_adhoc (src_nroot, "build/export.build");
- }
- }
-
- // Collect the files. We want to take the snapshot of targets
- // since updating some of them may result in more targets being
- // entered.
- //
- action_targets files;
- const variable& dist_var (var_pool.find ("dist"));
-
- for (const auto& pt: targets)
- {
- file* ft (pt->is_a<file> ());
-
- if (ft == nullptr) // Not a file.
- continue;
-
- if (ft->dir.sub (src_root))
- {
- // Include unless explicitly excluded.
- //
- auto l ((*ft)[dist_var]);
-
- if (l && !as<bool> (*l))
- level5 ([&]{trace << "excluding " << *ft;});
- else
- files.push_back (ft);
-
- continue;
- }
-
- if (ft->dir.sub (out_root))
- {
- // Exclude unless explicitly included.
- //
- auto l ((*ft)[dist_var]);
-
- if (l && as<bool> (*l))
- {
- level5 ([&]{trace << "including " << *ft;});
- files.push_back (ft);
- }
-
- continue;
- }
- }
-
- // Make sure what we need to distribute is up to date.
- //
- {
- if (perform.meta_operation_pre != nullptr)
- perform.meta_operation_pre ();
-
- current_mif = &perform;
-
- if (perform.operation_pre != nullptr)
- perform.operation_pre (update_id);
-
- current_inner_oif = &update;
- current_outer_oif = nullptr;
- current_mode = update.mode;
- dependency_count = 0;
-
- action a (perform_id, update_id);
-
- perform.match (a, files);
- perform.execute (a, files, true); // Run quiet.
-
- if (perform.operation_post != nullptr)
- perform.operation_post (update_id);
-
- if (perform.meta_operation_post != nullptr)
- perform.meta_operation_post ();
- }
-
- dir_path td (dist_root / dir_path (dist_package));
-
- // Clean up the target directory.
- //
- // @@ Not for incremental dist?
- //
- if (build::rmdir_r (td) == rmdir_status::not_empty)
- fail << "unable to clean target directory " << td;
-
- install (dist_cmd, td);
-
- // Copy over all the files.
- //
- for (void* v: files)
- {
- file& t (*static_cast<file*> (v));
-
- // Figure out where this file is inside the target directory.
- //
- dir_path d (td);
- d /= t.dir.sub (src_root)
- ? t.dir.leaf (src_root)
- : t.dir.leaf (out_root);
-
- if (!dir_exists (d))
- install (dist_cmd, d);
-
- install (dist_cmd, t, d);
- }
-
- // Archive if requested.
- //
- if (auto l = rs->vars["dist.archives"])
- {
- for (const string& e: as<strings> (*l))
- archive (dist_root, dist_package, e);
- }
- }
-
- // install -d <dir>
- //
- static void
- install (const string& cmd, const dir_path& d)
- {
- path reld (relative (d));
-
- cstrings args {cmd.c_str (), "-d"};
-
- args.push_back ("-m");
- args.push_back ("755");
- args.push_back (reld.string ().c_str ());
- args.push_back (nullptr);
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "dist -d " << d;
-
- try
- {
- process pr (args.data ());
-
- if (!pr.wait ())
- throw failed ();
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- }
-
- // install <file> <dir>
- //
- static void
- install (const string& cmd, file& t, const dir_path& d)
- {
- path reld (relative (d));
- path relf (relative (t.path ()));
-
- cstrings args {cmd.c_str ()};
-
- // Preserve timestamps. This could becomes important if, for
- // example, we have pre-generated sources. Note that the
- // install-sh script doesn't support this option, while both
- // Linux and BSD install's do.
- //
- args.push_back ("-p");
-
- // Assume the file is executable if the owner has execute
- // permission, in which case we make it executable for
- // everyone.
- //
- args.push_back ("-m");
- args.push_back (
- (path_permissions (t.path ()) & permissions::xu) == permissions::xu
- ? "755"
- : "644");
-
- args.push_back (relf.string ().c_str ());
- args.push_back (reld.string ().c_str ());
- args.push_back (nullptr);
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "dist " << t;
-
- try
- {
- process pr (args.data ());
-
- if (!pr.wait ())
- throw failed ();
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- }
-
- static void
- archive (const dir_path& root, const string& pkg, const string& e)
- {
- string a (pkg + '.' + e);
-
- // Delete old archive for good measure.
- //
- path ap (root / path (a));
- if (file_exists (ap))
- rmfile (ap);
-
- // Use zip for .zip archives. Everything else goes to tar in the
- // auto-compress mode (-a).
- //
- cstrings args;
- if (e == "zip")
- args = {"zip", "-rq", a.c_str (), pkg.c_str (), nullptr};
- else
- args = {"tar", "-a", "-cf", a.c_str (), pkg.c_str (), nullptr};
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << args[0] << " " << ap;
-
- try
- {
- // Change child's working directory to dist_root.
- //
- process pr (root.string ().c_str (), args.data ());
-
- if (!pr.wait ())
- throw failed ();
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- }
-
- meta_operation_info dist {
- "dist",
- "distribute",
- "distributing",
- "has nothing to distribute", // We cannot "be distributed".
- &dist_meta_operation_pre,
- &dist_operation_pre,
- &load, // normal load
- &search, // normal search
- &dist_match,
- &dist_execute,
- nullptr, // operation post
- nullptr // meta-operation post
- };
- }
-}
diff --git a/build/dist/rule b/build/dist/rule
deleted file mode 100644
index ffb2227..0000000
--- a/build/dist/rule
+++ /dev/null
@@ -1,29 +0,0 @@
-// file : build/dist/rule -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_DIST_RULE
-#define BUILD_DIST_RULE
-
-#include <build/rule>
-#include <build/types>
-#include <build/target>
-#include <build/operation>
-
-namespace build
-{
- namespace dist
- {
- class rule: public build::rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string&) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
- };
- }
-}
-
-#endif // BUILD_DIST_RULE
diff --git a/build/dist/rule.cxx b/build/dist/rule.cxx
deleted file mode 100644
index 80e7b05..0000000
--- a/build/dist/rule.cxx
+++ /dev/null
@@ -1,55 +0,0 @@
-// file : build/dist/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/dist/rule>
-
-#include <build/scope>
-#include <build/target>
-#include <build/algorithm>
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- namespace dist
- {
- match_result rule::
- match (action, target& t, const std::string&) const
- {
- return t; // We always match.
- }
-
- recipe rule::
- apply (action a, target& t, const match_result&) const
- {
- const dir_path& out_root (t.root_scope ().out_path ());
-
- auto r (group_prerequisite_members (a, t, false));
- for (auto i (r.begin ()); i != r.end (); ++i)
- {
- prerequisite_member p (*i);
-
- // Skip prerequisites imported from other projects.
- //
- if (p.proj () != nullptr)
- continue;
-
- // If we can, go inside see-through groups.
- //
- if (p.type ().see_through && i.enter_group ())
- continue;
-
- target& pt (p.search ());
-
- // Don't match targets that are outside of our project.
- //
- if (pt.dir.sub (out_root))
- build::match (a, pt);
- }
-
- return noop_recipe; // We will never be executed.
- }
- }
-}
diff --git a/build/dump b/build/dump
deleted file mode 100644
index 8bc548e..0000000
--- a/build/dump
+++ /dev/null
@@ -1,18 +0,0 @@
-// file : build/dump -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_DUMP
-#define BUILD_DUMP
-
-#include <build/operation>
-
-namespace build
-{
- // Dump the state pertaining to the specified action.
- //
- void
- dump (action);
-}
-
-#endif // BUILD_DUMP
diff --git a/build/dump.cxx b/build/dump.cxx
deleted file mode 100644
index 62e032b..0000000
--- a/build/dump.cxx
+++ /dev/null
@@ -1,253 +0,0 @@
-// file : build/dump.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/dump>
-
-#include <string>
-#include <cassert>
-
-#include <build/scope>
-#include <build/target>
-#include <build/variable>
-#include <build/context>
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- static void
- dump_variable (ostream& os, const variable& var, const value& val)
- {
- os << var.name << " = ";
-
- if (val.null ())
- os << "[null]";
- else
- os << val.data_;
- }
-
- static void
- dump_variables (ostream& os, string& ind, const variable_map& vars)
- {
- for (const auto& e: vars)
- {
- os << endl
- << ind;
-
- dump_variable (os, e.first, e.second);
- }
- }
-
- static void
- dump_variables (ostream& os, string& ind, const variable_type_map& vtm)
- {
- for (const auto& vt: vtm)
- {
- const target_type& t (vt.first);
- const variable_pattern_map& vpm (vt.second);
-
- for (const auto& vp: vpm)
- {
- const string p (vp.first);
- const variable_map& vars (vp.second);
-
- os << endl
- << ind;
-
- if (t != target::static_type)
- os << t.name << '{';
-
- os << p;
-
- if (t != target::static_type)
- os << '}';
-
- os << ':';
-
- if (vars.size () == 1)
- {
- os << ' ';
- dump_variable (os, vars.begin ()->first, vars.begin ()->second);
- }
- else
- {
- os << endl
- << ind << '{';
- ind += " ";
- dump_variables (os, ind, vars);
- ind.resize (ind.size () - 2);
- os << endl
- << ind << '}';
- }
- }
- }
- }
-
- static void
- dump_target (ostream& os, string& ind, action a, const target& t)
- {
- os << ind << t;
-
- if (t.group != nullptr)
- os << "->" << *t.group;
-
- os << ':';
-
- for (const prerequisite& p: t.prerequisites)
- {
- os << ' ';
-
- // Print it as target if one has been cached.
- //
- if (p.target != nullptr)
- os << *p.target;
- else
- os << p;
- }
-
- // If the target has been matched to a rule, also print resolved
- // prerequisite targets.
- //
- if (t.recipe (a))
- {
- bool first (true);
- for (const target* pt: t.prerequisite_targets)
- {
- if (pt == nullptr) // Skipped.
- continue;
-
- os << (first ? " | " : " ") << *pt;
- first = false;
- }
- }
-
- // Print target-specific variables.
- //
- if (!t.vars.empty ())
- {
- os << endl
- << ind << '{';
- ind += " ";
- dump_variables (os, ind, t.vars);
- ind.resize (ind.size () - 2);
- os << endl
- << ind << '}';
- }
- }
-
- static void
- dump_scope (ostream& os,
- string& ind,
- action a,
- scope_map::const_iterator& i)
- {
- scope& p (*i->second);
- const dir_path& d (i->first);
- ++i;
-
- // We don't want the extra notations (e.g., ~/) provided by
- // diag_relative() since we want the path to be relative to
- // the outer scope.
- //
- os << ind << relative (d) << ":" << endl
- << ind << '{';
-
- const dir_path* orb (relative_base);
- relative_base = &d;
-
- ind += " ";
-
- bool vb (false), sb (false); // Variable/scope block.
-
- // Target type/pattern-sepcific variables.
- //
- if (!p.target_vars.empty ())
- {
- dump_variables (os, ind, p.target_vars);
- vb = true;
- }
-
- // Scope variables.
- //
- if (!p.vars.empty ())
- {
- if (vb)
- os << endl;
-
- dump_variables (os, ind, p.vars);
- vb = true;
- }
-
- // Nested scopes of which we are an immediate parent.
- //
- for (auto e (scopes.end ()); i != e && i->second->parent_scope () == &p;)
- {
- // See what kind of scope entry this is. It can be:
- //
- // 1. Out-of-project scope.
- // 2. In-project out entry.
- // 3. In-project src entry.
- //
- // We want to print #2 and #3 as a single, unified scope.
- //
- scope& s (*i->second);
- if (s.src_path_ != s.out_path_ && s.src_path_ == &i->first)
- {
- ++i;
- continue;
- }
-
- if (vb)
- {
- os << endl;
- vb = false;
- }
-
- if (sb)
- os << endl; // Extra newline between scope blocks.
-
- os << endl;
- dump_scope (os, ind, a, i);
- sb = true;
- }
-
- // Targets.
- //
- for (const auto& pt: targets)
- {
- const target& t (*pt);
-
- if (&p != &t.base_scope ())
- continue;
-
- if (vb || sb)
- {
- os << endl;
- vb = sb = false;
- }
-
- os << endl;
- dump_target (os, ind, a, t);
- }
-
- ind.resize (ind.size () - 2);
- relative_base = orb;
-
- os << endl
- << ind << '}';
- }
-
- void
- dump (action a)
- {
- auto i (scopes.begin ());
- assert (i->second == global_scope);
-
- string ind;
- ostream& os (*diag_stream);
- dump_scope (os, ind, a, i);
- os << endl;
- }
-}
diff --git a/build/file b/build/file
deleted file mode 100644
index c2b2535..0000000
--- a/build/file
+++ /dev/null
@@ -1,144 +0,0 @@
-// file : build/file -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_FILE
-#define BUILD_FILE
-
-#include <map>
-#include <string>
-
-#include <build/types>
-#include <build/scope>
-#include <build/variable> // list_value
-
-namespace build
-{
- class target;
- class location;
- class prerequisite_key;
-
- using subprojects = std::map<std::string, dir_path>;
-
- extern const dir_path build_dir; // build
- extern const dir_path bootstrap_dir; // build/bootstrap
-
- extern const path root_file; // build/root.build
- extern const path bootstrap_file; // build/bootstrap.build
- extern const path src_root_file; // build/bootstrap/src-root.build
-
- bool
- is_src_root (const dir_path&);
-
- 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);
-
- // As above but first check if this buildfile has already been
- // sourced for the base scope.
- //
- void
- source_once (const path& buildfile, scope& root, scope& base);
-
- // As above but checks against the specified scope rather than base.
- //
- void
- source_once (const path& buildfile, scope& root, scope& base, scope& once);
-
- // Create project's root scope. Only set the src_root variable if the
- // passed src_root value is not empty.
- //
- scope&
- create_root (const dir_path& out_root, const dir_path& src_root);
-
- // Setup root scope. Note that it assume the src_root variable
- // has already been set.
- //
- void
- setup_root (scope&);
-
- // Setup the base scope (set *_base variables, etc).
- //
- scope&
- setup_base (scope_map::iterator,
- const dir_path& out_base,
- const dir_path& src_base);
-
- // Bootstrap the project's root scope, the out part.
- //
- void
- bootstrap_out (scope& root);
-
- // Bootstrap the project's root scope, the src part. Return true if
- // we loaded anything (which confirms the src_root is not bogus).
- //
- bool
- bootstrap_src (scope& root);
-
- // 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 dir_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
- load_root_pre (scope& root);
-
- // Import has two phases: the first is triggered by the import
- // directive in the buildfile. It will try to find and load the
- // project. Failed that, it will return the project-qualified
- // name of the target which will be used to create a project-
- // qualified prerequisite. This gives the rule that will be
- // searching this prerequisite a chance to do some target-type
- // specific search. For example, a C++ link rule can search
- // for lib{} prerequisites in the C++ compiler default library
- // search paths (so that we end up with functionality identical
- // to -lfoo). If, however, the rule didn't do any of that (or
- // failed to find anything usable), it calls the standard
- // prerequisite search() function which sees this is a project-
- // qualified prerequisite and goes straight to the second phase
- // of import. Here, currently, we simply fail but in the future
- // this will be the place where we can call custom "last resort"
- // import hooks. For example, we can hook a package manager that
- // will say, "Hey, I see you are trying to import foo and I see
- // there is a package foo available in repository bar. Wanna
- // download and use it?"
- //
- names
- import (scope& base, name, const location&);
-
- target&
- import (const prerequisite_key&);
-}
-
-#include <build/file.ixx>
-
-#endif // BUILD_FILE
diff --git a/build/file.cxx b/build/file.cxx
deleted file mode 100644
index b4b1b77..0000000
--- a/build/file.cxx
+++ /dev/null
@@ -1,980 +0,0 @@
-// file : build/file.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/file>
-
-#include <fstream>
-#include <utility> // move()
-#include <system_error>
-
-#include <butl/filesystem>
-
-#include <build/scope>
-#include <build/context>
-#include <build/parser>
-#include <build/prerequisite>
-#include <build/diagnostics>
-
-#include <build/token>
-#include <build/lexer>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- const dir_path build_dir ("build");
- const dir_path bootstrap_dir ("build/bootstrap");
-
- const path root_file ("build/root.build");
- const path bootstrap_file ("build/bootstrap.build");
- const path src_root_file ("build/bootstrap/src-root.build");
-
- bool
- is_src_root (const dir_path& d)
- {
- // @@ Can we have root without bootstrap? I don't think so.
- //
- return file_exists (d / bootstrap_file) || file_exists (d / root_file);
- }
-
- bool
- is_out_root (const dir_path& d)
- {
- return file_exists (d / src_root_file);
- }
-
- 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 ();
- }
-
- static void
- source (const path& bf, scope& root, scope& base, bool boot)
- {
- tracer trace ("source");
-
- try
- {
- ifstream ifs (bf.string ());
- if (!ifs.is_open ())
- fail << "unable to open " << bf;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
-
- level5 ([&]{trace << "sourcing " << bf;});
-
- parser p (boot);
- p.parse_buildfile (ifs, bf, root, base);
- }
- catch (const ifstream::failure&)
- {
- fail << "unable to read buildfile " << bf;
- }
- }
-
- void
- source (const path& bf, scope& root, scope& base)
- {
- return source (bf, root, base, false);
- }
-
- void
- source_once (const path& bf, scope& root, scope& base, scope& once)
- {
- tracer trace ("source_once");
-
- if (!once.buildfiles.insert (bf).second)
- {
- level5 ([&]{trace << "skipping already sourced " << bf;});
- return;
- }
-
- source (bf, root, base);
- }
-
- scope&
- create_root (const dir_path& out_root, const dir_path& src_root)
- {
- auto i (scopes.insert (out_root, nullptr, true, true));
- scope& rs (*i->second);
-
- // Set out_path. src_path is set in setup_root() below.
- //
- if (rs.out_path_ != &i->first)
- {
- assert (rs.out_path_ == nullptr);
- rs.out_path_ = &i->first;
- }
-
- // Enter built-in meta-operation and operation names. Loading of
- // modules (via the src bootstrap; see below) can result in
- // additional meta/operations being added.
- //
- if (rs.meta_operations.empty ())
- {
- rs.meta_operations.insert (perform_id, perform);
-
- rs.operations.insert (default_id, default_);
- rs.operations.insert (update_id, update);
- rs.operations.insert (clean_id, clean);
- }
-
- // If this is already a root scope, verify that things are
- // consistent.
- //
- {
- value& v (rs.assign ("out_root"));
-
- if (!v)
- v = out_root;
- else
- {
- const dir_path& p (as<dir_path> (v));
-
- if (p != out_root)
- fail << "new out_root " << out_root << " does not match "
- << "existing " << p;
- }
- }
-
- if (!src_root.empty ())
- {
- value& v (rs.assign ("src_root"));
-
- if (!v)
- v = src_root;
- else
- {
- const dir_path& p (as<dir_path> (v));
-
- if (p != src_root)
- fail << "new src_root " << src_root << " does not match "
- << "existing " << p;
- }
- }
-
- return rs;
- }
-
- void
- setup_root (scope& s)
- {
- value& v (s.assign ("src_root"));
- assert (v);
-
- // Register and set src_path.
- //
- if (s.src_path_ == nullptr)
- s.src_path_ = &scopes.insert (as<dir_path> (v), &s, false, true)->first;
- }
-
- scope&
- setup_base (scope_map::iterator i,
- const dir_path& out_base,
- const dir_path& src_base)
- {
- scope& s (*i->second);
-
- // Set src/out_path. The key (i->first) can be either out_base
- // or src_base.
- //
- if (s.out_path_ == nullptr)
- {
- s.out_path_ =
- i->first == out_base
- ? &i->first
- : &scopes.insert (out_base, &s, true, false)->first;
- }
-
- if (s.src_path_ == nullptr)
- {
- s.src_path_ =
- i->first == src_base
- ? &i->first
- : &scopes.insert (src_base, &s, false, false)->first;
- }
-
- // Set src/out_base variables.
- //
- {
- value& v (s.assign ("out_base"));
-
- if (!v)
- v = out_base;
- else
- assert (as<dir_path> (v) == out_base);
- }
-
- {
- value& v (s.assign ("src_base"));
-
- if (!v)
- v = src_base;
- else
- assert (as<dir_path> (v) == src_base);
- }
-
- return s;
- }
-
- void
- bootstrap_out (scope& root)
- {
- path bf (root.out_path () / path ("build/bootstrap/src-root.build"));
-
- if (!file_exists (bf))
- return;
-
- //@@ TODO: if bootstrap files can source other bootstrap files
- // (the way to express dependecies), then we need a way to
- // prevent multiple sourcing. We handle it here but we still
- // need something like source_once (once [scope] source).
- //
- source_once (bf, root, root);
- }
-
- // Extract the specified variable value from a buildfile. It is
- // expected to be the first non-comment line and not to rely on
- // any variable expansion other than those from the global scope.
- //
- static value
- extract_variable (const path& bf, const char* var)
- {
- try
- {
- ifstream ifs (bf.string ());
- if (!ifs.is_open ())
- fail << "unable to open " << bf;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
-
- path rbf (diag_relative (bf));
-
- lexer lex (ifs, rbf.string ());
- token t (lex.next ());
- token_type tt;
-
- if (t.type != token_type::name || t.value != var ||
- ((tt = lex.next ().type) != token_type::equal &&
- tt != token_type::equal_plus &&
- tt != token_type::plus_equal))
- {
- error << "variable '" << var << "' expected as first line in " << rbf;
- throw failed (); // Suppress "used uninitialized" warning.
- }
-
- parser p;
- temp_scope tmp (*global_scope);
- p.parse_variable (lex, tmp, t.value, tt);
-
- auto l (tmp.vars[var]);
- assert (l.defined ());
- value& v (*l);
- return move (v); // Steal the value, the scope is going away.
- }
- catch (const ifstream::failure&)
- {
- fail << "unable to read buildfile " << bf;
- }
-
- return value (); // Never reaches.
- }
-
- // Extract the project name from bootstrap.build.
- //
- static string
- find_project_name (const dir_path& out_root,
- const dir_path& fallback_src_root,
- bool* src_hint = nullptr)
- {
- tracer trace ("find_project_name");
-
- // Load the project name. If this subdirectory is the subproject's
- // src_root, then we can get directly to that. Otherwise, we first
- // have to discover its src_root.
- //
- const dir_path* src_root;
- value src_root_v; // Need it to live until the end.
-
- if (src_hint != nullptr ? *src_hint : is_src_root (out_root))
- src_root = &out_root;
- else
- {
- path f (out_root / src_root_file);
-
- if (!fallback_src_root.empty () && !file_exists (f))
- src_root = &fallback_src_root;
- else
- {
- src_root_v = extract_variable (f, "src_root");
- src_root = &as<dir_path> (src_root_v);
- level5 ([&]{trace << "extracted src_root " << *src_root << " for "
- << out_root;});
- }
- }
-
- string name;
- {
- value v (extract_variable (*src_root / bootstrap_file, "project"));
- name = move (as<string> (v));
- }
-
- level5 ([&]{trace << "extracted project name '" << name << "' for "
- << *src_root;});
- return name;
- }
-
- // Scan the specified directory for any subprojects. If a subdirectory
- // is a subproject, then enter it into the map, handling the duplicates.
- // Otherwise, scan the subdirectory recursively.
- //
- static void
- find_subprojects (subprojects& sps,
- const dir_path& d,
- const dir_path& root,
- bool out)
- {
- tracer trace ("find_subprojects");
-
- for (const dir_entry& de: dir_iterator (d))
- {
- // If this is a link, then type() will try to stat() it. And if
- // the link is dangling or points to something inaccessible, it
- // will fail.
- //
- try
- {
- if (de.type () != entry_type::directory)
- continue;
- }
- catch (const system_error& e)
- {
- continue;
- }
-
- dir_path sd (d / path_cast<dir_path> (de.path ()));
-
- bool src (false);
- if (!((out && is_out_root (sd)) || (src = is_src_root (sd))))
- {
- find_subprojects (sps, sd, root, out);
- continue;
- }
-
- // Calculate relative subdirectory for this subproject.
- //
- dir_path dir (sd.leaf (root));
- level5 ([&]{trace << "subproject " << sd << " as " << dir;});
-
- // Load its name. Note that here we don't use fallback src_root
- // since this function is used to scan both out_root and src_root.
- //
- string name (find_project_name (sd, dir_path (), &src));
-
- // If the name is empty, then is is an unnamed project. While the
- // 'project' variable stays empty, here we come up with a surrogate
- // name for a key. The idea is that such a key should never conflict
- // with a real project name. We ensure this by using the project's
- // sub-directory and appending trailing '/' to it.
- //
- if (name.empty ())
- name = dir.posix_string () + '/';
-
- // @@ Can't use move() because we may need the values in diagnostics
- // below. Looks like C++17 try_emplace() is what we need.
- //
- auto rp (sps.emplace (name, dir));
-
- // Handle duplicates.
- //
- if (!rp.second)
- {
- const dir_path& dir1 (rp.first->second);
-
- if (dir != dir1)
- fail << "inconsistent subproject directories for " << name <<
- info << "first alternative: " << dir1 <<
- info << "second alternative: " << dir;
-
- level6 ([&]{trace << "skipping duplicate";});
- }
- }
- }
-
- bool
- bootstrap_src (scope& root)
- {
- tracer trace ("bootstrap_src");
-
- bool r (false);
-
- const dir_path& out_root (root.out_path ());
- const dir_path& src_root (root.src_path ());
-
- path bf (src_root / path ("build/bootstrap.build"));
-
- 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.
- //
- if (root.buildfiles.insert (bf).second)
- source (bf, root, root, true);
- else
- level5 ([&]{trace << "skipping already sourced " << bf;});
-
- r = true;
- }
-
- // 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 (and which we convert to NULL below). When
- // calculated, the NULL value indicates that we are not
- // amalgamated.
- //
- {
- auto rp (root.vars.assign ("amalgamation")); // Set NULL by default.
- value& v (rp.first);
-
- if (v && v.empty ()) // Convert empty to NULL.
- v = nullptr;
-
- if (scope* aroot = root.parent_scope ()->root_scope ())
- {
- const dir_path& ad (aroot->out_path ());
- dir_path rd (ad.relative (out_root));
-
- // If we already have the amalgamation variable set, verify
- // that aroot matches its value.
- //
- if (!rp.second)
- {
- if (!v)
- {
- fail << out_root << " cannot be amalgamated" <<
- info << "amalgamated by " << ad;
- }
- else
- {
- const dir_path& vd (as<dir_path> (v));
-
- if (vd != rd)
- {
- fail << "inconsistent amalgamation of " << out_root <<
- info << "specified: " << vd <<
- info << "actual: " << rd << " by " << ad;
- }
- }
- }
- else
- {
- // Otherwise, use the outer root as our amalgamation.
- //
- level5 ([&]{trace << out_root << " amalgamated as " << rd;});
- v = 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& ad (find_out_root (out_root.directory ()));
-
- if (!ad.empty ())
- {
- dir_path rd (ad.relative (out_root));
- level5 ([&]{trace << out_root << " amalgamated as " << rd;});
- v = move (rd);
- }
- }
- }
-
- // See if we have any subprojects. In a sense, this is the other
- // side/direction of the amalgamation logic above. Here, the
- // subprojects variable 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 there are no subproject and none should be searched
- // for (and which we convert to NULL below). Otherwise, it is a
- // list of directory[=project] pairs. The directory must be
- // relative to our out_root. If the project name is not specified,
- // then we have to figure it out. When subprojects are calculated,
- // the NULL value indicates that we found no subprojects.
- //
- {
- const variable& var (var_pool.find ("subprojects"));
- auto rp (root.vars.assign(var)); // Set NULL by default.
- value& v (rp.first);
-
- if (rp.second)
- {
- // No subprojects set so we need to figure out if there are any.
- //
- // First we are going to scan our out_root and find all the
- // pre-configured subprojects. Then, if out_root != src_root,
- // we are going to do the same for src_root. Here, however,
- // we need to watch out for duplicates.
- //
- subprojects sps;
-
- if (dir_exists (out_root))
- find_subprojects (sps, out_root, out_root, true);
-
- if (out_root != src_root)
- find_subprojects (sps, src_root, src_root, false);
-
- if (!sps.empty ()) // Keep it NULL if no subprojects.
- v = move (sps);
- }
- else if (v)
- {
- // Convert empty to NULL.
- //
- if (v.empty ())
- v = nullptr;
- else
- {
- // Pre-scan the value and convert it to the "canonical" form,
- // that is, a list of simple=dir pairs.
- //
- for (auto i (v.data_.begin ()); i != v.data_.end (); ++i)
- {
- bool p (i->pair != '\0');
-
- if (p)
- {
- // Project name.
- //
- if (!assign<string> (*i) || as<string> (*i).empty ())
- fail << "expected project name instead of '" << *i << "' in "
- << "the subprojects variable";
-
- ++i; // Got to have the second half of the pair.
- }
-
- if (!assign<dir_path> (*i))
- fail << "expected directory instead of '" << *i << "' in the "
- << "subprojects variable";
-
- auto& d (as<dir_path> (*i));
-
- // Figure out the project name if the user didn't specify one.
- //
- if (!p)
- {
- // Pass fallback src_root since this is a subproject that
- // was specified by the user so it is most likely in our
- // src.
- //
- string n (find_project_name (out_root / d, src_root / d));
-
- // See find_subprojects() for details on unnamed projects.
- //
- if (n.empty ())
- n = d.posix_string () + '/';
-
- i = v.data_.emplace (i, move (n));
-
- i->pair = '=';
- ++i;
- }
- }
-
- // Make it of the map type.
- //
- assign<subprojects> (v, var);
- }
- }
- }
-
- return r;
- }
-
- void
- create_bootstrap_outer (scope& root)
- {
- auto l (root.vars["amalgamation"]);
-
- if (!l)
- return;
-
- const dir_path& d (as<dir_path> (*l));
- dir_path out_root (root.out_path () / d);
- out_root.normalize ();
-
- // src_root is a bit more complicated. Here we have three cases:
- //
- // 1. Amalgamation's src_root is "parallel" to the sub-project's.
- // 2. Amalgamation's src_root is the same as its out_root.
- // 3. Some other pre-configured (via src-root.build) src_root.
- //
- // So we need to try all these cases in some sensible order.
- // #3 should probably be tried first since that src_root was
- // explicitly configured by the user. After that, #2 followed
- // by #1 seems reasonable.
- //
- scope& rs (create_root (out_root, dir_path ()));
- bootstrap_out (rs); // #3 happens here, if at all.
-
- value& v (rs.assign ("src_root"));
-
- if (!v)
- {
- if (is_src_root (out_root)) // #2
- v = out_root;
- else // #1
- {
- dir_path src_root (root.src_path () / d);
- src_root.normalize ();
- v = move (src_root);
- }
- }
-
- setup_root (rs);
-
- bootstrap_src (rs);
- create_bootstrap_outer (rs);
-
- // Check if we are strongly amalgamated by this outer root scope.
- //
- if (root.src_path ().sub (rs.src_path ()))
- root.strong_ = rs.strong_scope (); // Itself or some outer scope.
- }
-
- scope&
- create_bootstrap_inner (scope& root, const dir_path& out_base)
- {
- if (auto l = root.vars["subprojects"])
- {
- for (const name& n: *l)
- {
- if (n.pair != '\0')
- continue; // Skip project names.
-
- dir_path out_root (root.out_path () / n.dir);
-
- if (!out_base.sub (out_root))
- continue;
-
- // The same logic to src_root as in create_bootstrap_outer().
- //
- scope& rs (create_root (out_root, dir_path ()));
- bootstrap_out (rs);
-
- value& v (rs.assign ("src_root"));
-
- if (!v)
- v = is_src_root (out_root)
- ? out_root
- : (root.src_path () / n.dir);
-
- setup_root (rs);
-
- bootstrap_src (rs);
-
- // Check if we strongly amalgamated this inner root scope.
- //
- if (rs.src_path ().sub (root.src_path ()))
- rs.strong_ = root.strong_scope (); // Itself or some outer scope.
-
- // 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 ())
- load_root_pre (*rs);
-
- // Finish off loading bootstrapped modules.
- //
- for (auto& p: root.modules)
- {
- const string& n (p.first);
- module_state& s (p.second);
-
- if (s.boot)
- {
- load_module (false, n, root, root, s.loc);
- assert (!s.boot);
- }
- }
-
- // Load root.build.
- //
- path bf (root.src_path () / path ("build/root.build"));
-
- if (file_exists (bf))
- source_once (bf, root, root);
- }
-
- names
- import (scope& ibase, name target, const location& loc)
- {
- tracer trace ("import");
-
- // If there is no project specified for this target, then our
- // run will be short and sweet: we simply return it as empty-
- // project-qualified and let someone else (e.g., a rule) take
- // a stab at it.
- //
- if (target.unqualified ())
- {
- target.proj = &project_name_pool.find ("");
- return names {move (target)};
- }
-
- // Otherwise, get the project name and convert the target to
- // unqualified.
- //
- const string& project (*target.proj);
- target.proj = nullptr;
-
- scope& iroot (*ibase.root_scope ());
-
- // Figure out this project's out_root.
- //
- dir_path out_root;
- dir_path fallback_src_root; // We have seen this already, haven't we..?
-
- // First search subprojects, starting with our root and then trying
- // outer roots for as long as we are inside an amalgamation.
- //
- for (scope* r (&iroot);; r = r->parent_scope ()->root_scope ())
- {
- // First check the amalgamation itself.
- //
- if (r != &iroot && as<string> (*r->vars["project"]) == project)
- {
- out_root = r->out_path ();
- fallback_src_root = r->src_path ();
- break;
- }
-
- if (auto l = r->vars["subprojects"])
- {
- const auto& m (as<subprojects> (*l));
- auto i (m.find (project));
-
- if (i != m.end ())
- {
- const dir_path& d ((*i).second);
- out_root = r->out_path () / d;
- fallback_src_root = r->src_path () / d;
- break;
- }
- }
-
- if (!r->vars["amalgamation"])
- break;
- }
-
- // Then try the config.import.* mechanism.
- //
- if (out_root.empty ())
- {
- const variable& var (
- var_pool.find ("config.import." + project, dir_path_type));
-
- if (auto l = iroot[var])
- {
- out_root = as<dir_path> (*l);
-
- if (l.belongs (*global_scope)) // A value from command line.
- {
- // Process the path by making it absolute and normalized.
- //
- if (out_root.relative ())
- out_root = work / out_root;
-
- out_root.normalize ();
-
- // Set on our root scope (part of our configuration).
- //
- iroot.assign (var) = out_root;
-
- // Also update the command-line value. This is necessary to avoid
- // a warning issued by the config module about global/root scope
- // value mismatch. Not very clean.
- //
- dir_path& d (as<dir_path> (const_cast<value&> (*l)));
- if (d != out_root)
- d = out_root;
- }
- }
- else
- {
- // If we can't find the project, convert it back into qualified
- // target and return to let someone else (e.g., a rule) to take
- // a stab at it.
- //
- target.proj = &project;
- level5 ([&]{trace << "postponing " << target;});
- return names {move (target)};
- }
- }
-
- // 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.
- //
- dir_path src_root (is_src_root (out_root) ? out_root : dir_path ());
- scope& root (create_root (out_root, src_root));
-
- bootstrap_out (root);
-
- // Check that the bootstrap process set src_root.
- //
- if (auto l = root.vars["src_root"])
- {
- const dir_path& p (as<dir_path> (*l));
-
- if (!src_root.empty () && p != src_root)
- fail (loc) << "bootstrapped src_root " << p << " does not match "
- << "discovered " << src_root;
- }
- // Otherwise, use fallback if available.
- //
- else if (!fallback_src_root.empty ())
- {
- value& v (root.assign ("src_root"));
- v = move (fallback_src_root);
- }
- else
- fail (loc) << "unable to determine src_root for imported " << project <<
- info << "consider configuring " << out_root;
-
- setup_root (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.assign ("out_root") = move (out_root);
- ts.assign ("src_root") = move (src_root);
-
- // Also pass the target being imported.
- //
- {
- value& v (ts.assign ("target"));
-
- if (!target.empty ()) // Otherwise leave NULL.
- v = move (target);
- }
-
- // 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.
- //
- path es (root.src_path () / path ("build/export.build"));
-
- try
- {
- ifstream ifs (es.string ());
- if (!ifs.is_open ())
- fail (loc) << "unable to open " << es;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
-
- level5 ([&]{trace << "importing " << es;});
-
- // @@ Should we verify these are all unqualified names? Or maybe
- // there is a use-case for the export stub to return a qualified
- // name?
- //
- parser p;
- return p.parse_export_stub (ifs, es, iroot, ts);
- }
- catch (const ifstream::failure&)
- {
- fail (loc) << "unable to read buildfile " << es;
- }
-
- return names (); // Never reached.
- }
-
- target&
- import (const prerequisite_key& pk)
- {
- assert (pk.proj != nullptr);
- const string& p (*pk.proj);
-
- // @@ We no longer have location. This is especially bad for the
- // empty case, i.e., where do I need to specify the project
- // name)? Looks like the only way to do this is to keep location
- // in name and then in prerequisite. Perhaps one day...
- //
- if (!p.empty ())
- fail << "unable to import target " << pk <<
- info << "consider explicitly specifying its project out_root via the "
- << "config.import." << p << " command line variable";
- else
- fail << "unable to import target " << pk <<
- info << "consider adding its installation location" <<
- info << "or explicitly specifying its project name";
-
- throw failed (); // No return.
- }
-}
diff --git a/build/file.ixx b/build/file.ixx
deleted file mode 100644
index 1a11cbe..0000000
--- a/build/file.ixx
+++ /dev/null
@@ -1,12 +0,0 @@
-// file : build/file.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace build
-{
- inline void
- source_once (const path& bf, scope& root, scope& base)
- {
- return source_once (bf, root, base, base);
- }
-}
diff --git a/build/install/module b/build/install/module
deleted file mode 100644
index 7a814c8..0000000
--- a/build/install/module
+++ /dev/null
@@ -1,26 +0,0 @@
-// file : build/install/module -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_INSTALL_MODULE
-#define BUILD_INSTALL_MODULE
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/module>
-
-namespace build
-{
- namespace install
- {
- extern "C" void
- install_boot (scope&, const location&, unique_ptr<module>&);
-
- extern "C" bool
- install_init (
- scope&, scope&, const location&, unique_ptr<module>&, bool, bool);
- }
-}
-
-#endif // BUILD_INSTALL_MODULE
diff --git a/build/install/module.cxx b/build/install/module.cxx
deleted file mode 100644
index 706f367..0000000
--- a/build/install/module.cxx
+++ /dev/null
@@ -1,188 +0,0 @@
-// file : build/install/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/install/module>
-
-#include <build/scope>
-#include <build/target>
-#include <build/rule>
-#include <build/operation>
-#include <build/diagnostics>
-
-#include <build/config/utility>
-
-#include <build/install/rule>
-#include <build/install/utility>
-#include <build/install/operation>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace install
- {
- // Set install.<name>.* values based on config.install.<name>.* ones
- // or the defaults. If none of config.install.* values were specified,
- // then we do omitted/delayed configuration. Note that we still need
- // to set all the install.* values to defaults, as if we had the
- // default configuration.
- //
- // If override is true, then override values that came from outer
- // configurations. We have to do this for paths that contain the
- // package name.
- //
- template <typename T>
- static void
- set_var (bool spec,
- scope& r,
- const char* name,
- const char* var,
- const T* dv,
- bool override = false)
- {
- string vn;
- const value* cv (nullptr);
-
- if (spec)
- {
- vn = "config.install.";
- vn += name;
- vn += var;
- const variable& vr (
- var_pool.find (move (vn), &value_traits<T>::value_type));
-
- cv = dv != nullptr
- ? &config::required (r, vr, *dv, override).first.get ()
- : &config::optional (r, vr);
- }
-
- vn = "install.";
- vn += name;
- vn += var;
- const variable& vr (
- var_pool.find (move (vn), &value_traits<T>::value_type));
-
- value& v (r.assign (vr));
-
- if (spec)
- {
- if (*cv && !cv->empty ())
- v = *cv;
- }
- else
- {
- if (dv != nullptr)
- v = *dv;
- }
- }
-
- static void
- set_dir (bool s, // specified
- scope& r, // root scope
- const char* n, // var name
- const string& ps, // path (as string)
- const string& fm = string (), // file mode
- const string& dm = string (), // dir mode
- const string& c = string (), // command
- bool o = false) // override
- {
- dir_path p (ps);
- set_var (s, r, n, "", p.empty () ? nullptr : &p, o);
- set_var (s, r, n, ".mode", fm.empty () ? nullptr : &fm);
- set_var (s, r, n, ".dir_mode", dm.empty () ? nullptr : &dm);
- set_var<string> (s, r, n, ".sudo", nullptr);
- set_var (s, r, n, ".cmd", c.empty () ? nullptr : &c);
- set_var<strings> (s, r, n, ".options", nullptr);
- }
-
- static alias_rule alias_;
- static file_rule file_;
-
- extern "C" void
- install_boot (scope& r, const location&, unique_ptr<module>&)
- {
- tracer trace ("install::boot");
-
- level5 ([&]{trace << "for " << r.out_path ();});
-
- // Register the install operation.
- //
- r.operations.insert (install_id, install);
- }
-
- extern "C" bool
- install_init (scope& r,
- scope& b,
- const location& l,
- unique_ptr<module>&,
- bool first,
- bool)
- {
- tracer trace ("install::init");
-
- if (!first)
- {
- warn (l) << "multiple install module initializations";
- return true;
- }
-
- const dir_path& out_root (r.out_path ());
- level5 ([&]{trace << "for " << out_root;});
-
- // Enter module variables.
- //
- // Note that the set_dir() calls below enter some more.
- //
- if (first)
- {
- auto& v (var_pool);
-
- v.find ("install", dir_path_type);
- }
-
- // Register our alias and file installer rule.
- //
- b.rules.insert<alias> (perform_install_id, "install.alias", alias_);
- b.rules.insert<file> (perform_install_id, "install.file", file_);
-
- // Configuration.
- //
- // Note that we don't use any defaults for root -- the location
- // must be explicitly specified or the installer will complain
- // if and when we try to install.
- //
- if (first)
- {
- bool s (config::specified (r, "config.install"));
- const string& n (as<string> (*r["project"]));
-
- set_dir (s, r, "root", "", "", "755", "install");
- set_dir (s, r, "data_root", "root", "644");
- set_dir (s, r, "exec_root", "root", "755");
-
- set_dir (s, r, "sbin", "exec_root/sbin");
- set_dir (s, r, "bin", "exec_root/bin");
- set_dir (s, r, "lib", "exec_root/lib");
- set_dir (s, r, "libexec", "exec_root/libexec/" + n, "", "", "", true);
-
- set_dir (s, r, "data", "data_root/share/" + n, "", "", "", true);
- set_dir (s, r, "include", "data_root/include");
-
- set_dir (s, r, "doc", "data_root/share/doc/" + n, "", "", "", true);
- set_dir (s, r, "man", "data_root/share/man");
-
- set_dir (s, r, "man1", "man/man1");
- }
-
- // Configure "installability" for built-in target types.
- //
- path<doc> (b, dir_path ("doc")); // Install into install.doc.
- path<man> (b, dir_path ("man")); // Install into install.man.
- path<man1> (b, dir_path ("man1")); // Install into install.man1.
-
- return true;
- }
- }
-}
diff --git a/build/install/operation b/build/install/operation
deleted file mode 100644
index 2763d10..0000000
--- a/build/install/operation
+++ /dev/null
@@ -1,18 +0,0 @@
-// file : build/install/operation -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_INSTALL_OPERATION
-#define BUILD_INSTALL_OPERATION
-
-#include <build/operation>
-
-namespace build
-{
- namespace install
- {
- extern operation_info install;
- }
-}
-
-#endif // BUILD_INSTALL_OPERATION
diff --git a/build/install/operation.cxx b/build/install/operation.cxx
deleted file mode 100644
index 1246671..0000000
--- a/build/install/operation.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// file : build/install/operation.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/install/operation>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace install
- {
- static operation_id
- install_pre (meta_operation_id mo)
- {
- // Run update as a pre-operation, unless we are disfiguring.
- //
- return mo != disfigure_id ? update_id : 0;
- }
-
- operation_info install {
- "install",
- "install",
- "installing",
- "has nothing to install", // We cannot "be installed".
- execution_mode::first,
- &install_pre,
- nullptr
- };
- }
-}
diff --git a/build/install/rule b/build/install/rule
deleted file mode 100644
index 54014e1..0000000
--- a/build/install/rule
+++ /dev/null
@@ -1,49 +0,0 @@
-// file : build/install/rule -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_INSTALL_RULE
-#define BUILD_INSTALL_RULE
-
-#include <build/rule>
-#include <build/types>
-#include <build/target>
-#include <build/operation>
-
-namespace build
-{
- namespace install
- {
- class alias_rule: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string&) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
- };
-
- class file_rule: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string&) const;
-
- // Return NULL if this prerequisite should be ignored and pointer to its
- // target otherwise. The default implementation ignores prerequsites that
- // are outside of this target's project.
- //
- virtual target*
- filter (action, target&, prerequisite_member) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_install (action, target&);
- };
- }
-}
-
-#endif // BUILD_INSTALL_RULE
diff --git a/build/install/rule.cxx b/build/install/rule.cxx
deleted file mode 100644
index fa648b5..0000000
--- a/build/install/rule.cxx
+++ /dev/null
@@ -1,410 +0,0 @@
-// file : build/install/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/install/rule>
-
-#include <butl/process>
-#include <butl/filesystem>
-
-#include <build/scope>
-#include <build/target>
-#include <build/algorithm>
-#include <build/diagnostics>
-
-#include <build/config/utility>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace install
- {
- // Lookup the install or install.* variable. Return NULL if
- // not found or if the value is the special 'false' name (which
- // means do not install). T is either scope or target.
- //
- template <typename T>
- static const dir_path*
- lookup (T& t, const string& var)
- {
- auto l (t[var]);
-
- if (!l)
- return nullptr;
-
- const dir_path& r (as<dir_path> (*l));
- return r.simple () && r.string () == "false" ? nullptr : &r;
- }
-
- // alias_rule
- //
- match_result alias_rule::
- match (action, target& t, const std::string&) const
- {
- return t;
- }
-
- recipe alias_rule::
- apply (action a, target& t, const match_result&) const
- {
- tracer trace ("install::alias_rule::apply");
-
- for (prerequisite p: group_prerequisites (t))
- {
- target& pt (search (p));
-
- // Check if this prerequisite is explicitly "not installable",
- // that is, there is the 'install' variable and its value is
- // false.
- //
- // At first, this might seem redundand since we could have let
- // the file_rule below take care of it. The nuance is this: this
- // prerequsite can be in a different subproject that hasn't loaded
- // the install module (and therefore has no file_rule registered).
- // The typical example would be the 'tests' subproject.
- //
- auto l (pt["install"]);
-
- if (l && as<dir_path> (*l).string () == "false")
- {
- level5 ([&]{trace << "ignoring " << pt;});
- continue;
- }
-
- build::match (a, pt);
- t.prerequisite_targets.push_back (&pt);
- }
-
- return default_recipe;
- }
-
- // file_rule
- //
-
- match_result file_rule::
- match (action a, target& t, const std::string&) const
- {
- // First determine if this target should be installed (called
- // "installable" for short).
- //
- if (lookup (t, "install") == nullptr)
- // If this is the update pre-operation, signal that we don't match so
- // that some other rule can take care of it.
- //
- return a.operation () == update_id ? nullptr : match_result (t, false);
-
- match_result mr (t, true);
-
- // If this is the update pre-operation, change the recipe action
- // to (update, 0) (i.e., "unconditional update").
- //
- if (a.operation () == update_id)
- mr.recipe_action = action (a.meta_operation (), update_id);
-
- return mr;
- }
-
- target* file_rule::
- filter (action, target& t, prerequisite_member p) const
- {
- target& pt (p.search ());
- return pt.in (t.root_scope ()) ? &pt : nullptr;
- }
-
- recipe file_rule::
- apply (action a, target& t, const match_result& mr) const
- {
- if (!mr.bvalue) // Not installable.
- return noop_recipe;
-
- // Ok, if we are here, then this means:
- //
- // 1. This target is installable.
- // 2. The action is either
- // a. (perform, install, 0) or
- // b. (*, update, install)
- //
- // In both cases, the next step is to search, match, and collect
- // all the installable prerequisites.
- //
- // @@ Perhaps if [noinstall] will be handled by the
- // group_prerequisite_members machinery, then we can just
- // run standard search_and_match()? Will need an indicator
- // that it was forced (e.g., [install]) for filter() below.
- //
- for (prerequisite_member p: group_prerequisite_members (a, t))
- {
- // Ignore unresolved targets that are imported from other projects.
- // We are definitely not installing those.
- //
- if (p.proj () != nullptr)
- continue;
-
- // Let a customized rule have its say.
- //
- target* pt (filter (a, t, p));
- if (pt == nullptr)
- continue;
-
- // See if the user instructed us not to install it.
- //
- auto l ((*pt)["install"]);
- if (l && as<dir_path> (*l).string () == "false")
- continue;
-
- build::match (a, *pt);
-
- // If the matched rule returned noop_recipe, then the target
- // state will be set to unchanged as an optimization. Use this
- // knowledge to optimize things on our side as well since this
- // will help a lot in case of any static installable content
- // (headers, documentation, etc).
- //
- if (pt->state () != target_state::unchanged)
- t.prerequisite_targets.push_back (pt);
- else
- unmatch (a, *pt); // No intent to execute.
- }
-
- // This is where we diverge depending on the operation. In the
- // update pre-operation, we need to make sure that this target
- // as well as all its installable prerequisites are up to date.
- //
- if (a.operation () == update_id)
- {
- // Save the prerequisite targets that we found since the
- // call to match_delegate() below will wipe them out.
- //
- target::prerequisite_targets_type p;
-
- if (!t.prerequisite_targets.empty ())
- p.swap (t.prerequisite_targets);
-
- // Find the "real" update rule, that is, the rule that would
- // have been found if we signalled that we do not match from
- // match() above.
- //
- recipe d (match_delegate (a, t).first);
-
- // If we have no installable prerequisites, then simply redirect
- // to it.
- //
- if (p.empty ())
- return d;
-
- // Ok, the worst case scenario: we need to cause update of
- // prerequisite targets and also delegate to the real update.
- //
- return [pt = move (p), dr = move (d)]
- (action a, target& t) mutable -> target_state
- {
- // Do the target update first.
- //
- target_state r (execute_delegate (dr, a, t));
-
- // Swap our prerequisite targets back in and execute.
- //
- t.prerequisite_targets.swap (pt);
- r |= execute_prerequisites (a, t);
- pt.swap (t.prerequisite_targets); // In case we get re-executed.
-
- return r;
- };
- }
- else
- return &perform_install;
- }
-
- struct install_dir
- {
- dir_path dir;
- string sudo;
- string cmd; //@@ VAR type
- const_strings_value options {nullptr};
- string mode;
- string dir_mode;
- };
-
- // install -d <dir>
- //
- static void
- install (const install_dir& base, const dir_path& d)
- {
- path reld (relative (d));
-
- cstrings args;
-
- if (!base.sudo.empty ())
- args.push_back (base.sudo.c_str ());
-
- args.push_back (base.cmd.c_str ());
- args.push_back ("-d");
-
- if (base.options.d != nullptr) //@@ VAR
- config::append_options (args, base.options);
-
- args.push_back ("-m");
- args.push_back (base.dir_mode.c_str ());
- args.push_back (reld.string ().c_str ());
- args.push_back (nullptr);
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "install " << d;
-
- try
- {
- process pr (args.data ());
-
- if (!pr.wait ())
- throw failed ();
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- }
-
- // install <file> <dir>
- //
- static void
- install (const install_dir& base, file& t)
- {
- path reld (relative (base.dir));
- path relf (relative (t.path ()));
-
- cstrings args;
-
- if (!base.sudo.empty ())
- args.push_back (base.sudo.c_str ());
-
- args.push_back (base.cmd.c_str ());
-
- if (base.options.d != nullptr) //@@ VAR
- config::append_options (args, base.options);
-
- args.push_back ("-m");
- args.push_back (base.mode.c_str ());
- args.push_back (relf.string ().c_str ());
- args.push_back (reld.string ().c_str ());
- args.push_back (nullptr);
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "install " << t;
-
- try
- {
- process pr (args.data ());
-
- if (!pr.wait ())
- throw failed ();
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
- }
-
- // Resolve installation directory name to absolute directory path,
- // creating leading directories as necessary.
- //
- static install_dir
- resolve (scope& s, dir_path d, const string* var = nullptr)
- {
- install_dir r;
-
- if (d.absolute ())
- {
- d.normalize ();
-
- // Make sure it already exists (this will normally be
- // install.root with everything else defined in term of it).
- //
- if (!dir_exists (d))
- fail << "installation directory " << d << " does not exist";
- }
- else
- {
- // If it is relative, then the first component is treated
- // as the installation directory name, e.g., bin, sbin, lib,
- // etc. Look it up and recurse.
- //
- const string& sn (*d.begin ());
- const string var ("install." + sn);
- if (const dir_path* dn = lookup (s, var))
- {
- r = resolve (s, *dn, &var);
- d = r.dir / dir_path (++d.begin (), d.end ());
- d.normalize ();
-
- if (!dir_exists (d))
- install (r, d); // install -d
- }
- else
- fail << "unknown installation directory name " << sn <<
- info << "did you forget to specify config." << var << "?";
- }
-
- r.dir = move (d);
-
- // Override components in install_dir if we have our own.
- //
- if (var != nullptr)
- {
- if (auto l = s[*var + ".sudo"]) r.sudo = as<string> (*l);
- if (auto l = s[*var + ".cmd"]) r.cmd = as<string> (*l);
- if (auto l = s[*var + ".mode"]) r.mode = as<string> (*l);
- if (auto l = s[*var + ".dir_mode"]) r.dir_mode = as<string> (*l);
- if (auto l = s[*var + ".options"]) r.options = as<strings> (*l);
- }
-
- // Set defaults for unspecified components.
- //
- if (r.cmd.empty ()) r.cmd = "install";
- if (r.mode.empty ()) r.mode = "644";
- if (r.dir_mode.empty ()) r.dir_mode = "755";
-
- return r;
- }
-
- target_state file_rule::
- perform_install (action a, target& t)
- {
- file& ft (static_cast<file&> (t));
- assert (!ft.path ().empty ()); // Should have been assigned by update.
-
- // First handle installable prerequisites.
- //
- target_state r (execute_prerequisites (a, t));
-
- // Resolve and, if necessary, create target directory.
- //
- install_dir d (
- resolve (t.base_scope (),
- as<dir_path> (*t["install"]))); // We know it's there.
-
- // Override mode if one was specified.
- //
- if (auto l = t["install.mode"])
- d.mode = as<string> (*l);
-
- install (d, ft);
- return (r |= target_state::changed);
- }
- }
-}
diff --git a/build/install/utility b/build/install/utility
deleted file mode 100644
index 9bc41f1..0000000
--- a/build/install/utility
+++ /dev/null
@@ -1,40 +0,0 @@
-// file : build/install/utility -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_INSTALL_UTILITY
-#define BUILD_INSTALL_UTILITY
-
-#include <string>
-#include <utility>
-
-#include <build/scope>
-#include <build/types>
-
-namespace build
-{
- namespace install
- {
- // Set install path, mode for a target type.
- //
- template <typename T>
- inline void
- path (scope& s, dir_path d)
- {
- auto r (s.target_vars[T::static_type]["*"].assign ("install"));
- if (r.second) // Already set by the user?
- r.first.get () = std::move (d);
- }
-
- template <typename T>
- inline void
- mode (scope& s, std::string m)
- {
- auto r (s.target_vars[T::static_type]["*"].assign ("install.mode"));
- if (r.second) // Already set by the user?
- r.first.get () = std::move (m);
- }
- }
-}
-
-#endif // BUILD_INSTALL_UTILITY
diff --git a/build/lexer b/build/lexer
deleted file mode 100644
index 4a50e2a..0000000
--- a/build/lexer
+++ /dev/null
@@ -1,138 +0,0 @@
-// file : build/lexer -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_LEXER
-#define BUILD_LEXER
-
-#include <stack>
-#include <string>
-#include <iosfwd>
-#include <cstddef> // size_t
-#include <cstdint> // uint64_t
-#include <cassert>
-#include <exception>
-
-#include <butl/char-scanner>
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/token>
-#include <build/diagnostics>
-
-namespace build
-{
- // Context-dependent lexing mode. In the value mode we don't treat
- // certain characters (e.g., +, =) as special so that we can use
- // them in the variable values, e.g., 'foo = g++'. In contrast, in
- // the variable mode, we restrict certain character (e.g., /) from
- // appearing in the name. The pairs mode is just like value except
- // that we split names separated by the pair character. The eval
- // mode is used in the evaluation context.
- //
- // The alternnative modes must be set manually. The value and pairs
- // modes are automatically reset after the end of the line. The
- // variable mode is reset after the name token. And the eval mode
- // is reset after the closing ')'.
- //
- // Quoted is an internal mode and should not be set explicitly.
- //
- enum class lexer_mode {normal, variable, value, pairs, eval, quoted};
-
- class lexer: protected butl::char_scanner
- {
- public:
- lexer (std::istream& is,
- const std::string& name,
- void (*processor) (token&, const lexer&) = nullptr)
- : char_scanner (is), fail (name), processor_ (processor), sep_ (false)
- {
- mode_.push (lexer_mode::normal);
- }
-
- const std::string&
- name () const {return fail.name_;}
-
- // Note: sets mode for the next token. If mode is pairs, then
- // the second argument specifies the separator character.
- //
- void
- mode (lexer_mode m, char pair_separator = '=')
- {
- mode_.push (m);
- pair_separator_ = pair_separator;
- }
-
- // Expire the current mode early.
- //
- void
- expire_mode () {mode_.pop ();}
-
- lexer_mode
- mode () const {return mode_.top ();}
-
- char
- pair_separator () const {return pair_separator_;}
-
- // Scanner.
- //
- token
- next ();
-
- // Peek at the first character of the next token. Return the character
- // or 0 if the next token will be eos. Also return an indicator of
- // whether the next token will be separated.
- //
- pair<char, bool>
- peek_char ();
-
- private:
- token
- next_impl ();
-
- token
- next_eval ();
-
- token
- next_quoted ();
-
- token
- name (bool separated);
-
- // Return true if we have seen any spaces. Skipped empty lines
- // don't count. In other words, we are only interested in spaces
- // that are on the same line as the following non-space character.
- //
- bool
- skip_spaces ();
-
- xchar
- escape ();
-
- // Diagnostics.
- //
- private:
- struct fail_mark_base: build::fail_mark_base<failed>
- {
- fail_mark_base (const std::string& n): name_ (n) {}
-
- location_prologue
- operator() (const xchar&) const;
-
- std::string name_;
- };
- typedef diag_mark<fail_mark_base> fail_mark;
-
- private:
- fail_mark fail;
-
- void (*processor_) (token&, const lexer&);
-
- std::stack<lexer_mode> mode_;
- char pair_separator_;
- bool sep_; // True if we skipped spaces in peek().
- };
-}
-
-#endif // BUILD_LEXER
diff --git a/build/lexer.cxx b/build/lexer.cxx
deleted file mode 100644
index 864fc7b..0000000
--- a/build/lexer.cxx
+++ /dev/null
@@ -1,431 +0,0 @@
-// file : build/lexer.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/lexer>
-
-using namespace std;
-
-namespace build
-{
- typedef token_type type;
-
- token lexer::
- next ()
- {
- token t (next_impl ());
- if (processor_ != nullptr)
- processor_ (t, *this);
- return t;
- }
-
- pair<char, bool> lexer::
- peek_char ()
- {
- // In the quoted mode we don't skip spaces.
- //
- sep_ = mode_.top () != lexer_mode::quoted && skip_spaces ();
- xchar c (peek ());
- return make_pair (eos (c) ? '\0' : char (c), sep_);
- }
-
- token lexer::
- next_impl ()
- {
- lexer_mode m (mode_.top ());
-
- // For some modes we have dedicated imlementations of next().
- //
- switch (m)
- {
- case lexer_mode::eval: return next_eval ();
- case lexer_mode::quoted: return next_quoted ();
- default: break;
- }
-
- bool sep (skip_spaces ());
-
- xchar c (get ());
- uint64_t ln (c.line), cn (c.column);
-
- if (eos (c))
- return token (type::eos, sep, ln, cn);
-
- switch (c)
- {
- // NOTE: remember to update name(), next_eval() if adding new
- // special characters.
- //
- case '\n':
- {
- // Expire value/pairs mode at the end of the line.
- //
- if (m == lexer_mode::value || m == lexer_mode::pairs)
- mode_.pop ();
-
- return token (type::newline, sep, ln, cn);
- }
- case '{': return token (type::lcbrace, sep, ln, cn);
- case '}': return token (type::rcbrace, sep, ln, cn);
- case '$': return token (type::dollar, sep, ln, cn);
- case '(': return token (type::lparen, sep, ln, cn);
- case ')': return token (type::rparen, sep, ln, cn);
- }
-
- // Handle pair separator.
- //
- if (m == lexer_mode::pairs && c == pair_separator_)
- return token (c, sep, ln, cn);
-
- // The following characters are not treated as special in the
- // value or pairs mode.
- //
- if (m != lexer_mode::value && m != lexer_mode::pairs)
- {
- switch (c)
- {
- // NOTE: remember to update name(), next_eval() if adding new
- // special characters.
- //
- case ':': return token (type::colon, sep, ln, cn);
- case '=':
- {
- if (peek () == '+')
- {
- get ();
- return token (type::equal_plus, sep, ln, cn);
- }
- else
- return token (type::equal, sep, ln, cn);
- }
- case '+':
- {
- if (peek () == '=')
- {
- get ();
- return token (type::plus_equal, sep, ln, cn);
- }
- }
- }
- }
-
- // Otherwise it is a name.
- //
- unget (c);
- return name (sep);
- }
-
- token lexer::
- next_eval ()
- {
- bool sep (skip_spaces ());
- xchar c (get ());
-
- if (eos (c))
- fail (c) << "unterminated evaluation context";
-
- uint64_t ln (c.line), cn (c.column);
-
- // This mode is quite a bit like the value mode when it comes
- // to special characters.
- //
- switch (c)
- {
- // NOTE: remember to update name() if adding new special characters.
- //
- case '\n': fail (c) << "newline in evaluation context";
- case '{': return token (type::lcbrace, sep, ln, cn);
- case '}': return token (type::rcbrace, sep, ln, cn);
- case '$': return token (type::dollar, sep, ln, cn);
- case '(': return token (type::lparen, sep, ln, cn);
- case ')':
- {
- mode_.pop (); // Expire eval mode.
- return token (type::rparen, sep, ln, cn);
- }
- }
-
- // Otherwise it is a name.
- //
- unget (c);
- return name (sep);
- }
-
- token lexer::
- next_quoted ()
- {
- xchar c (get ());
-
- if (eos (c))
- fail (c) << "unterminated double-quoted sequence";
-
- uint64_t ln (c.line), cn (c.column);
-
- switch (c)
- {
- case '$': return token (type::dollar, false, ln, cn);
- case '(': return token (type::lparen, false, ln, cn);
- }
-
- // Otherwise it is a name.
- //
- unget (c);
- return name (false);
- }
-
- token lexer::
- name (bool sep)
- {
- xchar c (peek ());
- assert (!eos (c));
-
- uint64_t ln (c.line), cn (c.column);
- string lexeme;
-
- lexer_mode m (mode_.top ());
- bool quoted (m == lexer_mode::quoted);
-
- for (; !eos (c); c = peek ())
- {
- bool done (false);
-
- // Handle pair separator.
- //
- if (m == lexer_mode::pairs && c == pair_separator_)
- break;
-
- // The following characters are not treated as special in the
- // value/pairs, eval, and quoted modes.
- //
- if (m != lexer_mode::value &&
- m != lexer_mode::pairs &&
- m != lexer_mode::eval &&
- m != lexer_mode::quoted)
- {
- switch (c)
- {
- case ':':
- case '+':
- case '=':
- {
- done = true;
- break;
- }
- }
-
- if (done)
- break;
- }
-
- // While these extra characters are treated as the name end in
- // the variable mode.
- //
- if (m == lexer_mode::variable)
- {
- switch (c)
- {
- case '/':
- case '-':
- {
- done = true;
- break;
- }
- }
-
- if (done)
- break;
- }
-
- // If we are quoted, these are ordinary characters.
- //
- if (m != lexer_mode::quoted)
- {
- switch (c)
- {
- case ' ':
- case '\t':
- case '\n':
- case '#':
- case '{':
- case '}':
- case ')':
- {
- done = true;
- break;
- }
- case '\\':
- {
- get ();
- c = escape ();
- if (c != '\n') // Ignore.
- lexeme += c;
- continue;
- }
- case '\'':
- {
- // If we are in the variable mode, then treat quote as just
- // another separator.
- //
- if (m == lexer_mode::variable)
- {
- done = true;
- break;
- }
- else
- {
- get ();
-
- for (c = get (); !eos (c) && c != '\''; c = get ())
- lexeme += c;
-
- if (eos (c))
- fail (c) << "unterminated single-quoted sequence";
-
- quoted = true;
- continue;
- }
- }
- }
-
- if (done)
- break;
- }
-
- switch (c)
- {
- case '$':
- case '(':
- {
- done = true;
- break;
- }
- case '\"':
- {
- // If we are in the variable mode, then treat quote as just
- // another separator.
- //
- if (m == lexer_mode::variable)
- {
- done = true;
- break;
- }
- else
- {
- get ();
-
- if (m == lexer_mode::quoted)
- mode_.pop ();
- else
- {
- mode_.push (lexer_mode::quoted);
- quoted = true;
- }
-
- m = mode_.top ();
- continue;
- }
- }
- default:
- {
- get ();
- lexeme += c;
- continue;
- }
- }
-
- assert (done);
- break;
- }
-
- if (m == lexer_mode::quoted && eos (c))
- fail (c) << "unterminated double-quoted sequence";
-
- // Expire variable mode at the end of the name.
- //
- if (m == lexer_mode::variable)
- mode_.pop ();
-
- return token (lexeme, sep, quoted, ln, cn);
- }
-
- bool lexer::
- skip_spaces ()
- {
- bool r (sep_);
- sep_ = false;
-
- xchar c (peek ());
- bool start (c.column == 1);
-
- for (; !eos (c); c = peek ())
- {
- switch (c)
- {
- case ' ':
- case '\t':
- {
- r = true;
- break;
- }
- case '\n':
- {
- // Skip empty lines.
- //
- if (start)
- {
- r = false;
- break;
- }
-
- return r;
- }
- case '#':
- {
- get ();
-
- // Read until newline or eos.
- //
- for (c = peek (); !eos (c) && c != '\n'; c = peek ())
- get ();
-
- r = true;
- continue;
- }
- case '\\':
- {
- get ();
-
- if (peek () == '\n')
- break; // Ignore.
-
- unget (c);
- // Fall through.
- }
- default:
- return r; // Not a space.
- }
-
- get ();
- }
-
- return r;
- }
-
- lexer::xchar lexer::
- escape ()
- {
- xchar c (get ());
-
- if (eos (c))
- fail (c) << "unterminated escape sequence";
-
- return c;
- }
-
- location_prologue lexer::fail_mark_base::
- operator() (const xchar& c) const
- {
- return build::fail_mark_base<failed>::operator() (
- location (name_.c_str (), c.line, c.column));
- }
-}
diff --git a/build/module b/build/module
deleted file mode 100644
index 061ef60..0000000
--- a/build/module
+++ /dev/null
@@ -1,86 +0,0 @@
-// file : build/module -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_MODULE
-#define BUILD_MODULE
-
-#include <map>
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/diagnostics>
-
-namespace build
-{
- class scope;
- class location;
-
- class module
- {
- public:
- virtual
- ~module () = default;
- };
-
- extern "C"
- using module_boot_function =
- void (scope& root, const location&, unique_ptr<module>&);
-
- // Return false if the module configuration (normally based on the default
- // values) was unsuccessful but this is not (yet) an error. One example
- // would be the optional use of a module. Or a module might remain
- // unconfigured for as long as it is actually not used (e.g., install,
- // dist). The return value is used to set the <module>.configured variable.
- //
- extern "C"
- using module_init_function =
- bool (scope& root,
- scope& base,
- const location&,
- unique_ptr<module>&,
- bool first, // First time for this project.
- bool optional); // Loaded with 'using?' (optional module).
-
-
- struct module_state
- {
- bool boot; // True if the module boot'ed but not yet init'ed.
- module_init_function* init;
- unique_ptr<build::module> module;
- const location loc; // Boot location.
- };
-
- using loaded_module_map = std::map<string, module_state>;
-
- // Load and boot the specified module.
- //
- void
- boot_module (const string& name, scope& root, const location&);
-
- // Load (if not already loaded) and initialize the specified module. Used
- // by the parser but also by some modules to load prerequisite modules.
- // Return true if the module was both successfully loaded and configured
- // (false can only be returned if optional).
- //
- bool
- load_module (bool optional,
- const std::string& name,
- scope& root,
- scope& base,
- const location&);
-
- // Builtin modules.
- //
- struct module_functions
- {
- module_boot_function* boot;
- module_init_function* init;
- };
-
- using available_module_map = std::map<string, module_functions>;
- extern available_module_map builtin_modules;
-}
-
-#endif // BUILD_MODULE
diff --git a/build/module.cxx b/build/module.cxx
deleted file mode 100644
index 79a9bdb..0000000
--- a/build/module.cxx
+++ /dev/null
@@ -1,114 +0,0 @@
-// file : build/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/module>
-
-#include <utility> // make_pair()
-
-#include <build/scope>
-#include <build/variable>
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- available_module_map builtin_modules;
-
- void
- boot_module (const string& name, scope& rs, const location& loc)
- {
- // First see if this modules has already been loaded for this project.
- //
- loaded_module_map& lm (rs.modules);
- auto i (lm.find (name));
-
- if (i != lm.end ())
- {
- module_state& s (i->second);
-
- // The only valid situation here is if the module has already been
- // bootstrapped.
- //
- assert (s.boot);
- return;
- }
-
- // Otherwise search for this module.
- //
- auto j (builtin_modules.find (name));
-
- if (j == builtin_modules.end ())
- fail (loc) << "unknown module " << name;
-
- const module_functions& mf (j->second);
-
- if (mf.boot == nullptr)
- fail (loc) << "module " << name << " shouldn't be loaded in bootstrap";
-
- i = lm.emplace (name, module_state {true, mf.init, nullptr, loc}).first;
- mf.boot (rs, loc, i->second.module);
- }
-
- bool
- load_module (bool opt,
- const string& name,
- scope& rs,
- scope& bs,
- const location& loc)
- {
- // First see if this modules has already been loaded for this project.
- //
- loaded_module_map& lm (rs.modules);
- auto i (lm.find (name));
- bool f (i == lm.end ());
-
- if (f)
- {
- // Otherwise search for this module.
- //
- auto j (builtin_modules.find (name));
-
- if (j == builtin_modules.end ())
- {
- if (!opt)
- fail (loc) << "unknown module " << name;
- }
- else
- {
- const module_functions& mf (j->second);
-
- if (mf.boot != nullptr)
- fail (loc) << "module " << name << " should be loaded in bootstrap";
-
- i = lm.emplace (
- name, module_state {false, mf.init, nullptr, loc}).first;
- }
- }
- else
- {
- module_state& s (i->second);
-
- if (s.boot)
- {
- s.boot = false;
- f = true; // This is a first call to init.
- }
- }
-
- bool l (i != lm.end ());
- bool c (l && i->second.init (rs, bs, loc, i->second.module, f, opt));
-
- const variable& lv (var_pool.find (name + ".loaded",
- variable_visibility::project,
- bool_type));
- const variable& cv (var_pool.find (name + ".configured",
- variable_visibility::project,
- bool_type));
- bs.assign (lv) = l;
- bs.assign (cv) = c;
-
- return l && c;
- }
-}
diff --git a/build/name b/build/name
deleted file mode 100644
index ecf86cc..0000000
--- a/build/name
+++ /dev/null
@@ -1,113 +0,0 @@
-// file : build/name -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_NAME
-#define BUILD_NAME
-
-#include <string>
-#include <vector>
-#include <iosfwd>
-#include <utility> // move()
-
-#include <butl/path>
-
-// Note: include <build/types> instead of this file directly.
-//
-namespace build
-{
- using butl::dir_path;
-
- // A name is what we operate on by default. Depending on the context,
- // it can be interpreted as a target or prerequisite name. A name
- // without a type and directory can be used to represent any text.
- // A name with directory and empty value represents a directory.
- // A name may also be project-qualified. If the project name is
- // empty, then it means the name is in a project other than our
- // own (e.g., it is installed).
- //
- // If pair is not '\0', then this name and the next in the list
- // form a pair.
- //
- struct name
- {
- name () = default;
-
- explicit name (std::string v): value (std::move (v)) {}
- name& operator= (std::string v) {return *this = name (std::move (v));}
-
- explicit name (dir_path d): dir (std::move (d)) {}
- name& operator= (dir_path d) {return *this = name (std::move (d));}
-
- name (std::string t, std::string v)
- : type (std::move (t)), value (std::move (v)) {}
-
- name (dir_path d, std::string t, std::string v)
- : dir (std::move (d)), type (std::move (t)), value (std::move (v)) {}
-
- // The first argument should be from project_name_pool.
- //
- name (const std::string* p, dir_path d, std::string t, std::string v)
- : proj (p),
- dir (std::move (d)),
- type (std::move (t)),
- value (std::move (v)) {}
-
- bool
- qualified () const {return proj != nullptr;}
-
- bool
- unqualified () const {return proj == nullptr;}
-
- bool
- typed () const {return !type.empty ();}
-
- bool
- untyped () const {return type.empty ();}
-
- bool
- empty () const {return dir.empty () && value.empty ();}
-
- // Note that strictly speaking the following tests should be
- // orthogonal to qualification. However, the vast majority of
- // cases where we expect a simple or directory name, we also
- // expect it to be unqualified.
- //
- // Note also that empty name is simple but not a directory.
- //
- bool
- simple () const {return unqualified () && untyped () && dir.empty ();}
-
- bool
- directory () const
- {return unqualified () && untyped () && !dir.empty () && value.empty ();}
-
- const std::string* proj = nullptr; // Points to project_name_pool.
- dir_path dir;
- std::string type;
- std::string value;
- char pair = '\0'; // Pair symbol, if any.
- };
-
- inline bool
- operator== (const name& x, const name& y)
- {
- return x.proj == y.proj && // Pooled, so can just compare pointers.
- x.type == y.type &&
- x.dir == y.dir &&
- x.value == y.value;
- }
-
- inline bool
- operator!= (const name& x, const name& y) {return !(x == y);}
-
- typedef std::vector<name> names;
-
- std::ostream&
- operator<< (std::ostream&, const name&);
-
- std::ostream&
- operator<< (std::ostream&, const names&);
-}
-
-#endif // BUILD_NAME
diff --git a/build/name.cxx b/build/name.cxx
deleted file mode 100644
index 1bb8d31..0000000
--- a/build/name.cxx
+++ /dev/null
@@ -1,63 +0,0 @@
-// file : build/name.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/name>
-
-#include <ostream>
-
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- ostream&
- operator<< (ostream& os, const name& n)
- {
- if (n.proj != nullptr)
- os << *n.proj << '%';
-
- // If the value is empty, then we want to print the directory
- // inside {}, e.g., dir{bar/}, not bar/dir{}. We also want to
- // print {} for an empty name.
- //
- bool d (!n.dir.empty ());
- bool v (!n.value.empty ());
- bool t (!n.type.empty () || (!d && !v));
-
- if (v)
- os << n.dir;
-
- if (t)
- os << n.type << '{';
-
- if (v)
- os << n.value;
- else
- os << n.dir;
-
- if (t)
- os << '}';
-
- return os;
- }
-
- ostream&
- operator<< (ostream& os, const names& ns)
- {
- for (auto i (ns.begin ()), e (ns.end ()); i != e; )
- {
- const name& n (*i);
- ++i;
- os << n;
-
- if (n.pair != '\0')
- os << n.pair;
- else if (i != e)
- os << ' ';
- }
-
- return os;
- }
-}
diff --git a/build/operation b/build/operation
deleted file mode 100644
index d8cbd07..0000000
--- a/build/operation
+++ /dev/null
@@ -1,359 +0,0 @@
-// file : build/operation -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_OPERATION
-#define BUILD_OPERATION
-
-#include <string>
-#include <iosfwd>
-#include <vector>
-#include <cstdint>
-#include <functional> // reference_wrapper
-
-#include <butl/string-table>
-
-#include <build/types>
-
-namespace build
-{
- class location;
- class scope;
- class target_key;
-
- // 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;
-
- // Meta-operations and operations are not the end of the story. We
- // also have operation nesting (currently only one level deep) which
- // is used to implement pre/post operations (currently, but may be
- // useful for other things). Here is the idea: the test operation
- // needs to make sure that the targets that it needs to test are
- // up-to-date. So it runs update as its pre-operation. It is almost
- // like an ordinary update except that it has test as its outer
- // operation (the meta-operations are always the same). This way a
- // rule can recognize that this is "update for test" and do something
- // differently. For example, if an executable is not a test, then
- // there is no use updating it. At the same time, most rules will
- // ignore the fact that this is a nested update and for them it is
- // "update as usual".
- //
- struct action
- {
- action (): inner_id (0), outer_id (0) {} // Invalid action.
-
- bool
- valid () const {return inner_id != 0;}
-
- // If this is not a nested operation, then outer should be 0.
- //
- action (meta_operation_id m, operation_id inner, operation_id outer = 0)
- : inner_id ((m << 4) | inner),
- outer_id (outer == 0 ? 0 : (m << 4) | outer) {}
-
- meta_operation_id
- meta_operation () const {return inner_id >> 4;}
-
- operation_id
- operation () const {return inner_id & 0xF;}
-
- operation_id
- outer_operation () const {return outer_id & 0xF;}
-
- // Implicit conversion operator to action_id for the switch()
- // statement, etc. Most places will only care about the inner
- // operation.
- //
- operator action_id () const {return inner_id;}
-
- action_id inner_id;
- action_id outer_id;
- };
-
- // This is an "overrides" comparison, i.e., it returns true
- // if the recipe for x overrides recipe for y. The idea is
- // that for the same inner operation, action with an outer
- // operation is "weaker" than the one without.
- //
- inline bool
- operator> (action x, action y)
- {
- return x.inner_id != y.inner_id ||
- (x.outer_id != y.outer_id && y.outer_id != 0);
- }
-
- // Note that these ignore the outer operation.
- //
- inline bool
- operator== (action x, action y) {return x.inner_id == y.inner_id;}
-
- inline bool
- operator!= (action x, action y) {return !(x == y);}
-
- std::ostream&
- operator<< (std::ostream&, action);
-
- // Id constants for build-in and pre-defined meta/operations.
- //
- const meta_operation_id perform_id = 1;
- const meta_operation_id configure_id = 2;
- const meta_operation_id disfigure_id = 3;
- const meta_operation_id dist_id = 4;
-
- // The default operation is a special marker that can be used to
- // indicate that no operation was explicitly specified by the user.
- //
- const operation_id default_id = 1; // Shall be first.
- const operation_id update_id = 2;
- const operation_id clean_id = 3;
- const operation_id test_id = 4;
- const operation_id install_id = 5;
-
- const action_id perform_update_id = (perform_id << 4) | update_id;
- const action_id perform_clean_id = (perform_id << 4) | clean_id;
- const action_id perform_test_id = (perform_id << 4) | test_id;
- const action_id perform_install_id = (perform_id << 4) | install_id;
-
- const action_id configure_update_id = (configure_id << 4) | update_id;
-
- // Recipe execution mode.
- //
- // When a target is a prerequisite of another target, its recipe can be
- // executed before the dependent's recipe (the normal case) or after.
- // We will call these "front" and "back" execution modes, respectively
- // (think "the prerequisite is 'front-running' the dependent").
- //
- // There could also be several dependent targets and the prerequisite's
- // recipe can be execute as part of the first dependent (the normal
- // case) or last (or for all/some of them; see the recipe execution
- // protocol in <target>). We will call these "first" and "last"
- // execution modes, respectively.
- //
- // Now you may be having a hard time imagining where a mode other than
- // the normal one (first/front) could be useful. An the answer is,
- // compensating or inverse operations such as clean, uninstall, etc.
- // If we use the last/back mode for, say, clean, then we will remove
- // targets in the order inverse to the way they were updated. While
- // this sounds like an elegant idea, are there any practical benefits
- // of doing it this way. As it turns out there is (at least) one: when
- // we are removing a directory (see fsdir{}), we want to do it after
- // all the targets that depend on it (such as files, sub-directories)
- // were removed. If we do it before, then the directory won't be empty
- // yet.
- //
- // It appears that this execution mode is dictated by the essence of
- // the operation. Constructive operations (those that "do") seem to
- // naturally use the first/front mode. That is, we need to "do" the
- // prerequisite first before we can "do" the dependent. While the
- // destructive ones (those that "undo") seem to need last/back. That
- // is, we need to "undo" all the dependents before we can "undo" the
- // prerequisite (say, we need to remove all the files before we can
- // remove their directory).
- //
- // If you noticed the parallel with the way C++ construction and
- // destruction works for base/derived object then you earned a gold
- // star!
- //
- // Note that the front/back mode is realized in the dependen's recipe
- // (which is another indication that it is a property of the operation).
- //
- enum class execution_mode {first, last};
-
- // Meta-operation info.
- //
-
- // Normally a list of resolved and matched targets to execute. But
- // can be something else, depending on the meta-operation.
- //
- typedef std::vector<void*> action_targets;
-
- struct meta_operation_info
- {
- const std::string name;
-
- // Name derivatives for diagnostics. If empty, then the meta-
- // operation need not be mentioned.
- //
- const std::string name_do; // E.g., [to] 'configure'.
- const std::string name_doing; // E.g., [while] 'configuring'.
- const std::string name_done; // E.g., 'is configured'.
-
- // If operation_pre() is not NULL, then it may translate default_id
- // (and only default_id) to some other operation. If not translated,
- // then default_id is used. If, however, operation_pre() is NULL,
- // then default_id is translated to update_id.
- //
- void (*meta_operation_pre) (); // Start of meta-operation batch.
- operation_id (*operation_pre) (operation_id); // Start of operation batch.
-
- // Meta-operation-specific logic to load the buildfile, search and match
- // the targets, and execute the action on the targets.
- //
- void (*load) (const path& buildfile,
- scope& root,
- const dir_path& out_base,
- const dir_path& src_base,
- const location&);
-
- void (*search) (scope& root,
- const target_key&,
- const location&,
- action_targets&);
-
- void (*match) (action, action_targets&);
-
- void (*execute) (action, const action_targets&, bool quiet);
-
- void (*operation_post) (operation_id); // End of operation batch.
- void (*meta_operation_post) (); // End of meta-operation batch.
- };
-
- // Built-in meta-operations.
- //
-
- // perform
- //
-
- // Load the buildfile. This is the default implementation that first
- // calls root_pre(), then creates the scope for out_base, and, finally,
- // loads the buildfile unless it has already been loaded for the root
- // scope.
- //
- void
- load (const path& buildfile,
- scope& root,
- const dir_path& out_base,
- const dir_path& src_base,
- 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 (scope&, const target_key&, const location&, action_targets&);
-
- void
- match (action, action_targets&);
-
- // Execute the action on the list of targets. This is the default
- // implementation that does just that while issuing appropriate
- // diagnostics (unless quiet).
- //
- void
- execute (action, const action_targets&, bool quiet);
-
- extern meta_operation_info perform;
-
- // Operation info.
- //
- struct operation_info
- {
- const std::string name;
-
- // Name derivatives for diagnostics. Note that unlike meta-operations,
- // these can only be empty for the default operation (id 1), And
- // meta-operations that make use of the default operation shall not
- // have empty derivatives (failed which only target name will be
- // printed).
- //
- const std::string name_do; // E.g., [to] 'update'.
- const std::string name_doing; // E.g., [while] 'updating'.
- const std::string name_done; // E.g., 'is up to date'.
-
- const execution_mode mode;
-
- // If the returned operation_id's are not 0, then they are injected
- // as pre/post operations for this operation. Can be NULL if unused.
- // The returned operation_id shall not be default_id.
- //
- operation_id (*pre) (meta_operation_id);
- operation_id (*post) (meta_operation_id);
- };
-
- // Built-in operations.
- //
- extern operation_info default_;
- extern operation_info update;
- extern operation_info clean;
-
- // Global meta/operation tables. Each registered meta/operation
- // is assigned an id which is used as an index in the per-project
- // registered meta/operation lists.
- //
- // We have three types of meta/operations: built-in (e.g., perform,
- // update), pre-defined (e.g., configure, test), and dynamically-
- // defined. For built-in ones, both the id and implementation are
- // part of the build2 core. For pre-defined, the id is registered
- // as part of the core but the implementation is loaded as part of
- // a module. The idea with pre-defined operations is that they have
- // common, well-established semantics but could still be optional.
- // Another aspect of pre-defined operations is that often rules
- // across multiple modules need to know their ids. Finally,
- // dynamically-defined meta/operations have their ids registered
- // as part of a module load. In this case, the meta/operation is
- // normally (but not necessarily) fully implemented by this module.
- //
- // Note also that the name of a meta/operation in a sense defines
- // its semantics. It would be strange to have an operation called
- // test that does two very different things in different projects.
- //
- extern butl::string_table<meta_operation_id> meta_operation_table;
- extern butl::string_table<operation_id> operation_table;
-
- // These are "sparse" in the sense that we may have "holes" that
- // are represented as NULL pointers. Also, lookup out of bounds
- // is treated as a hole.
- //
- template <typename T>
- struct sparse_vector
- {
- using base_type = std::vector<T*>;
- using size_type = typename base_type::size_type;
-
- void
- insert (size_type i, T& x)
- {
- size_type n (v_.size ());
-
- if (i < n)
- v_[i] = &x;
- else
- {
- if (n != i)
- v_.resize (i, nullptr); // Add holes.
- v_.push_back (&x);
- }
- }
-
- T*
- operator[] (size_type i) const
- {
- return i < v_.size () ? v_[i] : nullptr;
- }
-
- bool
- empty () const {return v_.empty ();}
-
- // Note that this is more of a "max index" rather than size.
- //
- size_type
- size () const {return v_.size ();}
-
- private:
- base_type v_;
- };
-
- using meta_operations = sparse_vector<const meta_operation_info>;
- using operations = sparse_vector<const operation_info>;
-}
-
-#endif // BUILD_OPERATION
diff --git a/build/operation.cxx b/build/operation.cxx
deleted file mode 100644
index b92479b..0000000
--- a/build/operation.cxx
+++ /dev/null
@@ -1,232 +0,0 @@
-// file : build/operation.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/operation>
-
-#include <vector>
-#include <ostream>
-#include <cassert>
-#include <functional> // reference_wrapper
-
-#include <butl/utility> // reverse_iterate
-
-#include <build/scope>
-#include <build/target>
-#include <build/file>
-#include <build/algorithm>
-#include <build/diagnostics>
-#include <build/dump>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- // action
- //
- ostream&
- operator<< (ostream& os, action a)
- {
- uint16_t
- m (a.meta_operation ()),
- i (a.operation ()),
- o (a.outer_operation ());
-
- os << '(' << m << ',';
-
- if (o != 0)
- os << o << '(';
-
- os << i;
-
- if (o != 0)
- os << ')';
-
- os << ')';
-
- return os;
- }
-
- // perform
- //
- void
- load (const path& bf,
- scope& root,
- const dir_path& out_base,
- const dir_path& src_base,
- const location&)
- {
- // Load project's root[-pre].build.
- //
- load_root_pre (root);
-
- // Create the base scope. Note that its existence doesn't
- // mean it was already setup as a base scope; it can be the
- // same as root.
- //
- auto i (scopes.insert (out_base, nullptr, true, false));
- scope& base (setup_base (i, out_base, src_base));
-
- // Load the buildfile unless it has already been loaded.
- //
- source_once (bf, root, base, root);
- }
-
- void
- search (scope&,
- const target_key& tk,
- const location& l,
- action_targets& ts)
- {
- tracer trace ("search");
-
- auto i (targets.find (tk, trace));
- if (i == targets.end ())
- fail (l) << "unknown target " << tk;
-
- ts.push_back (i->get ());
- }
-
- void
- match (action a, action_targets& ts)
- {
- tracer trace ("match");
-
- if (verb >= 6)
- dump (a);
-
- for (void* vt: ts)
- {
- target& t (*static_cast<target*> (vt));
- level5 ([&]{trace << "matching " << t;});
- match (a, t);
- }
-
- if (verb >= 6)
- dump (a);
- }
-
- void
- execute (action a, const action_targets& ts, bool quiet)
- {
- tracer trace ("execute");
-
- // Execute collecting postponed targets (to be re-examined later).
- // Do it in reverse order if the execution mode is 'last'.
- //
- vector<reference_wrapper<target>> psp;
-
- auto body (
- [a, quiet, &psp, &trace] (void* v)
- {
- target& t (*static_cast<target*> (v));
-
- level5 ([&]{trace << diag_doing (a, t);});
-
- switch (execute (a, t))
- {
- case target_state::unchanged:
- {
- if (!quiet)
- info << diag_done (a, t);
- break;
- }
- case target_state::postponed:
- psp.push_back (t);
- break;
- case target_state::changed:
- break;
- case target_state::failed:
- //@@ This could probably happen in a parallel build.
- default:
- assert (false);
- }
- });
-
- if (current_mode == execution_mode::first)
- for (void* v: ts) body (v);
- else
- for (void* v: reverse_iterate (ts)) body (v);
-
- // We should have executed every target that we matched.
- //
- assert (dependency_count == 0);
-
- // Re-examine postponed targets. This is the only reliable way to
- // find out whether the target has changed.
- //
- for (target& t: psp)
- {
- switch (execute (a, t))
- {
- case target_state::unchanged:
- {
- if (!quiet)
- info << diag_done (a, t);
- break;
- }
- case target_state::changed:
- break;
- case target_state::postponed:
- assert (false);
- case target_state::failed:
- //@@ This could probably happen in a parallel build.
- default:
- assert (false);
- }
- }
- }
-
- meta_operation_info perform {
- "perform",
- "",
- "",
- "",
- nullptr, // meta-operation pre
- nullptr, // operation pre
- &load,
- &search,
- &match,
- &execute,
- nullptr, // operation post
- nullptr // meta-operation post
- };
-
- // operations
- //
- operation_info default_ {
- "<default>",
- "",
- "",
- "",
- execution_mode::first,
- nullptr,
- nullptr
- };
-
- operation_info update {
- "update",
- "update",
- "updating",
- "is up to date",
- execution_mode::first,
- nullptr,
- nullptr
- };
-
- operation_info clean {
- "clean",
- "clean",
- "cleaning",
- "is clean",
- execution_mode::last,
- nullptr,
- nullptr
- };
-
- // Tables.
- //
- string_table<meta_operation_id> meta_operation_table;
- string_table<operation_id> operation_table;
-}
diff --git a/build/options b/build/options
deleted file mode 100644
index a2fea2c..0000000
--- a/build/options
+++ /dev/null
@@ -1,298 +0,0 @@
-// -*- C++ -*-
-//
-// This file was generated by CLI, a command line interface
-// compiler for C++.
-//
-
-#ifndef BUILD_OPTIONS
-#define BUILD_OPTIONS
-
-// Begin prologue.
-//
-//
-// End prologue.
-
-#include <iosfwd>
-#include <string>
-#include <cstddef>
-#include <exception>
-
-namespace cl
-{
- class unknown_mode
- {
- public:
- enum value
- {
- skip,
- stop,
- fail
- };
-
- unknown_mode (value v);
-
- operator value () const
- {
- return v_;
- }
-
- private:
- value v_;
- };
-
- // Exceptions.
- //
-
- class exception: public std::exception
- {
- public:
- virtual void
- print (::std::ostream&) const = 0;
- };
-
- ::std::ostream&
- operator<< (::std::ostream&, const exception&);
-
- class unknown_option: public exception
- {
- public:
- virtual
- ~unknown_option () throw ();
-
- unknown_option (const std::string& option);
-
- const std::string&
- option () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string option_;
- };
-
- class unknown_argument: public exception
- {
- public:
- virtual
- ~unknown_argument () throw ();
-
- unknown_argument (const std::string& argument);
-
- const std::string&
- argument () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string argument_;
- };
-
- class missing_value: public exception
- {
- public:
- virtual
- ~missing_value () throw ();
-
- missing_value (const std::string& option);
-
- const std::string&
- option () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string option_;
- };
-
- class invalid_value: public exception
- {
- public:
- virtual
- ~invalid_value () throw ();
-
- invalid_value (const std::string& option,
- const std::string& value);
-
- const std::string&
- option () const;
-
- const std::string&
- value () const;
-
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
-
- private:
- std::string option_;
- std::string value_;
- };
-
- class eos_reached: public exception
- {
- public:
- virtual void
- print (::std::ostream&) const;
-
- virtual const char*
- what () const throw ();
- };
-
- class scanner
- {
- public:
- virtual
- ~scanner ();
-
- virtual bool
- more () = 0;
-
- virtual const char*
- peek () = 0;
-
- virtual const char*
- next () = 0;
-
- virtual void
- skip () = 0;
- };
-
- class argv_scanner: public scanner
- {
- public:
- argv_scanner (int& argc, char** argv, bool erase = false);
- argv_scanner (int start, int& argc, char** argv, bool erase = false);
-
- int
- end () const;
-
- virtual bool
- more ();
-
- virtual const char*
- peek ();
-
- virtual const char*
- next ();
-
- virtual void
- skip ();
-
- private:
- int i_;
- int& argc_;
- char** argv_;
- bool erase_;
- };
-
- template <typename X>
- struct parser;
-}
-
-#include <cstdint>
-
-class options
-{
- public:
- options ();
-
- options (int& argc,
- char** argv,
- bool erase = false,
- ::cl::unknown_mode option = ::cl::unknown_mode::fail,
- ::cl::unknown_mode argument = ::cl::unknown_mode::stop);
-
- options (int start,
- int& argc,
- char** argv,
- bool erase = false,
- ::cl::unknown_mode option = ::cl::unknown_mode::fail,
- ::cl::unknown_mode argument = ::cl::unknown_mode::stop);
-
- options (int& argc,
- char** argv,
- int& end,
- bool erase = false,
- ::cl::unknown_mode option = ::cl::unknown_mode::fail,
- ::cl::unknown_mode argument = ::cl::unknown_mode::stop);
-
- options (int start,
- int& argc,
- char** argv,
- int& end,
- bool erase = false,
- ::cl::unknown_mode option = ::cl::unknown_mode::fail,
- ::cl::unknown_mode argument = ::cl::unknown_mode::stop);
-
- options (::cl::scanner&,
- ::cl::unknown_mode option = ::cl::unknown_mode::fail,
- ::cl::unknown_mode argument = ::cl::unknown_mode::stop);
-
- // Option accessors.
- //
- const bool&
- help () const;
-
- const bool&
- version () const;
-
- const bool&
- v () const;
-
- const bool&
- q () const;
-
- const std::uint16_t&
- verbose () const;
-
- bool
- verbose_specified () const;
-
- // Print usage information.
- //
- static void
- print_usage (::std::ostream&);
-
- // Implementation details.
- //
- protected:
- bool
- _parse (const char*, ::cl::scanner&);
-
- private:
- void
- _parse (::cl::scanner&,
- ::cl::unknown_mode option,
- ::cl::unknown_mode argument);
-
- public:
- bool help_;
- bool version_;
- bool v_;
- bool q_;
- std::uint16_t verbose_;
- bool verbose_specified_;
-};
-
-#include <build/options.ixx>
-
-// Begin epilogue.
-//
-//
-// End epilogue.
-
-#endif // BUILD_OPTIONS
diff --git a/build/options.cli b/build/options.cli
deleted file mode 100644
index 9bd1dcd..0000000
--- a/build/options.cli
+++ /dev/null
@@ -1,31 +0,0 @@
-// file : build/options.cli
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-include <cstdint>;
-
-class options
-{
- bool --help {"Print usage information and exit."};
- bool --version {"Print version and exit."};
-
- bool -v
- {
- "Print actual commands being executed. This is equivalent to
- \cb{--verbose 2}."
- };
-
- bool -q
- {
- "Run quietly, only printing error messages. This is equivalent
- to \cb{--verbose 0}."
- };
-
- std::uint16_t --verbose = 1
- {
- "<level>",
- "Set the diagnostics verbosity to <level> between 0 (disabled) and
- 6 (lots of information). The default is 1. @@ Need to document
- further."
- };
-};
diff --git a/build/options.cxx b/build/options.cxx
deleted file mode 100644
index b5c2754..0000000
--- a/build/options.cxx
+++ /dev/null
@@ -1,552 +0,0 @@
-// -*- C++ -*-
-//
-// This file was generated by CLI, a command line interface
-// compiler for C++.
-//
-
-// Begin prologue.
-//
-//
-// End prologue.
-
-#include <build/options>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-#include <ostream>
-#include <sstream>
-
-namespace cl
-{
- // unknown_option
- //
- unknown_option::
- ~unknown_option () throw ()
- {
- }
-
- void unknown_option::
- print (::std::ostream& os) const
- {
- os << "unknown option '" << option ().c_str () << "'";
- }
-
- const char* unknown_option::
- what () const throw ()
- {
- return "unknown option";
- }
-
- // unknown_argument
- //
- unknown_argument::
- ~unknown_argument () throw ()
- {
- }
-
- void unknown_argument::
- print (::std::ostream& os) const
- {
- os << "unknown argument '" << argument ().c_str () << "'";
- }
-
- const char* unknown_argument::
- what () const throw ()
- {
- return "unknown argument";
- }
-
- // missing_value
- //
- missing_value::
- ~missing_value () throw ()
- {
- }
-
- void missing_value::
- print (::std::ostream& os) const
- {
- os << "missing value for option '" << option ().c_str () << "'";
- }
-
- const char* missing_value::
- what () const throw ()
- {
- return "missing option value";
- }
-
- // invalid_value
- //
- invalid_value::
- ~invalid_value () throw ()
- {
- }
-
- void invalid_value::
- print (::std::ostream& os) const
- {
- os << "invalid value '" << value ().c_str () << "' for option '"
- << option ().c_str () << "'";
- }
-
- const char* invalid_value::
- what () const throw ()
- {
- return "invalid option value";
- }
-
- // eos_reached
- //
- void eos_reached::
- print (::std::ostream& os) const
- {
- os << what ();
- }
-
- const char* eos_reached::
- what () const throw ()
- {
- return "end of argument stream reached";
- }
-
- // scanner
- //
- scanner::
- ~scanner ()
- {
- }
-
- // argv_scanner
- //
- bool argv_scanner::
- more ()
- {
- return i_ < argc_;
- }
-
- const char* argv_scanner::
- peek ()
- {
- if (i_ < argc_)
- return argv_[i_];
- else
- throw eos_reached ();
- }
-
- const char* argv_scanner::
- next ()
- {
- if (i_ < argc_)
- {
- const char* r (argv_[i_]);
-
- if (erase_)
- {
- for (int i (i_ + 1); i < argc_; ++i)
- argv_[i - 1] = argv_[i];
-
- --argc_;
- argv_[argc_] = 0;
- }
- else
- ++i_;
-
- return r;
- }
- else
- throw eos_reached ();
- }
-
- void argv_scanner::
- skip ()
- {
- if (i_ < argc_)
- ++i_;
- else
- throw eos_reached ();
- }
-
- template <typename X>
- struct parser
- {
- static void
- parse (X& x, bool& xs, scanner& s)
- {
- std::string o (s.next ());
-
- if (s.more ())
- {
- std::string v (s.next ());
- std::istringstream is (v);
- if (!(is >> x && is.eof ()))
- throw invalid_value (o, v);
- }
- else
- throw missing_value (o);
-
- xs = true;
- }
- };
-
- template <>
- struct parser<bool>
- {
- static void
- parse (bool& x, scanner& s)
- {
- s.next ();
- x = true;
- }
- };
-
- template <>
- struct parser<std::string>
- {
- static void
- parse (std::string& x, bool& xs, scanner& s)
- {
- const char* o (s.next ());
-
- if (s.more ())
- x = s.next ();
- else
- throw missing_value (o);
-
- xs = true;
- }
- };
-
- template <typename X>
- struct parser<std::vector<X> >
- {
- static void
- parse (std::vector<X>& c, bool& xs, scanner& s)
- {
- X x;
- bool dummy;
- parser<X>::parse (x, dummy, s);
- c.push_back (x);
- xs = true;
- }
- };
-
- template <typename X>
- struct parser<std::set<X> >
- {
- static void
- parse (std::set<X>& c, bool& xs, scanner& s)
- {
- X x;
- bool dummy;
- parser<X>::parse (x, dummy, s);
- c.insert (x);
- xs = true;
- }
- };
-
- template <typename K, typename V>
- struct parser<std::map<K, V> >
- {
- static void
- parse (std::map<K, V>& m, bool& xs, scanner& s)
- {
- std::string o (s.next ());
-
- if (s.more ())
- {
- std::string ov (s.next ());
- std::string::size_type p = ov.find ('=');
-
- if (p == std::string::npos)
- {
- K k = K ();
-
- if (!ov.empty ())
- {
- std::istringstream ks (ov);
-
- if (!(ks >> k && ks.eof ()))
- throw invalid_value (o, ov);
- }
-
- m[k] = V ();
- }
- else
- {
- K k = K ();
- V v = V ();
- std::string kstr (ov, 0, p);
- std::string vstr (ov, p + 1);
-
- if (!kstr.empty ())
- {
- std::istringstream ks (kstr);
-
- if (!(ks >> k && ks.eof ()))
- throw invalid_value (o, ov);
- }
-
- if (!vstr.empty ())
- {
- std::istringstream vs (vstr);
-
- if (!(vs >> v && vs.eof ()))
- throw invalid_value (o, ov);
- }
-
- m[k] = v;
- }
- }
- else
- throw missing_value (o);
-
- xs = true;
- }
- };
-
- template <typename X, typename T, T X::*M>
- void
- thunk (X& x, scanner& s)
- {
- parser<T>::parse (x.*M, s);
- }
-
- template <typename X, typename T, T X::*M, bool X::*S>
- void
- thunk (X& x, scanner& s)
- {
- parser<T>::parse (x.*M, x.*S, s);
- }
-}
-
-#include <map>
-#include <cstring>
-
-// options
-//
-
-options::
-options ()
-: help_ (),
- version_ (),
- v_ (),
- q_ (),
- verbose_ (1),
- verbose_specified_ (false)
-{
-}
-
-options::
-options (int& argc,
- char** argv,
- bool erase,
- ::cl::unknown_mode opt,
- ::cl::unknown_mode arg)
-: help_ (),
- version_ (),
- v_ (),
- q_ (),
- verbose_ (1),
- verbose_specified_ (false)
-{
- ::cl::argv_scanner s (argc, argv, erase);
- _parse (s, opt, arg);
-}
-
-options::
-options (int start,
- int& argc,
- char** argv,
- bool erase,
- ::cl::unknown_mode opt,
- ::cl::unknown_mode arg)
-: help_ (),
- version_ (),
- v_ (),
- q_ (),
- verbose_ (1),
- verbose_specified_ (false)
-{
- ::cl::argv_scanner s (start, argc, argv, erase);
- _parse (s, opt, arg);
-}
-
-options::
-options (int& argc,
- char** argv,
- int& end,
- bool erase,
- ::cl::unknown_mode opt,
- ::cl::unknown_mode arg)
-: help_ (),
- version_ (),
- v_ (),
- q_ (),
- verbose_ (1),
- verbose_specified_ (false)
-{
- ::cl::argv_scanner s (argc, argv, erase);
- _parse (s, opt, arg);
- end = s.end ();
-}
-
-options::
-options (int start,
- int& argc,
- char** argv,
- int& end,
- bool erase,
- ::cl::unknown_mode opt,
- ::cl::unknown_mode arg)
-: help_ (),
- version_ (),
- v_ (),
- q_ (),
- verbose_ (1),
- verbose_specified_ (false)
-{
- ::cl::argv_scanner s (start, argc, argv, erase);
- _parse (s, opt, arg);
- end = s.end ();
-}
-
-options::
-options (::cl::scanner& s,
- ::cl::unknown_mode opt,
- ::cl::unknown_mode arg)
-: help_ (),
- version_ (),
- v_ (),
- q_ (),
- verbose_ (1),
- verbose_specified_ (false)
-{
- _parse (s, opt, arg);
-}
-
-void options::
-print_usage (::std::ostream& os)
-{
- os << "--help Print usage information and exit." << ::std::endl;
-
- os << "--version Print version and exit." << ::std::endl;
-
- os << "-v Print actual commands being executed." << ::std::endl;
-
- os << "-q Run quietly, only printing error messages." << ::std::endl;
-
- os << "--verbose <level> Set the diagnostics verbosity to <level> between 0 (disabled)" << ::std::endl
- << " and 6 (lots of information)." << ::std::endl;
-}
-
-typedef
-std::map<std::string, void (*) (options&, ::cl::scanner&)>
-_cli_options_map;
-
-static _cli_options_map _cli_options_map_;
-
-struct _cli_options_map_init
-{
- _cli_options_map_init ()
- {
- _cli_options_map_["--help"] =
- &::cl::thunk< options, bool, &options::help_ >;
- _cli_options_map_["--version"] =
- &::cl::thunk< options, bool, &options::version_ >;
- _cli_options_map_["-v"] =
- &::cl::thunk< options, bool, &options::v_ >;
- _cli_options_map_["-q"] =
- &::cl::thunk< options, bool, &options::q_ >;
- _cli_options_map_["--verbose"] =
- &::cl::thunk< options, std::uint16_t, &options::verbose_,
- &options::verbose_specified_ >;
- }
-};
-
-static _cli_options_map_init _cli_options_map_init_;
-
-bool options::
-_parse (const char* o, ::cl::scanner& s)
-{
- _cli_options_map::const_iterator i (_cli_options_map_.find (o));
-
- if (i != _cli_options_map_.end ())
- {
- (*(i->second)) (*this, s);
- return true;
- }
-
- return false;
-}
-
-void options::
-_parse (::cl::scanner& s,
- ::cl::unknown_mode opt_mode,
- ::cl::unknown_mode arg_mode)
-{
- bool opt = true;
-
- while (s.more ())
- {
- const char* o = s.peek ();
-
- if (std::strcmp (o, "--") == 0)
- {
- s.skip ();
- opt = false;
- continue;
- }
-
- if (opt && _parse (o, s));
- else if (opt && std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
- {
- switch (opt_mode)
- {
- case ::cl::unknown_mode::skip:
- {
- s.skip ();
- continue;
- }
- case ::cl::unknown_mode::stop:
- {
- break;
- }
- case ::cl::unknown_mode::fail:
- {
- throw ::cl::unknown_option (o);
- }
- }
-
- break;
- }
- else
- {
- switch (arg_mode)
- {
- case ::cl::unknown_mode::skip:
- {
- s.skip ();
- continue;
- }
- case ::cl::unknown_mode::stop:
- {
- break;
- }
- case ::cl::unknown_mode::fail:
- {
- throw ::cl::unknown_argument (o);
- }
- }
-
- break;
- }
- }
-}
-
-// Begin epilogue.
-//
-//
-// End epilogue.
-
diff --git a/build/options.ixx b/build/options.ixx
deleted file mode 100644
index c44d7e5..0000000
--- a/build/options.ixx
+++ /dev/null
@@ -1,157 +0,0 @@
-// -*- C++ -*-
-//
-// This file was generated by CLI, a command line interface
-// compiler for C++.
-//
-
-// Begin prologue.
-//
-//
-// End prologue.
-
-namespace cl
-{
- // unknown_mode
- //
- inline unknown_mode::
- unknown_mode (value v)
- : v_ (v)
- {
- }
-
- // exception
- //
- inline ::std::ostream&
- operator<< (::std::ostream& os, const exception& e)
- {
- e.print (os);
- return os;
- }
-
- // unknown_option
- //
- inline unknown_option::
- unknown_option (const std::string& option)
- : option_ (option)
- {
- }
-
- inline const std::string& unknown_option::
- option () const
- {
- return option_;
- }
-
- // unknown_argument
- //
- inline unknown_argument::
- unknown_argument (const std::string& argument)
- : argument_ (argument)
- {
- }
-
- inline const std::string& unknown_argument::
- argument () const
- {
- return argument_;
- }
-
- // missing_value
- //
- inline missing_value::
- missing_value (const std::string& option)
- : option_ (option)
- {
- }
-
- inline const std::string& missing_value::
- option () const
- {
- return option_;
- }
-
- // invalid_value
- //
- inline invalid_value::
- invalid_value (const std::string& option,
- const std::string& value)
- : option_ (option), value_ (value)
- {
- }
-
- inline const std::string& invalid_value::
- option () const
- {
- return option_;
- }
-
- inline const std::string& invalid_value::
- value () const
- {
- return value_;
- }
-
- // argv_scanner
- //
- inline argv_scanner::
- argv_scanner (int& argc, char** argv, bool erase)
- : i_ (1), argc_ (argc), argv_ (argv), erase_ (erase)
- {
- }
-
- inline argv_scanner::
- argv_scanner (int start, int& argc, char** argv, bool erase)
- : i_ (start), argc_ (argc), argv_ (argv), erase_ (erase)
- {
- }
-
- inline int argv_scanner::
- end () const
- {
- return i_;
- }
-}
-
-// options
-//
-
-inline const bool& options::
-help () const
-{
- return this->help_;
-}
-
-inline const bool& options::
-version () const
-{
- return this->version_;
-}
-
-inline const bool& options::
-v () const
-{
- return this->v_;
-}
-
-inline const bool& options::
-q () const
-{
- return this->q_;
-}
-
-inline const std::uint16_t& options::
-verbose () const
-{
- return this->verbose_;
-}
-
-inline bool options::
-verbose_specified () const
-{
- return this->verbose_specified_;
-}
-
-// Begin epilogue.
-//
-//
-// End epilogue.
diff --git a/build/parser b/build/parser
deleted file mode 100644
index 2631ca2..0000000
--- a/build/parser
+++ /dev/null
@@ -1,296 +0,0 @@
-// file : build/parser -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_PARSER
-#define BUILD_PARSER
-
-#include <string>
-#include <iosfwd>
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/spec>
-#include <build/lexer>
-#include <build/token>
-#include <build/variable> // list_value
-#include <build/diagnostics>
-
-namespace build
-{
- class scope;
- class target;
-
- class parser
- {
- public:
- typedef build::names names_type;
- typedef build::variable variable_type;
-
- // If boot is true, then we are parsing bootstrap.build and modules
- // should only be bootstrapped.
- //
- parser (bool boot = false): fail (&path_), boot_ (boot) {}
-
- // Issue diagnostics and throw failed in case of an error.
- //
- void
- parse_buildfile (std::istream&, const path&, scope& root, scope& base);
-
- buildspec
- parse_buildspec (std::istream&, const std::string& name);
-
- token
- parse_variable (lexer&, scope&, std::string name, token_type kind);
-
- names_type
- parse_export_stub (std::istream& is, const path& p, scope& r, scope& b)
- {
- parse_buildfile (is, p, r, b);
- return std::move (export_value_);
- }
-
- // Recursive descent parser.
- //
- protected:
- void
- clause (token&, token_type&);
-
- void
- print (token&, token_type&);
-
- void
- source (token&, token_type&);
-
- void
- include (token&, token_type&);
-
- void
- import (token&, token_type&);
-
- void
- export_ (token&, token_type&);
-
- void
- using_ (token&, token_type&);
-
- void
- define (token&, token_type&);
-
- void
- if_else (token&, token_type&);
-
- void
- variable (token&, token_type&, std::string name, token_type kind);
-
- std::string
- variable_name (names_type&&, const location&);
-
- names_type
- variable_value (token&, token_type&, const variable_type&);
-
- names_type
- eval (token&, token_type&);
-
- // If chunk is true, then parse the smallest but complete, name-wise,
- // chunk of input. Note that in this case you may still end up with
- // multiple names, for example, {foo bar}.
- //
- names_type
- names (token& t, token_type& tt, bool chunk = false)
- {
- names_type ns;
- names (t, tt, ns, chunk, 0, nullptr, nullptr, nullptr);
- return ns;
- }
-
- void
- names (token&, token_type&,
- names_type&,
- bool chunk,
- std::size_t pair,
- const std::string* prj,
- const dir_path* dir,
- const std::string* type);
-
- size_t
- names_trailer (token&, token_type&,
- names_type&,
- size_t pair,
- const std::string* prj,
- const dir_path* dir,
- const std::string* type);
-
- // Skip until newline or eos.
- //
- void
- skip_line (token&, token_type&);
-
- // Skip until block-closing } or eos, taking into account nested blocks.
- //
- void
- skip_block (token&, token_type&);
-
- // Return true if the name token can be considered a directive keyword.
- //
- bool
- keyword (token&);
-
- // Buildspec.
- //
- buildspec
- buildspec_clause (token&, token_type&, token_type end);
-
- // Utilities.
- //
- protected:
-
- // Switch to a new current scope. Note that this function might
- // also have to switch to a new root scope if the new current
- // scope is in another project. So both must be saved and
- // restored.
- //
- void
- switch_scope (const dir_path&);
-
- void
- process_default_target (token&);
-
- // Enter buildfile as a target.
- //
- void
- enter_buildfile (const path&);
-
- // Lexer.
- //
- protected:
- token_type
- next (token&, token_type&);
-
- // Be careful with peeking and switching the lexer mode. See keyword()
- // for more information.
- //
- token_type
- peek ();
-
- const token&
- peeked () const
- {
- assert (peeked_);
- return peek_;
- }
-
- void
- mode (lexer_mode m, char ps = '=')
- {
- if (replay_ != replay::play)
- lexer_->mode (m, ps);
- }
-
- lexer_mode
- mode () const
- {
- assert (replay_ != replay::play);
- return lexer_->mode ();
- }
-
- void
- expire_mode ()
- {
- if (replay_ != replay::play)
- lexer_->expire_mode ();
- }
-
- // Token saving and replaying. Note that is can only be used in certain
- // contexts. Specifically, the lexer mode should be the same and the code
- // that parses a replay must not interact with the lexer directly (e.g.,
- // the keyword() test). For now we don't enforce any of this.
- //
- // Note also that the peeked token is not part of the replay, until it
- // is "got".
- //
- //
- void
- replay_save ()
- {
- assert (replay_ == replay::stop);
- replay_ = replay::save;
- }
-
- void
- replay_play ()
- {
- assert ((replay_ == replay::save && !replay_data_.empty ()) ||
- (replay_ == replay::play && replay_i_ == replay_data_.size ()));
-
- replay_i_ = 0;
- replay_ = replay::play;
- }
-
- void
- replay_stop ()
- {
- replay_data_.clear ();
- replay_ = replay::stop;
- }
-
- const token&
- replay_next ()
- {
- assert (replay_i_ != replay_data_.size ());
- return replay_data_[replay_i_++];
- }
-
- struct replay_guard
- {
- replay_guard (parser& p, bool start = true)
- : p_ (start ? &p : nullptr)
- {
- if (p_ != nullptr)
- p_->replay_save ();
- }
-
- void
- play ()
- {
- if (p_ != nullptr)
- p_->replay_play ();
- }
-
- ~replay_guard ()
- {
- if (p_ != nullptr)
- p_->replay_stop ();
- }
-
- private:
- parser* p_;
- };
-
- // Diagnostics.
- //
- protected:
- const fail_mark<failed> fail;
-
- protected:
- bool boot_;
-
- const std::string* path_; // Path processed by diag_relative() and pooled.
- lexer* lexer_;
- target* target_; // Current target, if any.
- scope* scope_; // Current base scope (out_base).
- scope* root_; // Current root scope (out_root).
- target* default_target_;
- names_type export_value_;
-
- token peek_ = token (token_type::eos, false, 0, 0);
- bool peeked_ = false;
-
- enum class replay {stop, save, play} replay_ = replay::stop;
- vector<token> replay_data_;
- size_t replay_i_; // Position of the next token during replay.
- };
-}
-
-#endif // BUILD_PARSER
diff --git a/build/parser.cxx b/build/parser.cxx
deleted file mode 100644
index ad17ae9..0000000
--- a/build/parser.cxx
+++ /dev/null
@@ -1,2206 +0,0 @@
-// file : build/parser.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/parser>
-
-#include <cctype> // is{alpha alnum}()
-
-#include <memory> // unique_ptr
-#include <fstream>
-#include <utility> // move()
-#include <iterator> // make_move_iterator()
-#include <iostream>
-
-#include <build/types>
-#include <build/utility>
-#include <build/version>
-
-#include <build/scope>
-#include <build/target>
-#include <build/prerequisite>
-#include <build/variable>
-#include <build/module>
-#include <build/file>
-#include <build/diagnostics>
-#include <build/context>
-
-using namespace std;
-
-namespace build
-{
- static location
- get_location (const token&, const void*);
-
- typedef token_type type;
-
- void parser::
- parse_buildfile (istream& is, const path& p, scope& root, scope& base)
- {
- enter_buildfile (p);
-
- path_ = &path_pool.find (diag_relative (p)); // Relative to work.
-
- lexer l (is, *path_);
- lexer_ = &l;
- target_ = nullptr;
- scope_ = &base;
- root_ = &root;
- default_target_ = nullptr;
-
- token t (type::eos, false, 0, 0);
- type tt;
- next (t, tt);
-
- clause (t, tt);
-
- if (tt != type::eos)
- fail (t) << "unexpected " << t;
-
- process_default_target (t);
- }
-
- token parser::
- parse_variable (lexer& l, scope& s, string name, type kind)
- {
- path_ = &l.name (); // Note: not pooled.
- lexer_ = &l;
- target_ = nullptr;
- scope_ = &s;
-
- type tt;
- token t (type::eos, false, 0, 0);
- variable (t, tt, name, kind);
- return t;
- }
-
- void parser::
- clause (token& t, type& tt)
- {
- tracer trace ("parser::clause", &path_);
-
- // clause() should always stop at a token that is at the beginning of
- // the line (except for eof). That is, if something is called to parse
- // a line, it should parse it until newline (or fail). This is important
- // for if-else blocks, directory scopes, etc., that assume the } token
- // they see is on the new line.
- //
- while (tt != type::eos)
- {
- // We always start with one or more names.
- //
- if (tt != type::name &&
- tt != type::lcbrace && // Untyped name group: '{foo ...'
- tt != type::dollar && // Variable expansion: '$foo ...'
- tt != type::lparen && // Eval context: '(foo) ...'
- tt != type::colon) // Empty name: ': ...'
- break; // Something else. Let our caller handle that.
-
- // See if this is one of the directives.
- //
- if (tt == type::name && keyword (t))
- {
- const string& n (t.value);
-
- if (n == "print")
- {
- // @@ Is this the only place where it is valid? Probably also
- // in var namespace.
- //
- print (t, tt);
- continue;
- }
- else if (n == "source")
- {
- source (t, tt);
- continue;
- }
- else if (n == "include")
- {
- include (t, tt);
- continue;
- }
- else if (n == "import")
- {
- import (t, tt);
- continue;
- }
- else if (n == "export")
- {
- export_ (t, tt);
- continue;
- }
- else if (n == "using" ||
- n == "using?")
- {
- using_ (t, tt);
- continue;
- }
- else if (n == "define")
- {
- define (t, tt);
- continue;
- }
- else if (n == "if" ||
- n == "if!")
- {
- if_else (t, tt);
- continue;
- }
- else if (n == "else" ||
- n == "elif" ||
- n == "elif!")
- {
- // Valid ones are handled in if_else().
- //
- fail (t) << n << " without if";
- }
- }
-
- // ': foo' is equvalent to '{}: foo' and to 'dir{}: foo'.
- //
- const location nloc (get_location (t, &path_));
- names_type ns (tt != type::colon
- ? names (t, tt)
- : names_type ({name ("dir", string ())}));
-
- if (tt == type::colon)
- {
- // While '{}:' means empty name, '{$x}:' where x is empty list
- // means empty list.
- //
- if (ns.empty ())
- fail (t) << "target expected before :";
-
- next (t, tt);
-
- if (tt == type::newline)
- {
- // See if this is a directory/target scope.
- //
- if (peek () == type::lcbrace)
- {
- next (t, tt);
-
- // Should be on its own line.
- //
- if (next (t, tt) != type::newline)
- fail (t) << "expected newline after {";
-
- // See if this is a directory or target scope. Different
- // things can appear inside depending on which one it is.
- //
- bool dir (false);
- for (const auto& n: ns)
- {
- // A name represents directory as an empty value.
- //
- if (n.directory ())
- {
- if (ns.size () != 1)
- {
- // @@ TODO: point to name (and above).
- //
- fail (nloc) << "multiple names in directory scope";
- }
-
- dir = true;
- }
- }
-
- next (t, tt);
-
- if (dir)
- {
- // Directory scope.
- //
- dir_path p (move (ns[0].dir)); // Steal.
-
- // Relative scopes are opened relative to out, not src.
- //
- if (p.relative ())
- p = scope_->out_path () / p;
-
- p.normalize ();
-
- scope* ors (root_);
- scope* ocs (scope_);
- switch_scope (p);
-
- // A directory scope can contain anything that a top level can.
- //
- clause (t, tt);
-
- scope_ = ocs;
- root_ = ors;
- }
- else
- {
- // @@ TODO: target scope.
- }
-
- if (tt != type::rcbrace)
- fail (t) << "expected } instead of " << t;
-
- // Should be on its own line.
- //
- if (next (t, tt) == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline after }";
-
- continue;
- }
-
- // If this is not a scope, then it is a target without any
- // prerequisites.
- //
- }
-
- // Dependency declaration or scope/target-specific variable
- // assignment.
- //
- if (tt == type::name ||
- tt == type::lcbrace ||
- tt == type::dollar ||
- tt == type::lparen ||
- tt == type::newline ||
- tt == type::eos)
- {
- const location ploc (get_location (t, &path_));
- names_type pns (tt != type::newline && tt != type::eos
- ? names (t, tt)
- : names_type ());
-
- // Common target entering code used in both cases.
- //
- auto enter_target = [this, &nloc, &trace] (name&& tn) -> target&
- {
- const string* e;
- const target_type* ti (scope_->find_target_type (tn, e));
-
- if (ti == nullptr)
- fail (nloc) << "unknown target type " << tn.type;
-
- path& d (tn.dir);
-
- if (d.empty ())
- d = scope_->out_path (); // Already normalized.
- else
- {
- if (d.relative ())
- d = scope_->out_path () / d;
-
- d.normalize ();
- }
-
- // Find or insert.
- //
- return targets.insert (
- *ti, move (tn.dir), move (tn.value), e, trace).first;
- };
-
- // Scope/target-specific variable assignment.
- //
- if (tt == type::equal ||
- tt == type::equal_plus ||
- tt == type::plus_equal)
- {
- token at (t);
- type att (tt);
-
- string v (variable_name (move (pns), ploc));
-
- // If we have multiple targets/scopes, then we save the value
- // tokens when parsing the first one and then replay them for
- // the subsequent. We have to do it this way because the value
- // may contain variable expansions that would be sensitive to
- // the target/scope context in which they are evaluated.
- //
- replay_guard rg (*this, ns.size () > 1);
-
- for (name& n: ns)
- {
- if (n.qualified ())
- fail (nloc) << "project name in scope/target " << n;
-
- if (n.directory ())
- {
- // The same code as in directory scope handling code above.
- //
- dir_path p (move (n.dir));
-
- if (p.relative ())
- p = scope_->out_path () / p;
-
- p.normalize ();
-
- scope* ors (root_);
- scope* ocs (scope_);
- switch_scope (p);
-
- variable (t, tt, v, att);
-
- scope_ = ocs;
- root_ = ors;
- }
- else
- {
- // Figure out if this is a target or type/pattern specific
- // variable.
- //
- size_t p (n.value.find ('*'));
-
- if (p == string::npos)
- {
- target* ot (target_);
- target_ = &enter_target (move (n));
- variable (t, tt, v, att);
- target_ = ot;
- }
- else
- {
- // See tests/variable/type-pattern.
- //
- if (!n.dir.empty ())
- fail (nloc) << "directory in target type/pattern " << n;
-
- if (n.value.find ('*', p + 1) != string::npos)
- fail (nloc) << "multiple wildcards in target type/pattern "
- << n;
-
- // Resolve target type. If none is specified, use the root
- // of the hierarchy.
- //
- const target_type* ti (
- n.untyped ()
- ? &target::static_type
- : scope_->find_target_type (n.type));
-
- if (ti == nullptr)
- fail (nloc) << "unknown target type " << n.type;
-
- if (att == type::equal_plus)
- fail (at) << "prepend to target type/pattern-specific "
- << "variable " << v;
-
- if (att == type::plus_equal)
- fail (at) << "append to target type/pattern-specific "
- << "variable " << v;
-
- const auto& var (var_pool.find (v));
-
- // Note: expand variables in the value in the context of
- // the scope.
- //
- names_type vns (variable_value (t, tt, var));
- value& val (scope_->target_vars[*ti][move (n.value)].assign (
- var).first);
- val.assign (move (vns), var);
- }
- }
-
- rg.play (); // Replay.
- }
- }
- // Dependency declaration.
- //
- else
- {
- // Prepare the prerequisite list.
- //
- target::prerequisites_type ps;
- ps.reserve (pns.size ());
-
- for (auto& pn: pns)
- {
- const string* e;
- const target_type* ti (scope_->find_target_type (pn, e));
-
- if (ti == nullptr)
- fail (ploc) << "unknown target type " << pn.type;
-
- pn.dir.normalize ();
-
- // Find or insert.
- //
- prerequisite& p (
- scope_->prerequisites.insert (
- pn.proj,
- *ti,
- move (pn.dir),
- move (pn.value),
- e,
- *scope_,
- trace).first);
-
- ps.emplace_back (p);
- }
-
- for (auto& tn: ns)
- {
- if (tn.qualified ())
- fail (nloc) << "project name in target " << tn;
-
- target& t (enter_target (move (tn)));
-
- //@@ OPT: move if last/single target (common cases).
- //
- t.prerequisites.insert (t.prerequisites.end (),
- ps.begin (),
- ps.end ());
-
- if (default_target_ == nullptr)
- default_target_ = &t;
- }
- }
-
- if (tt == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline instead of " << t;
-
- continue;
- }
-
- if (tt == type::eos)
- continue;
-
- fail (t) << "expected newline instead of " << t;
- }
-
- // Variable assignment.
- //
- if (tt == type::equal ||
- tt == type::equal_plus ||
- tt == type::plus_equal)
- {
- variable (t, tt, variable_name (move (ns), nloc), tt);
-
- if (tt == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline instead of " << t;
-
- continue;
- }
-
- // Allow things like function calls that don't result in anything.
- //
- if (tt == type::newline && ns.empty ())
- {
- next (t, tt);
- continue;
- }
-
- fail (t) << "unexpected " << t;
- }
- }
-
- void parser::
- source (token& t, type& tt)
- {
- tracer trace ("parser::source", &path_);
-
- // The rest should be a list of buildfiles. Parse them as names
- // to get variable expansion and directory prefixes.
- //
- mode (lexer_mode::value);
- next (t, tt);
- const 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.qualified () || n.empty () || n.value.empty ())
- fail (l) << "expected buildfile instead of " << n;
-
- // Construct the buildfile path.
- //
- path p (move (n.dir));
- p /= path (move (n.value));
-
- // If the path is relative then use the src directory corresponding
- // to the current directory scope.
- //
- if (root_->src_path_ != nullptr && p.relative ())
- p = src_out (scope_->out_path (), *root_) / p;
-
- p.normalize ();
-
- try
- {
- ifstream ifs (p.string ());
-
- if (!ifs.is_open ())
- fail (l) << "unable to open " << p;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
-
- level5 ([&]{trace (t) << "entering " << p;});
-
- enter_buildfile (p);
-
- const string* op (path_);
- path_ = &path_pool.find (diag_relative (p)); // Relative to work.
-
- lexer l (ifs, *path_);
- lexer* ol (lexer_);
- lexer_ = &l;
-
- token t (type::eos, false, 0, 0);
- type tt;
- next (t, tt);
- clause (t, tt);
-
- if (tt != type::eos)
- fail (t) << "unexpected " << t;
-
- level5 ([&]{trace (t) << "leaving " << p;});
-
- lexer_ = ol;
- path_ = op;
- }
- catch (const ifstream::failure&)
- {
- fail (l) << "unable to read buildfile " << p;
- }
- }
-
- if (tt == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline instead of " << t;
- }
-
- void parser::
- include (token& t, type& tt)
- {
- tracer trace ("parser::include", &path_);
-
- if (root_->src_path_ == nullptr)
- fail (t) << "inclusion during bootstrap";
-
- // The rest should be a list of buildfiles. Parse them as names
- // to get variable expansion and directory prefixes.
- //
- mode (lexer_mode::value);
- next (t, tt);
- const 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.qualified () || n.empty ())
- fail (l) << "expected buildfile instead of " << n;
-
- // Construct the buildfile path. If it is a directory, then append
- // 'buildfile'.
- //
- path p (move (n.dir));
- if (n.value.empty ())
- p /= path ("buildfile");
- else
- {
- bool d (path::traits::is_separator (n.value.back ())
- || n.type == "dir");
-
- p /= path (move (n.value));
- if (d)
- p /= path ("buildfile");
- }
-
- level6 ([&]{trace (l) << "relative path " << p;});
-
- // Determine new out_base.
- //
- dir_path out_base;
-
- if (p.relative ())
- {
- out_base = scope_->out_path () / p.directory ();
- out_base.normalize ();
- }
- else
- {
- p.normalize ();
-
- // Make sure the path is in this project. Include is only meant
- // to be used for intra-project inclusion (plus amalgamation).
- //
- bool in_out (false);
- if (!p.sub (root_->src_path ()) &&
- !(in_out = p.sub (root_->out_path ())))
- fail (l) << "out of project include " << p;
-
- out_base = in_out
- ? p.directory ()
- : out_src (p.directory (), *root_);
- }
-
- // Switch the scope. Note that we need to do this before figuring
- // out the absolute buildfile path since we may switch the project
- // root and src_root with it (i.e., include into a sub-project).
- //
- scope* ors (root_);
- scope* ocs (scope_);
- switch_scope (out_base);
-
- // Use the new scope's src_base to get absolute buildfile path
- // if it is relative.
- //
- if (p.relative ())
- p = scope_->src_path () / p.leaf ();
-
- level6 ([&]{trace (l) << "absolute path " << p;});
-
- if (!root_->buildfiles.insert (p).second) // Note: may be "new" root.
- {
- level5 ([&]{trace (l) << "skipping already included " << p;});
- scope_ = ocs;
- root_ = ors;
- continue;
- }
-
- try
- {
- ifstream ifs (p.string ());
-
- if (!ifs.is_open ())
- fail (l) << "unable to open " << p;
-
- ifs.exceptions (ifstream::failbit | ifstream::badbit);
-
- level5 ([&]{trace (t) << "entering " << p;});
-
- enter_buildfile (p);
-
- const string* op (path_);
- path_ = &path_pool.find (diag_relative (p)); // Relative to work.
-
- lexer l (ifs, *path_);
- lexer* ol (lexer_);
- lexer_ = &l;
-
- target* odt (default_target_);
- default_target_ = nullptr;
-
- token t (type::eos, false, 0, 0);
- type tt;
- next (t, tt);
- clause (t, tt);
-
- if (tt != type::eos)
- fail (t) << "unexpected " << t;
-
- process_default_target (t);
-
- level5 ([&]{trace (t) << "leaving " << p;});
-
- default_target_ = odt;
- lexer_ = ol;
- path_ = op;
- }
- catch (const ifstream::failure&)
- {
- fail (l) << "unable to read buildfile " << p;
- }
-
- scope_ = ocs;
- 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, type& tt)
- {
- tracer trace ("parser::import", &path_);
-
- if (root_->src_path_ == nullptr)
- fail (t) << "import during bootstrap";
-
- next (t, tt);
-
- // General import format:
- //
- // import [<var>=](<project>|<project>/<target>])+
- //
- value* val (nullptr);
- const build::variable* var (nullptr);
-
- type at; // Assignment type.
- if (tt == type::name)
- {
- at = peek ();
-
- if (at == type::equal ||
- at == type::equal_plus ||
- at == type::plus_equal)
- {
- var = &var_pool.find (t.value);
- val = at == type::equal
- ? &scope_->assign (*var)
- : &scope_->append (*var);
- next (t, tt); // Consume =/=+/+=.
- mode (lexer_mode::value);
- next (t, tt);
- }
- }
-
- // The rest should be a list of projects and/or targets. Parse
- // them as names to get variable expansion and directory prefixes.
- //
- const location l (get_location (t, &path_));
- names_type ns (tt != type::newline && tt != type::eos
- ? names (t, tt)
- : names_type ());
-
- for (name& n: ns)
- {
- // build::import() will check the name, if required.
- //
- names_type r (build::import (*scope_, move (n), l));
-
- if (val != nullptr)
- {
- if (at == type::equal)
- val->assign (move (r), *var);
- else if (at == type::equal_plus)
- val->prepend (move (r), *var);
- else
- val->append (move (r), *var);
- }
- }
-
- if (tt == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline instead of " << t;
- }
-
- void parser::
- export_ (token& t, type& tt)
- {
- tracer trace ("parser::export", &path_);
-
- scope* ps (scope_->parent_scope ());
-
- // This should be temp_scope.
- //
- if (ps == nullptr || ps->out_path () != scope_->out_path ())
- fail (t) << "export outside export stub";
-
- // The rest is a value. Parse it as names to get variable expansion.
- // build::import() will check the names, if required.
- //
- mode (lexer_mode::value);
- next (t, tt);
-
- if (tt != type::newline && tt != type::eos)
- export_value_ = names (t, tt);
-
- if (tt == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline instead of " << t;
- }
-
- void parser::
- using_ (token& t, type& tt)
- {
- tracer trace ("parser::using", &path_);
-
- bool optional (t.value.back () == '?');
-
- if (optional && boot_)
- fail (t) << "optional module in bootstrap";
-
- // The rest should be a list of module names. Parse them as names
- // to get variable expansion, etc.
- //
- mode (lexer_mode::pairs, '@');
- next (t, tt);
- const location l (get_location (t, &path_));
- names_type ns (tt != type::newline && tt != type::eos
- ? names (t, tt)
- : names_type ());
-
- for (auto i (ns.begin ()); i != ns.end (); ++i)
- {
- string n, v;
-
- if (!i->simple ())
- fail (l) << "module name expected instead of " << *i;
-
- n = move (i->value);
-
- if (i->pair)
- {
- ++i;
- if (!i->simple ())
- fail (l) << "module version expected instead of " << *i;
-
- v = move (i->value);
- }
-
- // Handle the special 'build' module.
- //
- if (n == "build")
- {
- if (!v.empty ())
- {
- unsigned int iv;
- try {iv = to_version (v);}
- catch (const invalid_argument& e)
- {
- fail (l) << "invalid version '" << v << "': " << e.what ();
- }
-
- if (iv > BUILD_VERSION)
- fail (l) << "build2 " << v << " required" <<
- info << "running build2 " << BUILD_VERSION_STR;
- }
- }
- else
- {
- assert (v.empty ()); // Module versioning not yet implemented.
-
- if (boot_)
- boot_module (n, *root_, l);
- else
- load_module (optional, n, *root_, *scope_, l);
- }
- }
-
- if (tt == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline instead of " << t;
- }
-
- static target*
- derived_factory (const target_type& t, dir_path d, string n, const string* e)
- {
- // Pass our type to the base factory so that it can detect that it is
- // being called to construct a derived target. This can be used, for
- // example, to decide whether to "link up" to the group.
- //
- target* r (t.base->factory (t, move (d), move (n), e));
- r->derived_type = &t;
- return r;
- }
-
- constexpr const char derived_ext_var[] = "extension";
-
- void parser::
- define (token& t, type& tt)
- {
- // define <derived>: <base>
- //
- // See tests/define.
- //
- if (next (t, tt) != type::name)
- fail (t) << "expected name instead of " << t << " in target type "
- << "definition";
-
- string dn (move (t.value));
- const location dnl (get_location (t, &path_));
-
- if (next (t, tt) != type::colon)
- fail (t) << "expected ':' instead of " << t << " in target type "
- << "definition";
-
- next (t, tt);
-
- if (tt == type::name)
- {
- // Target.
- //
- const string& bn (t.value);
- const target_type* bt (scope_->find_target_type (bn));
-
- if (bt == nullptr)
- fail (t) << "unknown target type " << bn;
-
- unique_ptr<target_type> dt (new target_type (*bt));
- dt->base = bt;
- dt->factory = &derived_factory;
-
- // Override extension derivation function: we most likely don't want
- // to use the same default as our base (think cli: file).
- //
- dt->extension = &target_extension_var<derived_ext_var, nullptr>;
-
- target_type& rdt (*dt); // Save a non-const reference to the object.
-
- auto pr (scope_->target_types.emplace (dn, target_type_ref (move (dt))));
-
- if (!pr.second)
- fail (dnl) << "target type " << dn << " already define in this scope";
-
- // Patch the alias name to use the map's key storage.
- //
- rdt.name = pr.first->first.c_str ();
-
- next (t, tt); // Get newline.
- }
- else
- fail (t) << "expected name instead of " << t << " in target type "
- << "definition";
-
- if (tt == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline instead of " << t;
- }
-
- void parser::
- if_else (token& t, type& tt)
- {
- // Handle the whole if-else chain. See tests/if-else.
- //
- bool taken (false); // One of the branches has been taken.
-
- for (;;)
- {
- string k (move (t.value));
- next (t, tt);
-
- bool take (false); // Take this branch?
-
- if (k != "else")
- {
- // Should we evaluate the expression if one of the branches has
- // already been taken? On the one hand, evaluating it is a waste
- // of time. On the other, it can be invalid and the only way for
- // the user to know their buildfile is valid is to test every
- // branch. There could also be side effects. We also have the same
- // problem with ignored branch blocks except there evaluating it
- // is not an option. So let's skip it.
- //
- if (taken)
- skip_line (t, tt);
- else
- {
- if (tt == type::newline || tt == type::eos)
- fail (t) << "expected " << k << "-expression instead of " << t;
-
- // Parse as names to get variable expansion, evaluation, etc.
- //
- const location nsl (get_location (t, &path_));
- names_type ns (names (t, tt));
-
- // Should evaluate to true or false.
- //
- if (ns.size () != 1 || !value_traits<bool>::assign (ns[0]))
- fail (nsl) << "expected " << k << "-expression to evaluate to "
- << "'true' or 'false' instead of '" << ns << "'";
-
- bool e (ns[0].value == "true");
- take = (k.back () == '!' ? !e : e);
- }
- }
- else
- take = !taken;
-
- if (tt != type::newline)
- fail (t) << "expected newline instead of " << t << " after " << k
- << (k != "else" ? "-expression" : "");
-
- if (next (t, tt) != type::lcbrace)
- fail (t) << "expected { instead of " << t << " at the beginning of "
- << k << "-block";
-
- if (next (t, tt) != type::newline)
- fail (t) << "expected newline after {";
-
- next (t, tt);
-
- if (take)
- {
- clause (t, tt);
- taken = true;
- }
- else
- skip_block (t, tt);
-
- if (tt != type::rcbrace)
- fail (t) << "expected } instead of " << t << " at the end of " << k
- << "-block";
-
- next (t, tt);
-
- if (tt == type::newline)
- next (t, tt);
- else if (tt != type::eos)
- fail (t) << "expected newline after }";
-
- // See if we have another el* keyword.
- //
- if (k != "else" && tt == type::name && keyword (t))
- {
- const string& n (t.value);
-
- if (n == "else" || n == "elif" || n == "elif!")
- continue;
- }
-
- break;
- }
- }
-
- void parser::
- print (token& t, type& tt)
- {
- // Parse the rest as names to get variable expansion, etc. Switch
- // to the variable value lexing mode so that we don't treat special
- // characters (e.g., ':') as the end of the names.
- //
- mode (lexer_mode::value);
- next (t, tt);
- names_type ns (tt != type::newline && tt != type::eos
- ? names (t, tt)
- : names_type ());
-
- cout << ns << endl;
-
- if (tt != type::eos)
- next (t, tt); // Swallow newline.
- }
-
- string parser::
- variable_name (names_type&& ns, const location& l)
- {
- // The list should contain a single, simple name.
- //
- if (ns.size () != 1 || !ns[0].simple () || ns[0].empty ())
- fail (l) << "variable name expected instead of " << ns;
-
- string& n (ns[0].value);
-
- if (n.front () == '.') // Fully qualified name.
- return string (n, 1, string::npos);
- else
- //@@ TODO: append namespace if any.
- return move (n);
- }
-
- void parser::
- variable (token& t, type& tt, string name, type kind)
- {
- const auto& var (var_pool.find (move (name)));
- names_type vns (variable_value (t, tt, var));
-
- if (kind == type::equal)
- {
- value& v (target_ != nullptr
- ? target_->assign (var)
- : scope_->assign (var));
- v.assign (move (vns), var);
- }
- else
- {
- value& v (target_ != nullptr
- ? target_->append (var)
- : scope_->append (var));
-
- if (kind == type::equal_plus)
- v.prepend (move (vns), var);
- else
- v.append (move (vns), var);
- }
- }
-
- names parser::
- variable_value (token& t, type& tt, const variable_type& var)
- {
- if (var.pairs != '\0')
- mode (lexer_mode::pairs, var.pairs);
- else
- mode (lexer_mode::value);
-
- next (t, tt);
- return (tt != type::newline && tt != type::eos
- ? names (t, tt)
- : names_type ());
- }
-
- parser::names_type parser::
- eval (token& t, type& tt)
- {
- mode (lexer_mode::eval);
- next (t, tt);
-
- names_type ns (tt != type::rparen ? names (t, tt) : names_type ());
-
- if (tt != type::rparen)
- fail (t) << "expected ')' instead of " << t;
-
- return ns;
- }
-
- // Parse names inside {} and handle the following "crosses" (i.e.,
- // {a b}{x y}) if any. Return the number of names added to the list.
- //
- size_t parser::
- names_trailer (token& t, type& tt,
- names_type& ns,
- size_t pair,
- const string* pp,
- const dir_path* dp,
- const string* tp)
- {
- next (t, tt); // Get what's after '{'.
-
- size_t count (ns.size ());
- names (t, tt,
- ns,
- false,
- (pair != 0
- ? pair
- : (ns.empty () || ns.back ().pair == '\0' ? 0 : ns.size ())),
- pp, dp, tp);
- count = ns.size () - count;
-
- if (tt != type::rcbrace)
- fail (t) << "expected } instead of " << t;
-
- // See if we have a cross. See tests/names.
- //
- if (peek () == type::lcbrace && !peeked ().separated)
- {
- next (t, tt); // Get '{'.
- const location loc (get_location (t, &path_));
-
- names_type x; // Parse into a separate list of names.
- names_trailer (t, tt, x, 0, nullptr, nullptr, nullptr);
-
- if (size_t n = x.size ())
- {
- // Now cross the last 'count' names in 'ns' with 'x'. First we will
- // allocate n - 1 additional sets of last 'count' names in 'ns'.
- //
- size_t b (ns.size () - count); // Start of 'count' names.
- ns.reserve (ns.size () + count * (n - 1));
- for (size_t i (0); i != n - 1; ++i)
- for (size_t j (0); j != count; ++j)
- ns.push_back (ns[b + j]);
-
- // Now cross each name, this time including the first set.
- //
- for (size_t i (0); i != n; ++i)
- {
- for (size_t j (0); j != count; ++j)
- {
- name& l (ns[b + i * count + j]);
- const name& r (x[i]);
-
- // Move the project names.
- //
- if (r.proj != nullptr)
- {
- if (l.proj != nullptr)
- fail (loc) << "nested project name " << *r.proj;
-
- l.proj = r.proj;
- }
-
- // Merge directories.
- //
- if (!r.dir.empty ())
- {
- if (l.dir.empty ())
- l.dir = move (r.dir);
- else
- l.dir /= r.dir;
- }
-
- // Figure out the type. As a first step, "promote" the lhs value
- // to type.
- //
- if (!l.value.empty ())
- {
- if (!l.type.empty ())
- fail (loc) << "nested type name " << l.value;
-
- l.type.swap (l.value);
- }
-
- if (!r.type.empty ())
- {
- if (!l.type.empty ())
- fail (loc) << "nested type name " << r.type;
-
- l.type = move (r.type);
- }
-
- l.value = move (r.value);
-
- // @@ TODO: need to handle pairs on lhs. I think all that needs
- // to be done is skip pair's first elements. Maybe also check
- // that there are no pairs on the rhs. There is just no easy
- // way to enable the pairs mode to test it, yet.
- }
- }
-
- count *= n;
- }
- }
-
- return count;
- }
-
- void parser::
- names (token& t, type& tt,
- names_type& ns,
- bool chunk,
- size_t pair,
- const string* pp,
- const dir_path* dp,
- const string* tp)
- {
- // If pair is not 0, then it is an index + 1 of the first half of
- // the pair for which we are parsing the second halves, e.g.,
- // a={b c d{e f} {}}.
- //
-
- // Buffer that is used to collect the complete name in case of
- // an unseparated variable expansion or eval context, e.g.,
- // 'foo$bar($baz)fox'. The idea is to concatenate all the
- // individual parts in this buffer and then re-inject it into
- // the loop as a single token.
- //
- string concat;
-
- // Number of names in the last group. This is used to detect when
- // we need to add an empty first pair element (e.g., {=y}) or when
- // we have a for now unsupported multi-name LHS (e.g., {x y}=z).
- //
- size_t count (0);
-
- for (bool first (true);; first = false)
- {
- // If the accumulating buffer is not empty, then we have two options:
- // continue accumulating or inject. We inject if the next token is
- // not a name, var expansion, or eval context or if it is separated.
- //
- if (!concat.empty () &&
- ((tt != type::name &&
- tt != type::dollar &&
- tt != type::lparen) || peeked ().separated))
- {
- tt = type::name;
- t = token (move (concat), true, false, t.line, t.column);
- concat.clear ();
- }
- else if (!first)
- {
- // If we are chunking, stop at the next separated token. Unless
- // current or next token is a pair separator, since we want the
- // "x = y" pair to be parsed as a single chunk.
- //
- bool p (t.type == type::pair_separator); // Current token.
-
- next (t, tt);
-
- if (chunk && t.separated && (tt != type::pair_separator && !p))
- break;
- }
-
- // Name.
- //
- if (tt == type::name)
- {
- string name (t.value); //@@ move?
- tt = peek ();
-
- // Should we accumulate? If the buffer is not empty, then
- // we continue accumulating (the case where we are separated
- // should have been handled by the injection code above). If
- // the next token is a var expansion or eval context and it
- // is not separated, then we need to start accumulating.
- //
- if (!concat.empty () || // Continue.
- ((tt == type::dollar ||
- tt == type::lparen) && !peeked ().separated)) // Start.
- {
- concat += name;
- continue;
- }
-
- string::size_type p (name.find_last_of ("/%"));
-
- // First take care of project. A project-qualified name is
- // not very common, so we can afford some copying for the
- // sake of simplicity.
- //
- const string* pp1 (pp);
-
- if (p != string::npos)
- {
- bool last (name[p] == '%');
- string::size_type p1 (last ? p : name.rfind ('%', p - 1));
-
- if (p1 != string::npos)
- {
- string proj;
- proj.swap (name);
-
- // First fix the rest of the name.
- //
- name.assign (proj, p1 + 1, string::npos);
- p = last ? string::npos : p - (p1 + 1);
-
- // Now process the project name.
- // @@ Validate it.
- //
- proj.resize (p1);
-
- if (pp != nullptr)
- fail (t) << "nested project name " << proj;
-
- pp1 = &project_name_pool.find (proj);
- }
- }
-
- string::size_type n (p != string::npos ? name.size () - 1 : 0);
-
- // See if this is a type name, directory prefix, or both. That
- // is, it is followed by an un-separated '{'.
- //
- if (tt == type::lcbrace && !peeked ().separated)
- {
- next (t, tt);
-
- if (p != n && tp != nullptr)
- fail (t) << "nested type name " << name;
-
- dir_path d1;
- const dir_path* dp1 (dp);
-
- string t1;
- const string* tp1 (tp);
-
- if (p == string::npos) // type
- tp1 = &name;
- else if (p == n) // directory
- {
- if (dp == nullptr)
- d1 = dir_path (name);
- else
- d1 = *dp / dir_path (name);
-
- dp1 = &d1;
- }
- else // both
- {
- t1.assign (name, p + 1, n - p);
-
- if (dp == nullptr)
- d1 = dir_path (name, 0, p + 1);
- else
- d1 = *dp / dir_path (name, 0, p + 1);
-
- dp1 = &d1;
- tp1 = &t1;
- }
-
- count = names_trailer (t, tt, ns, pair, pp1, dp1, tp1);
- tt = peek ();
- continue;
- }
-
- // If we are a second half of a pair, add another first half
- // unless this is the first instance.
- //
- if (pair != 0 && pair != ns.size ())
- ns.push_back (ns[pair - 1]);
-
- count = 1;
-
- // If it ends with a directory separator, then it is a directory.
- // Note that at this stage we don't treat '.' and '..' as special
- // (unless they are specified with a directory separator) because
- // then we would have ended up treating '.: ...' as a directory
- // scope. Instead, this is handled higher up the processing chain,
- // in target_types::find(). This would also mess up reversibility
- // to simple name.
- //
- // @@ TODO: and not quoted
- //
- if (p == n)
- {
- // For reversibility to simple name, only treat it as a directory
- // if the string is an exact representation.
- //
- if (p != 0 && name[p - 1] != '/') // Take care of the "//" case.
- name.resize (p); // Strip trailing '/'.
-
- dir_path dir (move (name), dir_path::exact);
-
- if (!dir.empty ())
- {
- if (dp != nullptr)
- dir = *dp / dir;
-
- ns.emplace_back (pp1,
- move (dir),
- (tp != nullptr ? *tp : string ()),
- string ());
- continue;
- }
-
- // Add the trailing slash back and treat it as a simple name.
- //
- if (p != 0 && name[p - 1] != '/')
- name.push_back ('/');
- }
-
- ns.emplace_back (pp1,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- move (name));
- continue;
- }
-
- // Variable expansion/function call or eval context.
- //
- if (tt == type::dollar || tt == type::lparen)
- {
- // These two cases are pretty similar in that in both we
- // pretty quickly end up with a list of names that we need
- // to splice into the result.
- //
- names_type lv_data;
- const names_type* plv;
-
- location loc;
- const char* what; // Variable or evaluation context.
-
- if (tt == type::dollar)
- {
- // Switch to the variable name mode. We want to use this
- // mode for $foo but not for $(foo). Since we don't know
- // whether the next token is a paren or a name, we turn
- // it on and switch to the eval mode if what we get next
- // is a paren.
- //
- mode (lexer_mode::variable);
- next (t, tt);
- loc = get_location (t, &path_);
-
- string n;
- if (tt == type::name)
- n = t.value;
- else if (tt == type::lparen)
- {
- expire_mode ();
- names_type ns (eval (t, tt));
-
- // Make sure the result of evaluation is a single, simple name.
- //
- if (ns.size () != 1 || !ns.front ().simple ())
- fail (loc) << "variable/function name expected instead of '"
- << ns << "'";
-
- n = move (ns.front ().value);
- }
- else
- fail (t) << "variable/function name expected instead of " << t;
-
- if (n.empty ())
- fail (loc) << "empty variable/function name";
-
- // Figure out whether this is a variable expansion or a function
- // call.
- //
- tt = peek ();
-
- if (tt == type::lparen)
- {
- next (t, tt); // Get '('.
- names_type ns (eval (t, tt));
-
- // Just a stub for now.
- //
- cout << n << "(" << ns << ")" << endl;
-
- tt = peek ();
-
- if (lv_data.empty ())
- continue;
-
- plv = &lv_data;
- what = "function call";
- }
- else
- {
- // Process variable name.
- //
- if (n.front () == '.') // Fully qualified name.
- n.erase (0, 1);
- else
- {
- //@@ TODO: append namespace if any.
- }
-
- // Lookup.
- //
- const auto& var (var_pool.find (move (n)));
- auto l (target_ != nullptr ? (*target_)[var] : (*scope_)[var]);
-
- // Undefined/NULL namespace variables are not allowed.
- //
- if (!l && var.name.find ('.') != string::npos)
- fail (loc) << "undefined/null namespace variable " << var.name;
-
- if (!l || l->empty ())
- continue;
-
- plv = &l->data_;
- what = "variable expansion";
- }
- }
- else
- {
- loc = get_location (t, &path_);
- lv_data = eval (t, tt);
-
- tt = peek ();
-
- if (lv_data.empty ())
- continue;
-
- plv = &lv_data;
- what = "context evaluation";
- }
-
- // @@ Could move if (lv == &lv_data).
- //
- const names_type& lv (*plv);
-
- // Should we accumulate? If the buffer is not empty, then
- // we continue accumulating (the case where we are separated
- // should have been handled by the injection code above). If
- // the next token is a name or var expansion and it is not
- // separated, then we need to start accumulating.
- //
- if (!concat.empty () || // Continue.
- ((tt == type::name || // Start.
- tt == type::dollar ||
- tt == type::lparen) && !peeked ().separated))
- {
- // This should be a simple value or a simple directory. The
- // token still points to the name (or closing paren).
- //
- if (lv.size () > 1)
- fail (loc) << "concatenating " << what << " contains multiple "
- << "values";
-
- const name& n (lv[0]);
-
- if (n.qualified ())
- fail (loc) << "concatenating " << what << " contains project name";
-
- if (n.typed ())
- fail (loc) << "concatenating " << what << " contains type";
-
- if (!n.dir.empty ())
- {
- if (!n.value.empty ())
- fail (loc) << "concatenating " << what << " contains directory";
-
- concat += n.dir.string ();
- }
- else
- concat += n.value;
- }
- else
- {
- // Copy the names from the variable into the resulting name list
- // while doing sensible things with the types and directories.
- //
- for (const name& n: lv)
- {
- const string* pp1 (pp);
- const dir_path* dp1 (dp);
- const string* tp1 (tp);
-
- if (n.proj != 0)
- {
- if (pp == nullptr)
- pp1 = n.proj;
- else
- fail (loc) << "nested project name " << *n.proj << " in "
- << what;
- }
-
- dir_path d1;
- if (!n.dir.empty ())
- {
- if (dp != nullptr)
- {
- if (n.dir.absolute ())
- fail (loc) << "nested absolute directory " << n.dir
- << " in " << what;
-
- d1 = *dp / n.dir;
- dp1 = &d1;
- }
- else
- dp1 = &n.dir;
- }
-
- if (!n.type.empty ())
- {
- if (tp == nullptr)
- tp1 = &n.type;
- else
- fail (loc) << "nested type name " << n.type << " in " << what;
- }
-
- // If we are a second half of a pair.
- //
- if (pair != 0)
- {
- // Check that there are no nested pairs.
- //
- if (n.pair != '\0')
- fail (loc) << "nested pair in " << what;
-
- // And add another first half unless this is the first instance.
- //
- if (pair != ns.size ())
- ns.push_back (ns[pair - 1]);
- }
-
- ns.emplace_back (pp1,
- (dp1 != nullptr ? *dp1 : dir_path ()),
- (tp1 != nullptr ? *tp1 : string ()),
- n.value);
-
- ns.back ().pair = n.pair;
- }
-
- count = lv.size ();
- }
-
- continue;
- }
-
- // Untyped name group without a directory prefix, e.g., '{foo bar}'.
- //
- if (tt == type::lcbrace)
- {
- count = names_trailer (t, tt, ns, pair, pp, dp, tp);
- tt = peek ();
- continue;
- }
-
- // A pair separator (only in the pairs mode).
- //
- if (tt == type::pair_separator)
- {
- if (pair != 0)
- fail (t) << "nested pair on the right hand side of a pair";
-
- if (count > 1)
- fail (t) << "multiple names on the left hand side of a pair";
-
- if (count == 0)
- {
- // Empty LHS, (e.g., {=y}), create an empty name.
- //
- ns.emplace_back (pp,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- string ());
- count = 1;
- }
-
- ns.back ().pair = t.pair;
- tt = peek ();
- continue;
- }
-
- if (!first)
- break;
-
- if (tt == type::rcbrace) // Empty name, e.g., dir{}.
- {
- // If we are a second half of a pair, add another first half
- // unless this is the first instance.
- //
- if (pair != 0 && pair != ns.size ())
- ns.push_back (ns[pair - 1]);
-
- ns.emplace_back (pp,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- string ());
- break;
- }
- else
- // Our caller expected this to be a name.
- //
- fail (t) << "expected name instead of " << t;
- }
-
- // Handle the empty RHS in a pair, (e.g., {y=}).
- //
- if (!ns.empty () && ns.back ().pair != '\0')
- {
- ns.emplace_back (pp,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- string ());
- }
- }
-
- void parser::
- skip_line (token& t, type& tt)
- {
- for (; tt != type::newline && tt != type::eos; next (t, tt)) ;
- }
-
- void parser::
- skip_block (token& t, type& tt)
- {
- // Skip until } or eos, keeping track of the {}-balance.
- //
- for (size_t b (0); tt != type::eos; )
- {
- if (tt == type::lcbrace || tt == type::rcbrace)
- {
- type ptt (peek ());
- if (ptt == type::newline || ptt == type::eos) // Block { or }.
- {
- if (tt == type::lcbrace)
- ++b;
- else
- {
- if (b == 0)
- break;
-
- --b;
- }
- }
- }
-
- skip_line (t, tt);
-
- if (tt != type::eos)
- next (t, tt);
- }
- }
-
- bool parser::
- keyword (token& t)
- {
- assert (replay_ == replay::stop); // Can't be used in a replay.
- assert (t.type == type::name);
-
- // The goal here is to allow using keywords as variable names and
- // target types without imposing ugly restrictions/decorators on
- // keywords (e.g., '.using' or 'USING'). A name is considered a
- // potential keyword if:
- //
- // - it is not quoted [so a keyword can always be escaped] and
- // - next token is '\n' (or eos) or '(' [so if(...) will work] or
- // - next token is separated and is not '=', '=+', or '+=' [which
- // means a "directive trailer" can never start with one of them].
- //
- // See tests/keyword.
- //
- if (!t.quoted)
- {
- // We cannot peek at the whole token here since it might have to be
- // lexed in a different mode. So peek at its first character.
- //
- pair<char, bool> p (lexer_->peek_char ());
- char c (p.first);
-
- return c == '\n' || c == '\0' || c == '(' ||
- (p.second && c != '=' && c != '+');
- }
-
- return false;
- }
-
- // Buildspec parsing.
- //
-
- // Here is the problem: we "overload" '(' and ')' to mean operation
- // application rather than the eval context. At the same time we want
- // to use names() to parse names, get variable expansion/function calls,
- // quoting, etc. We just need to disable the eval context. The way this
- // is done has two parts: Firstly, we parse names in chunks and detect
- // and handle the opening paren. In other words, a buildspec like
- // 'clean (./)' is "chunked" as 'clean', '(', etc. While this is fairly
- // straightforward, there is one snag: concatenating eval contexts, as
- // in 'clean(./)'. Normally, this will be treated as a single chunk and
- // we don't want that. So here comes the trick (or hack, if you like):
- // we will make every opening paren token "separated" (i.e., as if it
- // was proceeded by a space). This will disable concatenating eval. In
- // fact, we will even go a step further and only do this if we are in
- // the original pairs mode. This will allow us to still use eval
- // contexts in buildspec, provided that we quote it: '"cle(an)"'. Note
- // also that function calls still work as usual: '$filter (clean test)'.
- // To disable a function call and make it instead a var that is expanded
- // into operation name(s), we can use quoting: '"$ops"(./)'.
- //
- static void
- paren_processor (token& t, const lexer& l)
- {
- if (t.type == type::lparen && l.mode () == lexer_mode::pairs)
- t.separated = true;
- }
-
- buildspec parser::
- parse_buildspec (istream& is, const std::string& name)
- {
- path_ = &name; // Note: caller pools.
-
- lexer l (is, name, &paren_processor);
- lexer_ = &l;
- target_ = nullptr;
- scope_ = root_ = global_scope;
-
- // Turn on pairs recognition with '@' as the pair separator (e.g.,
- // src_root/@out_root/exe{foo bar}).
- //
- mode (lexer_mode::pairs, '@');
-
- token t (type::eos, false, 0, 0);
- type tt;
- next (t, tt);
-
- return buildspec_clause (t, tt, type::eos);
- }
-
- static bool
- opname (const name& n)
- {
- // First it has to be a non-empty simple name.
- //
- if (n.pair != '\0' || !n.simple () || n.empty ())
- return false;
-
- // C identifier.
- //
- for (size_t i (0); i != n.value.size (); ++i)
- {
- char c (n.value[i]);
- if (c != '_' && !(i != 0 ? isalnum (c) : isalpha (c)))
- return false;
- }
-
- return true;
- }
-
- buildspec parser::
- buildspec_clause (token& t, type& tt, type tt_end)
- {
- buildspec bs;
-
- while (tt != tt_end)
- {
- // We always start with one or more names. Eval context
- // (lparen) only allowed if quoted.
- //
- if (tt != type::name &&
- tt != type::lcbrace && // Untyped name group: '{foo ...'
- tt != type::dollar && // Variable expansion: '$foo ...'
- !(tt == type::lparen && mode () == lexer_mode::quoted) &&
- tt != type::pair_separator) // Empty pair LHS: '@foo ...'
- fail (t) << "operation or target expected instead of " << t;
-
- const location l (get_location (t, &path_)); // Start of names.
-
- // This call will parse the next chunk of output and produce
- // zero or more names.
- //
- names_type ns (names (t, tt, true));
-
- // What these names mean depends on what's next. If it is an
- // opening paren, then they are operation/meta-operation names.
- // Otherwise they are targets.
- //
- if (tt == type::lparen) // Peeked into by names().
- {
- if (ns.empty ())
- fail (t) << "operation name expected before '('";
-
- for (const name& n: ns)
- if (!opname (n))
- fail (l) << "operation name expected instead of '" << n << "'";
-
- // Inside '(' and ')' we have another, nested, buildspec.
- //
- next (t, tt);
- const location l (get_location (t, &path_)); // Start of nested names.
- buildspec nbs (buildspec_clause (t, tt, type::rparen));
-
- // Merge the nested buildspec into ours. But first determine
- // if we are an operation or meta-operation and do some sanity
- // checks.
- //
- bool meta (false);
- for (const metaopspec& nms: nbs)
- {
- // We definitely shouldn't have any meta-operations.
- //
- if (!nms.name.empty ())
- fail (l) << "nested meta-operation " << nms.name;
-
- if (!meta)
- {
- // If we have any operations in the nested spec, then this
- // mean that our names are meta-operation names.
- //
- for (const opspec& nos: nms)
- {
- if (!nos.name.empty ())
- {
- meta = true;
- break;
- }
- }
- }
- }
-
- // No nested meta-operations means we should have a single
- // metaopspec object with empty meta-operation name.
- //
- assert (nbs.size () == 1);
- const metaopspec& nmo (nbs.back ());
-
- if (meta)
- {
- for (name& n: ns)
- {
- bs.push_back (nmo);
- bs.back ().name = move (n.value);
- }
- }
- else
- {
- // Since we are not a meta-operation, the nested buildspec
- // should be just a bunch of targets.
- //
- assert (nmo.size () == 1);
- const opspec& nos (nmo.back ());
-
- if (bs.empty () || !bs.back ().name.empty ())
- bs.push_back (metaopspec ()); // Empty (default) meta operation.
-
- for (name& n: ns)
- {
- bs.back ().push_back (nos);
- bs.back ().back ().name = move (n.value);
- }
- }
-
- next (t, tt); // Done with '('.
- }
- else if (!ns.empty ())
- {
- // Group all the targets into a single operation. In other
- // words, 'foo bar' is equivalent to 'update(foo bar)'.
- //
- if (bs.empty () || !bs.back ().name.empty ())
- bs.push_back (metaopspec ()); // Empty (default) meta operation.
-
- metaopspec& ms (bs.back ());
-
- for (auto i (ns.begin ()), e (ns.end ()); i != e; ++i)
- {
- // @@ We may actually want to support this at some point.
- //
- if (i->qualified ())
- fail (l) << "target name expected instead of " << *i;
-
- if (opname (*i))
- ms.push_back (opspec (move (i->value)));
- else
- {
- // Do we have the src_base?
- //
- dir_path src_base;
- if (i->pair != '\0')
- {
- if (i->typed ())
- fail (l) << "expected target src_base instead of " << *i;
-
- src_base = move (i->dir);
-
- if (!i->value.empty ())
- src_base /= dir_path (move (i->value));
-
- ++i;
- assert (i != e); // Got to have the second half of the pair.
- }
-
- if (ms.empty () || !ms.back ().name.empty ())
- ms.push_back (opspec ()); // Empty (default) operation.
-
- opspec& os (ms.back ());
- os.emplace_back (move (src_base), move (*i));
- }
- }
- }
- }
-
- return bs;
- }
-
- void parser::
- switch_scope (const dir_path& p)
- {
- tracer trace ("parser::switch_scope", &path_);
-
- // First, enter the scope into the map and see if it is in any
- // project. If it is not, then there is nothing else to do.
- //
- auto i (scopes.insert (p, nullptr, true, false));
- scope_ = i->second;
- scope* rs (scope_->root_scope ());
-
- if (rs == nullptr)
- return;
-
- // Path p can be src_base or out_base. Figure out which one it is.
- //
- dir_path out_base (p.sub (rs->out_path ()) ? p : src_out (p, *rs));
-
- // Create and bootstrap root scope(s) of subproject(s) that this
- // scope may belong to. If any were created, load them. Note that
- // we need to do this before figuring out src_base since we may
- // switch the root project (and src_root with it).
- //
- {
- scope* nrs (&create_bootstrap_inner (*rs, out_base));
-
- if (rs != nrs)
- {
- load_root_pre (*nrs); // Load outer roots recursively.
- rs = nrs;
- }
- }
-
- // Switch to the new root scope.
- //
- if (rs != root_)
- {
- level5 ([&]{trace << "switching to root scope " << rs->out_path ();});
- root_ = rs;
- }
-
- // Now we can figure out src_base and finish setting the scope.
- //
- dir_path src_base (src_out (out_base, *rs));
- setup_base (i, move (out_base), move (src_base));
- }
-
- void parser::
- process_default_target (token& t)
- {
- tracer trace ("parser::process_default_target", &path_);
-
- // The logic is as follows: if we have an explicit current directory
- // target, then that's the default target. Otherwise, we take the
- // first target and use it as a prerequisite to create an implicit
- // current directory target, effectively making it the default
- // target via an alias. If there are no targets in this buildfile,
- // then we don't do anything.
- //
- if (default_target_ == nullptr || // No targets in this buildfile.
- targets.find (dir::static_type, // Explicit current dir target.
- scope_->out_path (),
- "",
- nullptr,
- trace) != targets.end ())
- return;
-
- target& dt (*default_target_);
-
- level5 ([&]{trace (t) << "creating current directory alias for " << dt;});
-
- target& ct (
- targets.insert (
- dir::static_type, scope_->out_path (), "", nullptr, trace).first);
-
- prerequisite& p (
- scope_->prerequisites.insert (
- nullptr,
- dt.type (),
- dt.dir,
- dt.name,
- dt.ext,
- *scope_, // Doesn't matter which scope since dir is absolute.
- trace).first);
-
- p.target = &dt;
- ct.prerequisites.emplace_back (p);
- }
-
- void parser::
- enter_buildfile (const path& p)
- {
- tracer trace ("parser::enter_buildfile", &path_);
-
- const char* e (p.extension ());
- targets.insert<buildfile> (
- p.directory (),
- p.leaf ().base ().string (),
- &extension_pool.find (e == nullptr ? "" : e), // Always specified.
- trace);
- }
-
- type parser::
- next (token& t, type& tt)
- {
- if (peeked_)
- {
- t = move (peek_);
- peeked_ = false;
- }
- else
- t = (replay_ == replay::play ? replay_next () : lexer_->next ());
-
- if (replay_ == replay::save)
- replay_data_.push_back (t);
-
- tt = t.type;
- return tt;
- }
-
- type parser::
- peek ()
- {
- if (!peeked_)
- {
- peek_ = (replay_ == replay::play ? replay_next () : lexer_->next ());
- peeked_ = true;
- }
-
- return peek_.type;
- }
-
- static location
- get_location (const token& t, const void* data)
- {
- assert (data != nullptr);
- const string& p (**static_cast<const string* const*> (data));
- return location (p.c_str (), t.line, t.column);
- }
-}
diff --git a/build/path-io b/build/path-io
deleted file mode 100644
index 888a8b4..0000000
--- a/build/path-io
+++ /dev/null
@@ -1,26 +0,0 @@
-// file : build/path-io -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_PATH_IO
-#define BUILD_PATH_IO
-
-#include <iosfwd>
-
-#include <butl/path>
-
-// Custom path IO.
-//
-namespace build
-{
- using butl::path;
- using butl::dir_path;
-
- std::ostream&
- operator<< (std::ostream&, const path&);
-
- std::ostream&
- operator<< (std::ostream&, const dir_path&);
-}
-
-#endif // BUILD_PATH_IO
diff --git a/build/path-io.cxx b/build/path-io.cxx
deleted file mode 100644
index b6fd9e7..0000000
--- a/build/path-io.cxx
+++ /dev/null
@@ -1,39 +0,0 @@
-// file : build/path-io.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/path-io>
-
-#include <ostream>
-
-#include <build/context>
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- ostream&
- operator<< (ostream& os, const path& p)
- {
- return os << (relative (os) ? diag_relative (p) : p.string ());
- }
-
- ostream&
- operator<< (ostream& os, const dir_path& d)
- {
- if (relative (os))
- os << diag_relative (d);
- else
- {
- const string& s (d.string ());
-
- // Print the directory with trailing '/'.
- //
- if (!s.empty ())
- os << s << (dir_path::traits::is_separator (s.back ()) ? "" : "/");
- }
-
- return os;
- }
-}
diff --git a/build/prerequisite b/build/prerequisite
deleted file mode 100644
index c435fd3..0000000
--- a/build/prerequisite
+++ /dev/null
@@ -1,129 +0,0 @@
-// file : build/prerequisite -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_PREREQUISITE
-#define BUILD_PREREQUISITE
-
-#include <set>
-#include <string>
-#include <iosfwd>
-#include <utility> // move
-#include <cassert>
-#include <functional> // reference_wrapper
-
-#include <build/types>
-#include <build/target-key>
-#include <build/utility> // extension_pool
-#include <build/diagnostics>
-
-namespace build
-{
- class scope;
- class target;
-
- // Light-weight (by being shallow-pointing) prerequisite key, similar
- // to (and based on) target key.
- //
- class prerequisite_key
- {
- public:
- typedef build::scope scope_type;
-
- mutable const std::string* proj; // Can be NULL, from project_name_pool.
- target_key tk;
- mutable scope_type* scope; // Can be NULL if tk.dir is absolute.
- };
-
- inline bool
- operator< (const prerequisite_key& x, const prerequisite_key& y)
- {
- assert (x.scope == y.scope);
-
- // Can compare project name pointers since they are from project_name_pool.
- //
- return x.proj < y.proj || (x.proj == y.proj && x.tk < y.tk);
- }
-
- std::ostream&
- operator<< (std::ostream&, const prerequisite_key&);
-
- class prerequisite
- {
- public:
- typedef build::target target_type;
- typedef build::target_type target_type_type;
- typedef build::scope scope_type;
-
- prerequisite (const std::string* p,
- const target_type_type& t,
- dir_path d,
- std::string n,
- const std::string* e,
- scope_type& s)
- : proj (p),
- type (t),
- dir (std::move (d)),
- name (std::move (n)),
- ext (e),
- scope (s),
- target (nullptr) {}
-
- public:
- const std::string* proj; // NULL if not project-qualified.
- const target_type_type& type;
- const dir_path dir; // Normalized absolute or relative (to scope).
- const std::string name;
- const std::string* ext; // NULL if unspecified.
- scope_type& scope;
- target_type* target; // NULL if not yet resolved. Note that this should
- // always be the "primary target", not a member of
- // a target group.
- prerequisite_key
- key () const
- {
- return prerequisite_key {proj, {&type, &dir, &name, &ext}, &scope};
- }
-
- public:
- // Prerequisite (target) type.
- //
- template <typename T>
- bool
- is_a () const {return type.is_a<T> ();}
- };
-
- inline bool
- operator< (const prerequisite& x, const prerequisite& y)
- {
- return x.key () < y.key ();
- }
-
- inline std::ostream&
- operator<< (std::ostream& os, const prerequisite& p)
- {
- return os << p.key ();
- }
-
- // Set of prerequisites in a scope.
- //
- struct prerequisite_set: std::set<prerequisite>
- {
- std::pair<prerequisite&, bool>
- insert (const std::string* proj,
- const target_type&,
- dir_path dir,
- std::string name,
- const std::string* ext,
- scope&,
- tracer&);
-
- std::pair<prerequisite&, bool>
- insert (const std::string* proj, const target_key& tk, scope& s, tracer& t)
- {
- return insert (proj, *tk.type, *tk.dir, *tk.name, *tk.ext, s, t);
- }
- };
-}
-
-#endif // BUILD_PREREQUISITE
diff --git a/build/prerequisite.cxx b/build/prerequisite.cxx
deleted file mode 100644
index 49cab89..0000000
--- a/build/prerequisite.cxx
+++ /dev/null
@@ -1,82 +0,0 @@
-// file : build/prerequisite.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/prerequisite>
-
-#include <ostream>
-
-#include <build/scope>
-#include <build/target> // target_type
-#include <build/context>
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- // prerequisite_key
- //
- ostream&
- operator<< (ostream& os, const prerequisite_key& pk)
- {
- if (pk.proj != nullptr)
- os << *pk.proj << '%';
-
- // Don't print scope if we are project-qualified or the
- // prerequisite's directory is absolute. In both these
- // cases the scope is not used to resolve it to target.
- //
- else if (!pk.tk.dir->absolute ())
- {
- string s (diag_relative (pk.scope->out_path (), false));
-
- if (!s.empty ())
- os << s << ':';
- }
-
- return os << pk.tk;
- }
-
- // prerequisite_set
- //
- auto prerequisite_set::
- insert (const std::string* proj,
- const target_type& tt,
- dir_path dir,
- std::string name,
- const std::string* ext,
- scope& s,
- tracer& trace) -> pair<prerequisite&, bool>
- {
- //@@ OPT: would be nice to somehow first check if this prerequisite is
- // already in the set before allocating a new instance.
-
- // Find or insert.
- //
- auto r (emplace (proj, tt, move (dir), move (name), ext, s));
- prerequisite& p (const_cast<prerequisite&> (*r.first));
-
- // Update extension if the existing prerequisite has it unspecified.
- //
- if (p.ext != ext)
- {
- level5 ([&]{
- diag_record r (trace);
- r << "assuming prerequisite " << p << " is the same as the "
- << "one with ";
- if (ext == nullptr)
- r << "unspecified extension";
- else if (ext->empty ())
- r << "no extension";
- else
- r << "extension " << *ext;
- });
-
- if (ext != nullptr)
- p.ext = ext;
- }
-
- return pair<prerequisite&, bool> (p, r.second);
- }
-}
diff --git a/build/rule b/build/rule
deleted file mode 100644
index af1bcc8..0000000
--- a/build/rule
+++ /dev/null
@@ -1,135 +0,0 @@
-// file : build/rule -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_RULE
-#define BUILD_RULE
-
-#include <string>
-#include <cstddef> // nullptr_t
-
-#include <build/types>
-#include <build/target>
-#include <build/operation>
-
-namespace build
-{
- class match_result
- {
- public:
- typedef build::target target_type;
- typedef build::prerequisite prerequisite_type;
-
- // Can contain neither (both are NULL), one of, or both. If both
- // are NULL, then it is a "no match" indicator.
- //
- // Note that if the "payload" is stored in *value instead of
- // prerequisite, then target must not be NULL.
- //
- union
- {
- prerequisite_type* prerequisite;
-
- bool bvalue;
- void* pvalue;
- const void* cpvalue;
- };
-
- target_type* target;
-
- action recipe_action = action (); // Used as recipe's action if set.
-
- match_result (std::nullptr_t v = nullptr): prerequisite (v), target (v) {}
- match_result (prerequisite_type& p): prerequisite (&p), target (nullptr) {}
- match_result (prerequisite_type* p): prerequisite (p), target (nullptr) {}
- match_result (target_type& t): prerequisite (nullptr), target (&t) {}
- match_result (target_type* t): prerequisite (nullptr), target (t) {}
- match_result (const prerequisite_member& pm)
- : prerequisite (&pm.prerequisite.get ()), target (pm.target) {}
-
- match_result (target_type& t, bool v): bvalue (v), target (&t) {}
- match_result (target_type& t, void* v): pvalue (v), target (&t) {}
- match_result (target_type& t, const void* v): cpvalue (v), target (&t) {}
- match_result (target_type& t, std::nullptr_t v): pvalue (v), target (&t) {}
-
- explicit
- operator bool () const
- {
- return target != nullptr || prerequisite != nullptr;
- }
- };
-
- class rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const = 0;
-
- virtual recipe
- apply (action, target&, const match_result&) const = 0;
- };
-
- // Fallback rule that on update verifies that the file exists and is
- // not older than any of its prerequisites.
- //
- class file_rule: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_update (action, target&);
-
- static file_rule instance;
- };
-
- class alias_rule: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static alias_rule instance;
- };
-
- class fsdir_rule: public rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string& hint) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_update (action, target&);
-
- static target_state
- perform_clean (action, target&);
-
- static fsdir_rule instance;
- };
-
- // Fallback rule that always matches and does nothing.
- //
- class fallback_rule: public build::rule
- {
- public:
- virtual match_result
- match (action, target& t, const std::string&) const {return t;}
-
- virtual recipe
- apply (action, target&, const match_result&) const {return noop_recipe;}
-
- static fallback_rule instance;
- };
-}
-
-#endif // BUILD_RULE
diff --git a/build/rule-map b/build/rule-map
deleted file mode 100644
index 74d1cfc..0000000
--- a/build/rule-map
+++ /dev/null
@@ -1,115 +0,0 @@
-// file : build/rule-map -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_RULE_MAP
-#define BUILD_RULE_MAP
-
-#include <map>
-#include <vector>
-#include <string>
-#include <memory> // unique_ptr
-#include <functional> // reference_wrapper
-
-#include <butl/prefix-map>
-
-#include <build/types>
-#include <build/operation>
-
-namespace build
-{
- class rule;
-
- using target_type_rule_map = std::map<
- const target_type*,
- butl::prefix_map<std::string, // Rule hint.
- std::reference_wrapper<rule>, '.'>>;
-
- // This is an "indexed map" with operation_id being the index. Entry
- // with id 0 is a wildcard.
- //
- class operation_rule_map
- {
- public:
- template <typename T>
- void
- insert (operation_id oid, const char* hint, rule& r)
- {
- // 3 is the number of builtin operations.
- //
- if (oid >= map_.size ())
- map_.resize ((oid < 3 ? 3 : oid) + 1);
-
- map_[oid][&T::static_type].emplace (hint, r);
- }
-
- // Return NULL if not found.
- //
- const target_type_rule_map*
- operator[] (operation_id oid) const
- {
- return map_.size () > oid ? &map_[oid] : nullptr;
- }
-
- bool
- empty () const {return map_.empty ();}
-
- private:
- std::vector<target_type_rule_map> map_;
- };
-
- // This is another indexed map but this time meta_operation_id is the
- // index. The implementation is different, however: here we use a linked
- // list with the first, statically-allocated node corresponding to the
- // perform meta-operation. The idea is to try and get away with a dynamic
- // allocation for the common cases since most rules will be registered
- // for perform, at least on non-root scopes.
- //
- class rule_map
- {
- public:
-
- template <typename T>
- void
- insert (action_id a, const char* hint, rule& r)
- {
- insert<T> (a >> 4, a & 0x0F, hint, r);
- }
-
- template <typename T>
- void
- insert (meta_operation_id mid, operation_id oid, const char* hint, rule& r)
- {
- if (mid_ == mid)
- map_.insert<T> (oid, hint, r);
- else
- {
- if (next_ == nullptr)
- next_.reset (new rule_map (mid));
-
- next_->insert<T> (mid, oid, hint, r);
- }
- }
-
- // Return NULL if not found.
- //
- const operation_rule_map*
- operator[] (meta_operation_id mid) const
- {
- return mid == mid_ ? &map_ : next_ == nullptr ? nullptr : (*next_)[mid];
- }
-
- explicit
- rule_map (meta_operation_id mid = perform_id): mid_ (mid) {}
-
- bool
- empty () const {return map_.empty () && next_ == nullptr;}
-
- private:
- meta_operation_id mid_;
- operation_rule_map map_;
- std::unique_ptr<rule_map> next_;
- };
-}
-
-#endif // BUILD_RULE_MAP
diff --git a/build/rule.cxx b/build/rule.cxx
deleted file mode 100644
index cce291c..0000000
--- a/build/rule.cxx
+++ /dev/null
@@ -1,249 +0,0 @@
-// file : build/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/rule>
-
-#include <utility> // move()
-#include <system_error>
-
-#include <butl/filesystem>
-
-#include <build/scope>
-#include <build/target>
-#include <build/algorithm>
-#include <build/diagnostics>
-#include <build/context>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- // file_rule
- //
- // Note that this rule is special. It is the last, fallback rule. If
- // it doesn't match, then no other rule can possibly match and we have
- // an error. It also cannot be ambigious with any other rule. As a
- // result the below implementation bends or ignores quite a few rules
- // that normal implementations should follow. So you probably shouldn't
- // use it as a guide to implement your own, normal, rules.
- //
- match_result file_rule::
- match (action a, target& t, const string&) const
- {
- tracer trace ("file_rule::match");
-
- // While strictly speaking we should check for the file's existence
- // for every action (because that's the condition for us matching),
- // for some actions this is clearly a waste. Say, perform_clean: we
- // are not doing anything for this action so not checking if the file
- // exists seems harmless. So the overall guideline seems to be this:
- // if we don't do anything for the action (other than performing it
- // on the prerequisites), then we match.
- //
- switch (a)
- {
- case perform_update_id:
- {
- path_target& pt (dynamic_cast<path_target&> (t));
-
- // Assign the path. While normally we shouldn't do this in match(),
- // no other rule should ever be ambiguous with the fallback one.
- //
- if (pt.path ().empty ())
- pt.derive_path ();
-
- // We cannot just call pt.mtime() since we haven't matched yet.
- //
- timestamp ts (file_mtime (pt.path ()));
- pt.mtime (ts);
-
- if (ts != timestamp_nonexistent)
- return t;
-
- level4 ([&]{trace << "no existing file for target " << t;});
- return nullptr;
- }
- default:
- return t;
- }
- }
-
- recipe file_rule::
- apply (action a, target& t, const match_result&) const
- {
- // Update triggers the update of this target's prerequisites
- // so it would seem natural that we should also trigger their
- // cleanup. However, this possibility is rather theoretical
- // since such an update would render this target out of date
- // which in turn would lead to an error. So until we see a
- // real use-case for this functionality, we simply ignore
- // the clean operation.
- //
- if (a.operation () == clean_id)
- return noop_recipe;
-
- // If we have no prerequisites, then this means this file
- // is up to date. Return noop_recipe which will also cause
- // the target's state to be set to unchanged. This is an
- // important optimization on which quite a few places that
- // deal with predominantly static content rely.
- //
- if (!t.has_prerequisites ())
- return noop_recipe;
-
- // Search and match all the prerequisites.
- //
- search_and_match_prerequisites (a, t);
- return a == perform_update_id ? &perform_update : default_recipe;
- }
-
- target_state file_rule::
- perform_update (action a, target& t)
- {
- // Make sure the target is not older than any of its prerequisites.
- //
- timestamp mt (dynamic_cast<path_target&> (t).mtime ());
-
- for (target* pt: t.prerequisite_targets)
- {
- target_state ts (execute (a, *pt));
-
- // If this is an mtime-based target, then compare timestamps.
- //
- if (auto mpt = dynamic_cast<const mtime_target*> (pt))
- {
- timestamp mp (mpt->mtime ());
-
- if (mt < mp)
- fail << "no recipe to " << diag_do (a, t) <<
- info << "prerequisite " << *pt << " is ahead of " << t
- << " by " << (mp - mt);
- }
- else
- {
- // Otherwise we assume the prerequisite is newer if it was changed.
- //
- if (ts == target_state::changed)
- fail << "no recipe to " << diag_do (a, t) <<
- info << "prerequisite " << *pt << " is ahead of " << t
- << " because it was updated";
- }
- }
-
- return target_state::unchanged;
- }
-
- file_rule file_rule::instance;
-
- // alias_rule
- //
- match_result alias_rule::
- match (action, target& t, const string&) const
- {
- return t;
- }
-
- recipe alias_rule::
- apply (action a, target& t, const match_result&) const
- {
- search_and_match_prerequisites (a, t);
- return default_recipe;
- }
-
- alias_rule alias_rule::instance;
-
- // fsdir_rule
- //
- match_result fsdir_rule::
- match (action, target& t, const string&) const
- {
- return t;
- }
-
- recipe fsdir_rule::
- apply (action a, target& t, const match_result&) const
- {
- // Inject dependency on the parent directory. Note that we
- // don't do it for clean since we shouldn't be removing it.
- //
- if (a.operation () != clean_id)
- inject_parent_fsdir (a, t);
-
- search_and_match_prerequisites (a, t);
-
- switch (a)
- {
- case perform_update_id: return &perform_update;
- case perform_clean_id: return &perform_clean;
- default: assert (false); return default_recipe;
- }
- }
-
- target_state fsdir_rule::
- perform_update (action a, target& t)
- {
- target_state ts (target_state::unchanged);
-
- // First update prerequisites (e.g. create parent directories)
- // then create this directory.
- //
- if (!t.prerequisite_targets.empty ())
- ts = execute_prerequisites (a, t);
-
- const dir_path& d (t.dir); // Everything is in t.dir.
-
- // Generally, it is probably correct to assume that in the majority
- // of cases the directory will already exist. If so, then we are
- // going to get better performance by first checking if it indeed
- // exists. See try_mkdir() for details.
- //
- if (!dir_exists (d))
- {
- if (verb >= 2)
- text << "mkdir " << d;
- else if (verb)
- text << "mkdir " << t;
-
- try
- {
- try_mkdir (d);
- }
- catch (const system_error& e)
- {
- fail << "unable to create directory " << d << ": " << e.what ();
- }
-
- ts |= target_state::changed;
- }
-
- return ts;
- }
-
- target_state fsdir_rule::
- perform_clean (action a, target& t)
- {
- // The reverse order of update: first delete this directory,
- // then clean prerequisites (e.g., delete parent directories).
- //
- // Don't fail if we couldn't remove the directory because it
- // is not empty (or is current working directory). In this
- // case rmdir() will issue a warning when appropriate.
- //
- target_state ts (rmdir (t.dir, t)
- ? target_state::changed
- : target_state::unchanged);
-
- if (!t.prerequisite_targets.empty ())
- ts |= reverse_execute_prerequisites (a, t);
-
- return ts;
- }
-
- fsdir_rule fsdir_rule::instance;
-
- // fallback_rule
- //
- fallback_rule fallback_rule::instance;
-}
diff --git a/build/scope b/build/scope
deleted file mode 100644
index 0cb319f..0000000
--- a/build/scope
+++ /dev/null
@@ -1,312 +0,0 @@
-// file : build/scope -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_SCOPE
-#define BUILD_SCOPE
-
-#include <functional> // function
-#include <unordered_set>
-#include <unordered_map>
-
-#include <butl/path-map>
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/module>
-#include <build/variable>
-#include <build/prerequisite>
-#include <build/target-type>
-#include <build/rule-map>
-#include <build/operation>
-
-namespace build
-{
- class scope
- {
- public:
- // Absolute and normalized.
- //
- const dir_path&
- out_path () const {return *out_path_;}
-
- const dir_path&
- src_path () const {return *src_path_;}
-
- // These are pointers to the keys in scope_map.
- //
- const dir_path* out_path_ {nullptr};
- const dir_path* src_path_ {nullptr};
-
- bool
- root () const {return root_ == this;}
-
- scope*
- parent_scope () const {return parent_;}
-
- // Root scope of this scope or NULL if this scope is not (yet)
- // in any (known) project. Note that if the scope itself is
- // root, then this function return this. To get to the outer
- // root, query the root scope of the parent.
- //
- scope*
- root_scope () const {return root_;}
-
- // Root scope of a strong amalgamation of this scope or NULL if
- // this scope is not (yet) in any (known) project. If there is
- // no strong amalgamation, then this function returns the root
- // scope of the project (in other words, in this case a project
- // is treated as its own strong amalgamation).
- //
- scope*
- strong_scope () const
- {
- return root_ != nullptr
- ? root_->strong_ != nullptr ? root_->strong_ : root_
- : nullptr;
- }
-
- // Root scope of the outermost amalgamation or NULL if this scope is not
- // (yet) in any (known) project. If there is no amalgamation, then this
- // function returns the root scope of the project (in other words, in this
- // case a project is treated as its own amalgamation).
- //
- scope*
- weak_scope () const
- {
- scope* r (root_);
- if (r != nullptr)
- for (; r->parent_->root_ != nullptr; r = r->parent_->root_) ;
- return r;
- }
-
- // Variables.
- //
- public:
- variable_map vars;
-
- // Lookup, including in outer scopes. If you only want to lookup
- // in this scope, do it on the the variables map directly.
- //
- build::lookup<const value>
- operator[] (const variable& var) const
- {
- return lookup (nullptr, nullptr, var);
- }
-
- build::lookup<const value>
- operator[] (const std::string& name) const
- {
- return operator[] (var_pool.find (name));
- }
-
- // As above, but includes target type/pattern-specific variables.
- //
- build::lookup<const value>
- lookup (const target_key& tk, const variable& var) const
- {
- return lookup (tk.type, tk.name, var);
- }
-
- build::lookup<const value>
- lookup (const target_key& tk, const string& var) const
- {
- return lookup (tk, var_pool.find (var));
- }
-
- build::lookup<const value>
- lookup (const target_type& tt,
- const string& name,
- const variable& var) const
- {
- return lookup (&tt, &name, var);
- }
-
- build::lookup<const value>
- lookup (const target_type& tt, const string& name, const string& var) const
- {
- return lookup (tt, name, var_pool.find (var));
- }
-
- build::lookup<const value>
- lookup (const target_type*, const string* name, const variable&) const;
-
- // Return a value suitable for assignment (or append if you only
- // want to append to the value from this scope). If the variable
- // does not exist in this scope's map, then a new one with the
- // NULL value is added and returned. Otherwise the existing value
- // is returned.
- //
- value&
- assign (const variable& var) {return vars.assign (var).first.get ();}
-
- value&
- assign (const std::string& name) {return vars.assign (name).first.get ();}
-
- // Return a value suitable for appending. If the variable does not
- // exist in this scope's map, then outer scopes are searched for
- // the same variable. If found then a new variable with the found
- // value is added to this scope and returned. Otherwise this
- // function proceeds as assign().
- //
- value&
- append (const variable&);
-
- value&
- append (const std::string& name)
- {
- return append (var_pool.find (name));
- }
-
- // Target type/pattern-specific variables.
- //
- variable_type_map target_vars;
-
- // Prerequisite cache.
- //
- public:
- prerequisite_set prerequisites;
-
- // Meta/operations supported by this project (set on the root
- // scope only).
- //
- build::meta_operations meta_operations;
- build::operations operations;
-
- typedef build::path path_type;
-
- // Set of buildfiles already loaded for this scope. The included
- // buildfiles are checked against the project's root scope while
- // imported -- against the global scope (global_scope).
- //
- std::unordered_set<path_type> buildfiles;
-
- // Target types.
- //
- public:
- target_type_map target_types;
-
- const target_type*
- find_target_type (const string&, const scope** = nullptr) const;
-
- // Given a name, figure out its type, taking into account extensions,
- // special names (e.g., '.' and '..'), or anything else that might be
- // relevant. Also process the name (in place) by extracting the
- // extension, adjusting dir/value, etc., (note that the dir is not
- // necessarily normalized). Return NULL if not found.
- //
- const target_type*
- find_target_type (name&, const string*& ext) const;
-
- // Rules.
- //
- public:
- rule_map rules;
-
- // Modules.
- //
- public:
- loaded_module_map modules; // Only on root scope.
-
- public:
- bool
- empty () const
- {
- return
- vars.empty () &&
- target_vars.empty () &&
- prerequisites.empty () &&
- meta_operations.empty () &&
- operations.empty () &&
- buildfiles.empty () &&
- target_types.empty () &&
- rules.empty () &&
- modules.empty ();
- }
-
- private:
- friend class scope_map;
- friend class temp_scope;
-
- // These two from <build/file> set strong_.
- //
- friend void create_bootstrap_outer (scope&);
- friend scope& create_bootstrap_inner (scope&, const dir_path&);
-
- scope () = default;
-
- scope* parent_;
- scope* root_;
- scope* strong_ = nullptr; // Only set on root sopes.
- // NULL means no strong amalgamtion.
- };
-
- // 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/prerequisites 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)
- {
- out_path_ = p.out_path_;
- src_path_ = p.src_path_;
- parent_ = &p;
- root_ = p.root_;
- // No need to copy strong_ since we are never root scope.
- }
- };
-
- class scope_map
- {
- public:
- using map_type = butl::dir_path_map<scope*>;
- using iterator = map_type::iterator;
- using const_iterator = map_type::const_iterator;
-
- // Note that we assume the first insertion into the map is that
- // of the global scope. If the passed scope pointer is not NULL,
- // then insert this scope instead of a new one.
- //
- iterator
- insert (const dir_path&, scope*, bool parent, bool root);
-
- // Find the most qualified scope that encompasses this path.
- //
- scope&
- find (const dir_path&) const;
-
- scope&
- find (const path& p) const
- {
- // Natural thing to do here would be to call find (p.directory ()).
- // However, there could be a situation where the passed path is a
- // directory (i.e., the calling code does not know what it is dealing
- // with), so let's use the whole path.
- //
- return find (dir_path (p.string ()));
- }
-
- const_iterator begin () const {return map_.begin ();}
- const_iterator end () const {return map_.end ();}
-
- void
- clear ();
-
- ~scope_map () {clear ();}
-
- private:
- map_type map_;
- };
-
- extern scope_map scopes;
- extern scope* global_scope;
-}
-
-#endif // BUILD_SCOPE
diff --git a/build/scope.cxx b/build/scope.cxx
deleted file mode 100644
index dfddf20..0000000
--- a/build/scope.cxx
+++ /dev/null
@@ -1,317 +0,0 @@
-// file : build/scope.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/scope>
-
-#include <build/target>
-
-using namespace std;
-
-namespace build
-{
- // scope
- //
- lookup<const value> scope::
- lookup (const target_type* tt, const string* name, const variable& var) const
- {
- using result = build::lookup<const value>;
-
- for (const scope* s (this); s != nullptr; )
- {
- if (tt != nullptr && !s->target_vars.empty ())
- {
- if (auto l = s->target_vars.lookup (*tt, *name, var))
- return l;
- }
-
- if (auto r = s->vars.find (var))
- return result (r, &s->vars);
-
- switch (var.visibility)
- {
- case variable_visibility::scope:
- s = nullptr;
- break;
- case variable_visibility::project:
- s = s->root () ? nullptr : s->parent_scope ();
- break;
- case variable_visibility::normal:
- s = s->parent_scope ();
- break;
- }
- }
-
- return result ();
- }
-
- value& scope::
- append (const variable& var)
- {
- auto l (operator[] (var));
-
- if (l && l.belongs (*this)) // Existing variable in this scope.
- return const_cast<value&> (*l);
-
- value& r (assign (var));
-
- if (l)
- r = *l; // Copy value from the outer scope.
-
- return r;
- }
-
- const target_type* scope::
- find_target_type (const string& tt, const scope** rs) const
- {
- // Search scopes outwards, stopping at the project root.
- //
- for (const scope* s (this);
- s != nullptr;
- s = s->root () ? global_scope : s->parent_scope ())
- {
- if (s->target_types.empty ())
- continue;
-
- auto i (s->target_types.find (tt));
-
- if (i != s->target_types.end ())
- {
- if (rs != nullptr)
- *rs = s;
-
- return &i->second.get ();
- }
- }
-
- return nullptr;
- }
-
- static const string dir_tt ("dir");
- static const string file_tt ("file");
-
- const target_type* scope::
- find_target_type (name& n, const string*& ext) const
- {
- ext = nullptr;
-
- string& v (n.value);
-
- // First determine the target type.
- //
- const string* tt;
- if (n.untyped ())
- {
- // Empty name or '.' and '..' signify a directory.
- //
- if (v.empty () || v == "." || v == "..")
- tt = &dir_tt;
- else
- //@@ TODO: derive type from extension.
- //
- tt = &file_tt;
- }
- else
- tt = &n.type;
-
- const target_type* r (find_target_type (*tt));
-
- if (r == nullptr)
- return r;
-
- // Directories require special name processing. If we find that more
- // targets deviate, then we should make this target-type-specific.
- //
- if (r->is_a<dir> () || r->is_a<fsdir> ())
- {
- // The canonical representation of a directory name is with empty
- // value.
- //
- if (!v.empty ())
- {
- n.dir /= dir_path (v); // Move name value to dir.
- v.clear ();
- }
- }
- else
- {
- // Split the path into its directory part (if any) the name part,
- // and the extension (if any). We cannot assume the name part is
- // a valid filesystem name so we will have to do the splitting
- // manually.
- //
- path::size_type i (path::traits::rfind_separator (v));
-
- if (i != string::npos)
- {
- n.dir /= dir_path (v, i != 0 ? i : 1); // Special case: "/".
- v = string (v, i + 1, string::npos);
- }
-
- // Extract the extension.
- //
- string::size_type j (path::traits::find_extension (v));
-
- if (j != string::npos)
- {
- ext = &extension_pool.find (v.c_str () + j + 1);
- v.resize (j);
- }
- }
-
- return r;
- }
-
- // scope_map
- //
- scope_map scopes;
- scope* global_scope;
-
- auto scope_map::
- insert (const dir_path& k, scope* ns, bool parent, bool root) -> iterator
- {
- auto er (map_.emplace (k, nullptr));
- scope*& ps (er.first->second);
-
- if (er.second)
- ps = ns == nullptr ? new scope : ns;
- else if (ns != nullptr && ps != ns)
- {
- assert (ps->out_path_ == nullptr || ps->src_path_ == nullptr);
-
- if (!ps->empty ())
- fail << "attempt to replace non-empty scope " << k;
-
- // Un-parent ourselves. We will becomes a new parent below,
- // if requested by the caller.
- //
- auto r (map_.find_prefix (k)); // The first entry is ourselves.
- for (++r.first; r.first != r.second; ++r.first)
- {
- scope& c (*r.first->second);
-
- if (c.parent_ == ps) // No intermediate parent.
- c.parent_ = ps->parent_;
- }
-
- delete ps;
- ps = ns;
- er.second = true;
- }
-
- scope& s (*ps);
-
- if (parent)
- {
- if (er.second)
- {
- scope* p (nullptr);
-
- // Update scopes of which we are a new parent/root (unless this
- // is the global scope). Also find our parent while at it.
- //
- if (map_.size () > 1)
- {
- // The first entry is ourselves.
- //
- auto r (map_.find_prefix (k));
- for (++r.first; r.first != r.second; ++r.first)
- {
- scope& c (*r.first->second);
-
- // The child-parent relationship is based on the out hierarchy,
- // thus the extra check.
- //
- if (c.out_path_ != nullptr && !c.out_path_->sub (k))
- continue;
-
- // The first scope of which we are a parent is the least
- // (shortest) one which means there is no other scope
- // between it and our parent.
- //
- if (p == nullptr)
- p = c.parent_;
-
- if (root && c.root_ == p->root_) // No intermediate root.
- c.root_ = &s;
-
- if (p == c.parent_) // No intermediate parent.
- c.parent_ = &s;
- }
-
- // We couldn't get the parent from one of its old children
- // so we have to find it ourselves.
- //
- if (p == nullptr)
- p = &find (k.directory ());
- }
-
- s.parent_ = p;
- s.root_ = root ? &s : (p != nullptr ? p->root_ : nullptr);
- }
- else if (root && !s.root ())
- {
- // Upgrade to root scope.
- //
- auto r (map_.find_prefix (k));
- for (++r.first; r.first != r.second; ++r.first)
- {
- scope& c (*r.first->second);
-
- if (c.root_ == s.root_) // No intermediate root.
- c.root_ = &s;
- }
-
- s.root_ = &s;
- }
- }
- else
- assert (s.parent_ != nullptr);
-
- return er.first;
- }
-
- // Find the most qualified scope that encompasses this path.
- //
- scope& scope_map::
- find (const dir_path& k) const
- {
- // Normally we would have a scope for the full path so try
- // that before making any copies.
- //
- auto i (map_.find (k)), e (map_.end ());
-
- if (i != e)
- return *i->second;
-
- for (dir_path d (k.directory ());; d = d.directory ())
- {
- auto i (map_.find (d));
-
- if (i != e)
- return *i->second;
-
- assert (!d.empty ()); // We should have the global scope.
- }
- }
-
- void scope_map::
- clear ()
- {
- for (auto& p: map_)
- {
- scope* s (p.second);
-
- if (s->out_path_ == &p.first)
- s->out_path_ = nullptr;
-
- if (s->src_path_ == &p.first)
- s->src_path_ = nullptr;
-
- if (s->out_path_ == nullptr && s->src_path_ == nullptr)
- delete s;
- }
-
- map_.clear ();
- }
-}
diff --git a/build/search b/build/search
deleted file mode 100644
index d5e2c04..0000000
--- a/build/search
+++ /dev/null
@@ -1,31 +0,0 @@
-// file : build/search -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_SEARCH
-#define BUILD_SEARCH
-
-#include <build/types>
-
-namespace build
-{
- class target;
- class prerequisite_key;
-
- // Search for an existing target in this prerequisite's scope.
- //
- target*
- search_existing_target (const prerequisite_key&);
-
- // Search for an existing file in the specified list of search paths.
- //
- target*
- search_existing_file (const prerequisite_key&, const dir_paths&);
-
- // Create a new target in this prerequisite's scope.
- //
- target&
- create_new_target (const prerequisite_key&);
-}
-
-#endif // BUILD_SEARCH
diff --git a/build/search.cxx b/build/search.cxx
deleted file mode 100644
index c06d281..0000000
--- a/build/search.cxx
+++ /dev/null
@@ -1,171 +0,0 @@
-// file : build/search.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/search>
-
-#include <utility> // move()
-#include <cassert>
-
-#include <butl/filesystem>
-
-#include <build/scope>
-#include <build/target>
-#include <build/prerequisite>
-#include <build/diagnostics>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- target*
- search_existing_target (const prerequisite_key& pk)
- {
- tracer trace ("search_existing_target");
-
- const target_key& tk (pk.tk);
-
- // Look for an existing target in this directory scope.
- //
- dir_path d;
- if (tk.dir->absolute ())
- d = *tk.dir; // Already normalized.
- else
- {
- d = pk.scope->out_path ();
-
- if (!tk.dir->empty ())
- {
- d /= *tk.dir;
- d.normalize ();
- }
- }
-
- auto i (targets.find (*tk.type, d, *tk.name, *tk.ext, trace));
-
- if (i == targets.end ())
- return 0;
-
- target& t (**i);
-
- level5 ([&]{trace << "existing target " << t << " for prerequisite "
- << pk;});
-
- return &t;
- }
-
- target*
- search_existing_file (const prerequisite_key& cpk, const dir_paths& sp)
- {
- tracer trace ("search_existing_file");
-
- prerequisite_key pk (cpk); // Make a copy so we can update extension.
- target_key& tk (pk.tk);
- assert (tk.dir->relative ());
-
- // Figure out the extension. Pretty similar logic to file::derive_path().
- //
- const string* ext (*tk.ext);
-
- if (ext == nullptr)
- {
- if (auto f = tk.type->extension)
- {
- ext = &f (tk, *pk.scope); // Already from the pool.
- tk.ext = &ext;
- }
- else
- {
- // What should we do here, fail or say we didn't find anything?
- // Current think is that if the target type didn't provide the
- // default extension, then it doesn't want us to search for an
- // existing file (of course, if the user specified the extension
- // explicitly, we will still do so). But let me know what you
- // think.
- //
- //fail << "no default extension for prerequisite " << pk;
- level4 ([&]{trace << "no existing file found for prerequisite "
- << pk;});
- return nullptr;
- }
- }
-
- // Go over paths looking for a file.
- //
- for (const dir_path& d: sp)
- {
- path f (d / *tk.dir / path (*tk.name));
- f.normalize ();
-
- if (!ext->empty ())
- {
- f += '.';
- f += *ext;
- }
-
- timestamp mt (file_mtime (f));
-
- if (mt == timestamp_nonexistent)
- continue;
-
- level5 ([&]{trace << "found existing file " << f << " for prerequisite "
- << pk;});
-
- // Find or insert. Note: using our updated extension.
- //
- auto r (targets.insert (*tk.type, f.directory (), *tk.name, ext, trace));
-
- // Has to be a file_target.
- //
- file& t (dynamic_cast<file&> (r.first));
-
- level5 ([&]{trace << (r.second ? "new" : "existing") << " target "
- << t << " for prerequisite " << pk;});
-
- if (t.path ().empty ())
- t.path (move (f));
-
- t.mtime (mt);
- return &t;
- }
-
- level4 ([&]{trace << "no existing file found for prerequisite " << pk;});
- return nullptr;
- }
-
- target&
- create_new_target (const prerequisite_key& pk)
- {
- tracer trace ("create_new_target");
-
- const target_key& tk (pk.tk);
-
- // We default to the target in this directory scope.
- //
- dir_path d;
- if (tk.dir->absolute ())
- d = *tk.dir; // Already normalized.
- else
- {
- d = pk.scope->out_path ();
-
- if (!tk.dir->empty ())
- {
- d /= *tk.dir;
- d.normalize ();
- }
- }
-
- // Find or insert.
- //
- auto r (targets.insert (*tk.type, move (d), *tk.name, *tk.ext, trace));
- assert (r.second);
-
- target& t (r.first);
-
- level5 ([&]{trace << "new target " << t << " for prerequisite " << pk;});
-
- return t;
- }
-}
diff --git a/build/spec b/build/spec
deleted file mode 100644
index f50110b..0000000
--- a/build/spec
+++ /dev/null
@@ -1,61 +0,0 @@
-// file : build/spec -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_SPEC
-#define BUILD_SPEC
-
-#include <string>
-#include <vector>
-#include <iosfwd>
-#include <utility> // move()
-
-#include <build/types>
-
-namespace build
-{
- struct targetspec
- {
- typedef build::name name_type;
-
- explicit
- targetspec (name_type n): name (std::move (n)) {}
- targetspec (dir_path sb, name_type n)
- : src_base (std::move (sb)), name (std::move (n)) {}
-
- dir_path src_base;
- name_type name;
- };
-
- struct opspec: std::vector<targetspec>
- {
- opspec () = default;
- opspec (std::string n): name (std::move (n)) {}
-
- std::string name;
- };
-
- struct metaopspec: std::vector<opspec>
- {
- metaopspec () = default;
- metaopspec (std::string n): name (std::move (n)) {}
-
- std::string name;
- };
-
- typedef std::vector<metaopspec> buildspec;
-
- std::ostream&
- operator<< (std::ostream&, const targetspec&);
-
- std::ostream&
- operator<< (std::ostream&, const opspec&);
-
- std::ostream&
- operator<< (std::ostream&, const metaopspec&);
-
- std::ostream&
- operator<< (std::ostream&, const buildspec&);
-}
-
-#endif // BUILD_SPEC
diff --git a/build/spec.cxx b/build/spec.cxx
deleted file mode 100644
index b08cdb3..0000000
--- a/build/spec.cxx
+++ /dev/null
@@ -1,81 +0,0 @@
-// file : build/spec.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/spec>
-
-#include <ostream>
-
-#include <build/context>
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- ostream&
- operator<< (ostream& os, const targetspec& s)
- {
- if (!s.src_base.empty ())
- {
- string d (diag_relative (s.src_base, false));
-
- if (!d.empty ())
- os << d << '@';
- }
-
- os << s.name;
- return os;
- }
-
- ostream&
- operator<< (ostream& os, const opspec& s)
- {
- bool hn (!s.name.empty ());
- bool ht (!s.empty ());
-
- //os << s.name;
- os << (hn ? "\"" : "") << s.name << (hn ? "\"" : "");
-
- if (hn && ht)
- os << '(';
-
- for (auto b (s.begin ()), i (b); i != s.end (); ++i)
- os << (i != b ? " " : "") << *i;
-
- if (hn && ht)
- os << ')';
-
- return os;
- }
-
- ostream&
- operator<< (ostream& os, const metaopspec& s)
- {
- bool hn (!s.name.empty ());
- bool ho (!s.empty ());
-
- //os << s.name;
- os << (hn ? "\'" : "") << s.name << (hn ? "\'" : "");
-
- if (hn && ho)
- os << '(';
-
- for (auto b (s.begin ()), i (b); i != s.end (); ++i)
- os << (i != b ? " " : "") << *i;
-
- if (hn && ho)
- os << ')';
-
- return os;
- }
-
- ostream&
- operator<< (ostream& os, const buildspec& s)
- {
- for (auto b (s.begin ()), i (b); i != s.end (); ++i)
- os << (i != b ? " " : "") << *i;
-
- return os;
- }
-}
diff --git a/build/target b/build/target
deleted file mode 100644
index 22b5e89..0000000
--- a/build/target
+++ /dev/null
@@ -1,1084 +0,0 @@
-// file : build/target -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_TARGET
-#define BUILD_TARGET
-
-#include <map>
-#include <string>
-#include <vector>
-#include <memory> // unique_ptr
-#include <cstddef> // size_t
-#include <cstdint> // uint8_t
-#include <functional> // reference_wrapper
-#include <ostream>
-#include <cassert>
-#include <utility> // move(), forward(), declval()
-#include <iterator>
-#include <type_traits>
-
-#include <butl/utility> // reverse_iterate()
-#include <butl/multi-index> // map_iterator_adapter
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/scope>
-#include <build/variable>
-#include <build/operation>
-#include <build/target-type>
-#include <build/target-key>
-#include <build/prerequisite>
-
-namespace build
-{
- class scope;
- class target;
-
- target&
- search (prerequisite&); // From <build/algorithm>.
-
- // Target state.
- //
- enum class target_state: std::uint8_t
- {
- // The order of the enumerators is arranged so that their integral
- // values indicate whether one "overrides" the other in the merge
- // operator (see below).
- //
- unknown,
- unchanged,
- postponed,
- changed,
- failed,
- group // Target's state is the group's state.
- };
-
- std::ostream&
- operator<< (std::ostream&, target_state);
-
- inline target_state&
- operator |= (target_state& l, target_state r)
- {
- if (static_cast<std::uint8_t> (r) > static_cast<std::uint8_t> (l))
- l = r;
-
- return l;
- }
-
- // Recipe.
- //
- // The returned target state should be changed, unchanged, or
- // postponed, though you shouldn't be returning postponed
- // directly. If there is an error, then the recipe should
- // throw rather than returning failed.
- //
- // The return value of the recipe is used to update the target
- // state except if the state was manually set by the recipe to
- // target_state::group. Note that in this case the returned by
- // the recipe value is still used as the resulting target state
- // so it should match the group's state.
- //
- using recipe_function = target_state (action, target&);
- using recipe = std::function<recipe_function>;
-
- // Commonly-used recipes. The default recipe executes the action on
- // all the prerequisites in a loop, skipping ignored. Specifically,
- // for actions with the "first" execution mode, it calls
- // execute_prerequisites() while for those with the "last" mode --
- // reverse_execute_prerequisites(); see <build/operation>,
- // <build/algorithm> for details. The group recipe call's the group's
- // recipe.
- //
- extern const recipe empty_recipe;
- extern const recipe noop_recipe;
- extern const recipe default_recipe;
- extern const recipe group_recipe;
-
- target_state
- noop_action (action, target&); // Defined in <build/algorithm>.
-
- target_state
- group_action (action, target&); // Defined in <build/algorithm>.
-
- // Prerequisite references as used in the target::prerequisites list
- // below.
- //
- struct prerequisite_ref: std::reference_wrapper<prerequisite>
- {
- typedef std::reference_wrapper<prerequisite> base;
-
- using base::base;
-
- // Return true if this reference belongs to the target's prerequisite
- // list. Note that this test only works if you use references to
- // the container elements and the container hasn't been resized
- // since such a reference was obtained. Normally this function is
- // used when iterating over a combined prerequisites range (see
- // group_prerequisites below).
- //
- bool
- belongs (const target&) const;
- };
-
- // A view of target group members.
- //
- struct group_view
- {
- target* const* members; // NULL means not yet known.
- std::size_t count;
- };
-
- // Target.
- //
- class target
- {
- public:
- typedef build::action action_type;
-
- virtual
- ~target () = default;
-
- target (const target&) = delete;
- target& operator= (const target&) = delete;
-
- target (dir_path d, std::string n, const std::string* e)
- : dir (std::move (d)), name (std::move (n)), ext (e) {}
-
- // Reset the target before matching a rule for it. The
- // default implementation clears prerequisite_targets.
- //
- virtual void
- reset (action_type);
-
- const dir_path dir; // Absolute and normalized.
- const std::string name;
- const std::string* ext; // Extension, NULL means unspecified,
- // empty means no extension.
-
- // Target group to which this target belongs, if any. Note that
- // we assume that the group and all its members are in the same
- // scope (for example, in variable lookup). We also don't support
- // nested groups.
- //
- // The semantics of the interaction between the group and its
- // members and what it means to, say, update the group, is
- // unspecified and determined by the group's type. In particular,
- // a group can be created out of member types that have no idea
- // they are part of this group (e.g., cli.cxx{}).
- //
- // Normally, however, there are two kinds of groups: "alternatives"
- // and "combination". In an alternatives group, normally one of the
- // members is selected when the group is mentioned as a prerequisite
- // with, perhaps, an exception for special rules, like aliases, where
- // it makes more sense to treat the group as a whole. In this case we
- // say that the rule "semantically recognizes" the group and picks
- // some of its members.
- //
- // Updating an alternatives group as a whole can mean updating some
- // subset of its members (e.g., lib{}). Or the group may not support
- // this at all (e.g., obj{}).
- //
- // In a combination group, when a group is updated, normally all
- // members are updates (and usually with a single command), though
- // there could be some members that are omitted, depending on the
- // configuration (e.g., an inline file not/being generated). When
- // a combination group is mentioned as a prerequisite, the rule
- // is usually interested in the individual members rather than
- // the whole group. For example, a C++ compile rule would like to
- // "see" the ?xx{} members when it gets a cli.cxx{} group.
- //
- // Which brings us to the group iteration mode. The target type
- // contains a member called see_through that indicates whether the
- // default iteration mode for the group should be "see through";
- // that is, whether we see the members or the group itself. For
- // the iteration support itself, see the *_prerequisite_members()
- // machinery below.
- //
- target* group {nullptr};
-
- // You should not call this function directly; rather use
- // resolve_group_members() from <build/algorithm>.
- //
- virtual group_view
- group_members (action_type) const;
-
- target_key
- key () const {return target_key {&type (), &dir, &name, &ext};}
-
- // Scoping.
- //
- public:
- // Most qualified scope that contains this target.
- //
- scope&
- base_scope () const;
-
- // Root scope of a project that contains this target. Note that
- // a target can be out of any (known) project root in which case
- // this function asserts. If you need to detect this situation,
- // then use base_scope().root_scope() expression instead.
- //
- scope&
- root_scope () const;
-
- // Root scope of a strong amalgamation that contains this target.
- // The same notes as to root_scope() apply.
- //
- scope&
- strong_scope () const {return *root_scope ().strong_scope ();}
-
- // Root scope of the outermost amalgamation that contains this target.
- // The same notes as to root_scope() apply.
- //
- scope&
- weak_scope () const {return *root_scope ().weak_scope ();}
-
-
- bool
- in (const scope& s) const
- {
- return
- (s.out_path_ != nullptr && dir.sub (*s.out_path_)) ||
- (s.src_path_ != nullptr && dir.sub (*s.src_path_));
- }
-
- // Prerequisites.
- //
- public:
- typedef std::vector<prerequisite_ref> prerequisites_type;
- prerequisites_type prerequisites;
-
- // Targets to which prerequisites resolve for this recipe. Note
- // that unlike prerequisite::target, these can be resolved to
- // group members. NULL means the target should be skipped (or
- // the rule may simply not add such a target to the list).
- //
- // Note also that it is possible the target can vary from
- // action to action, just like recipes. We don't need to keep
- // track of the action here since the targets will be updated
- // if the recipe is updated, normally as part of rule::apply().
- //
- typedef std::vector<target*> prerequisite_targets_type;
- prerequisite_targets_type prerequisite_targets;
-
- // Check if there are any prerequisites, taking into account
- // group prerequisites.
- //
- bool
- has_prerequisites () const
- {
- return !prerequisites.empty () ||
- (group != nullptr && !group->prerequisites.empty ());
- }
-
- // Target-specific variables.
- //
- public:
- variable_map vars;
-
- // Lookup, including in groups to which this target belongs and
- // then in outer scopes (including target type/pattern-specific
- // variables). If you only want to lookup in this target, do it
- // on the variable map directly.
- //
- lookup<const value>
- operator[] (const variable&) const;
-
- lookup<const value>
- operator[] (const std::string& name) const
- {
- return operator[] (var_pool.find (name));
- }
-
- // Return a value suitable for assignment. See class scope for
- // details.
- //
- value&
- assign (const variable& var) {return vars.assign (var).first;}
-
- value&
- assign (const std::string& name) {return vars.assign (name).first;}
-
- // Return a value suitable for appending. See class scope for
- // details.
- //
- value&
- append (const variable&);
-
- value&
- append (const std::string& name)
- {
- return append (var_pool.find (name));
- }
-
- public:
- target_state raw_state;
-
- target_state
- state () const
- {
- return raw_state != target_state::group ? raw_state : group->raw_state;
- }
-
- // Number of direct targets that depend on this target in the current
- // action. It is incremented during the match phase and then decremented
- // during execution, before running the recipe. As a result, the recipe
- // can detect the last chance (i.e., last dependent) to execute the
- // command (see also the first/last execution modes in <operation>).
- //
- // Note that setting a new recipe (which happens when we match the rule
- // and which in turn is triggered by the first dependent) clears this
- // counter. However, if the previous action was the same as the current,
- // then the existing recipe is reused. In this case, however, the counter
- // should have been decremented to 0 naturally, as part of the previous
- // action execution.
- //
- std::size_t dependents;
-
- public:
- action_type action; // Action this recipe is for.
-
- public:
- typedef build::recipe recipe_type;
-
- const recipe_type&
- recipe (action_type a) const {return a > action ? empty_recipe : recipe_;}
-
- void
- recipe (action_type, recipe_type);
-
- // Target type info.
- //
- public:
- template <typename T>
- T*
- is_a () {return dynamic_cast<T*> (this);}
-
- template <typename T>
- const T*
- is_a () const {return dynamic_cast<const T*> (this);}
-
- // Dynamic derivation to support define.
- //
- const target_type* derived_type = nullptr;
-
- const target_type&
- type () const
- {
- return derived_type != nullptr ? *derived_type : dynamic_type ();
- }
-
- virtual const target_type& dynamic_type () const = 0;
- static const target_type static_type;
-
- private:
- recipe_type recipe_;
- };
-
- std::ostream&
- operator<< (std::ostream&, const target&);
-
- // A "range" that presents the prerequisites of a group and one of
- // its members as one continuous sequence, or, in other words, as
- // if they were in a single container. The group's prerequisites
- // come first followed by the member's. If you need to see them
- // in the other direction, iterate in reverse, for example:
- //
- // for (prerequisite_ref& pr: group_prerequisites (t))
- //
- // for (prerequisite_ref& pr: reverse_iterate (group_prerequisites (t))
- //
- // Note that in this case the individual elements of each list will
- // also be traversed in reverse, but that's what you usually want,
- // anyway.
- //
- class group_prerequisites
- {
- public:
- typedef target::prerequisites_type prerequisites_type;
-
- explicit
- group_prerequisites (target& t): t_ (t) {}
-
- struct iterator
- {
- typedef prerequisites_type::iterator base_iterator;
-
- typedef base_iterator::value_type value_type;
- typedef base_iterator::pointer pointer;
- typedef base_iterator::reference reference;
- typedef base_iterator::difference_type difference_type;
- typedef std::bidirectional_iterator_tag iterator_category;
-
- iterator () {}
- iterator (target* t, prerequisites_type* c, base_iterator i)
- : t_ (t), c_ (c), i_ (i) {}
-
- iterator&
- operator++ ()
- {
- if (++i_ == c_->end () && c_ != &t_->prerequisites)
- {
- c_ = &t_->prerequisites;
- i_ = c_->begin ();
- }
- return *this;
- }
-
- iterator
- operator++ (int) {iterator r (*this); operator++ (); return r;}
-
- iterator&
- operator-- ()
- {
- if (i_ == c_->begin () && c_ == &t_->prerequisites)
- {
- c_ = &t_->group->prerequisites;
- i_ = c_->end ();
- }
-
- --i_;
- return *this;
- }
-
- iterator
- operator-- (int) {iterator r (*this); operator-- (); return r;}
-
- reference operator* () const {return *i_;}
- pointer operator-> () const {return i_.operator -> ();}
-
- friend bool
- operator== (const iterator& x, const iterator& y)
- {
- return x.t_ == y.t_ && x.c_ == y.c_ && x.i_ == y.i_;
- }
-
- friend bool
- operator!= (const iterator& x, const iterator& y) {return !(x == y);}
-
- private:
- target* t_ {nullptr};
- prerequisites_type* c_ {nullptr};
- base_iterator i_;
- };
-
- typedef std::reverse_iterator<iterator> reverse_iterator;
-
- iterator
- begin () const
- {
- auto& c ((t_.group != nullptr && !t_.group->prerequisites.empty ()
- ? *t_.group : t_).prerequisites);
- return iterator (&t_, &c, c.begin ());
- }
-
- iterator
- end () const
- {
- auto& c (t_.prerequisites);
- return iterator (&t_, &c, c.end ());
- }
-
- reverse_iterator
- rbegin () const {return reverse_iterator (end ());}
-
- reverse_iterator
- rend () const {return reverse_iterator (begin ());}
-
- std::size_t
- size () const
- {
- return t_.prerequisites.size () +
- (t_.group != nullptr ? t_.group->prerequisites.size () : 0);
- }
-
- private:
- target& t_;
- };
-
- // A member of a prerequisite. If 'target' is NULL, then this is the
- // prerequisite itself. Otherwise, it is its member. In this case
- // 'prerequisite' still refers to the prerequisite.
- //
- struct prerequisite_member
- {
- typedef build::target target_type;
- typedef build::prerequisite prerequisite_type;
-
- prerequisite_ref& prerequisite;
- target_type* target;
-
- template <typename T>
- bool
- is_a () const
- {
- return target != nullptr
- ? target->is_a<T> () != nullptr
- : prerequisite.get ().is_a<T> ();
- }
-
- prerequisite_key
- key () const
- {
- return target != nullptr
- ? prerequisite_key {prerequisite.get ().proj, target->key (), nullptr}
- : prerequisite.get ().key ();
- }
-
- const build::target_type&
- type () const
- {
- return target != nullptr ? target->type () : prerequisite.get ().type;
- }
-
- const std::string&
- name () const
- {
- return target != nullptr ? target->name : prerequisite.get ().name;
- }
-
- const std::string*
- proj () const
- {
- // Target cannot be project-qualified.
- //
- return target != nullptr ? nullptr : prerequisite.get ().proj;
- }
-
- target_type&
- search () const
- {
- return target != nullptr ? *target : build::search (prerequisite);
- }
-
- prerequisite_type&
- as_prerequisite (tracer&) const;
- };
-
- inline std::ostream&
- operator<< (std::ostream& os, const prerequisite_member& pm)
- {
- return os << pm.key ();
- }
-
- // A "range" that presents a sequence of prerequisites (e.g., from
- // group_prerequisites()) as a sequence of prerequisite_member's. For
- // each group prerequisite you will "see" either the prerequisite
- // itself or all its members, depending on the default iteration
- // mode of the target group type. You can skip the rest of the
- // group members with leave_group() and you can force iteration
- // over the members with enter_group(). Usage:
- //
- // for (prerequisite_member pm: prerequisite_members (a, ...))
- //
- // Where ... can be:
- //
- // t.prerequisites
- // reverse_iterate(t.prerequisites)
- // group_prerequisites (t)
- // reverse_iterate (group_prerequisites (t))
- //
- // But use shortcuts instead:
- //
- // prerequisite_members (a, t)
- // reverse_prerequisite_members (a, t)
- // group_prerequisite_members (a, t)
- // reverse_group_prerequisite_members (a, t)
- //
- template <typename T>
- class prerequisite_members_range;
-
- template <typename T>
- inline prerequisite_members_range<T>
- prerequisite_members (action a, T&& x, bool members = true)
- {
- return prerequisite_members_range<T> (a, std::forward<T> (x), members);
- }
-
- template <typename T>
- class prerequisite_members_range
- {
- public:
- prerequisite_members_range (action a, T&& r, bool m)
- : a_ (a), members_ (m), r_ (std::forward<T> (r)), e_ (r_.end ()) {}
-
- using base_iterator = decltype (std::declval<T> ().begin ());
-
- struct iterator
- {
- typedef prerequisite_member value_type;
- typedef const value_type* pointer;
- typedef const value_type& reference;
- typedef typename base_iterator::difference_type difference_type;
- typedef std::forward_iterator_tag iterator_category;
-
- iterator (): r_ (nullptr) {}
- iterator (const prerequisite_members_range* r, const base_iterator& i)
- : r_ (r), i_ (i), g_ {nullptr, 0}
- {
- if (r_->members_ && i_ != r_->e_ && i_->get ().type.see_through)
- {
- bool r (switch_members ());
- assert (r); // Group could not be resolved.
- }
- }
-
- iterator& operator++ ();
- iterator operator++ (int) {iterator r (*this); operator++ (); return r;}
-
- // Skip iterating over the rest of this group's members, if any.
- // Note that the only valid operation after this call is to
- // increment the iterator.
- //
- void
- leave_group ()
- {
- // Pretend we are on the last member of some group.
- //
- j_ = 0;
- g_.count = 1;
- }
-
- // Iterate over this group's members. Return false if the member
- // information is not available. Similar to leave_group(), you
- // should increment the iterator after calling this function
- // (provided it returned true).
- //
- bool
- enter_group ()
- {
- bool r (switch_members ());
- if (r)
- --j_; // Compensate for the increment that will follow.
- return r;
- }
-
- value_type operator* () const
- {
- return value_type {*i_, g_.count != 0 ? g_.members[j_ - 1] : nullptr};
- }
-
- pointer operator-> () const
- {
- static_assert (
- std::is_trivially_destructible<prerequisite_member>::value,
- "prerequisite_member is not trivially destructible");
-
- return new (&m_)
- value_type {*i_, g_.count != 0 ? g_.members[j_ - 1] : nullptr};
- }
-
- friend bool
- operator== (const iterator& x, const iterator& y)
- {
- return x.i_ == y.i_ &&
- x.g_.count == y.g_.count &&
- (x.g_.count == 0 || x.j_ == y.j_);
- }
-
- friend bool
- operator!= (const iterator& x, const iterator& y) {return !(x == y);}
-
- private:
- bool
- switch_members ();
-
- private:
- const prerequisite_members_range* r_;
- base_iterator i_;
- group_view g_;
- std::size_t j_; // 1-based index, to support enter_group().
- mutable std::aligned_storage<sizeof (prerequisite_member),
- alignof (prerequisite_member)>::type m_;
- };
-
- iterator
- begin () const {return iterator (this, r_.begin ());}
-
- iterator
- end () const {return iterator (this, e_);}
-
- private:
- action a_;
- bool members_; // Go into group members by default?
- T r_;
- base_iterator e_;
- };
-
- // prerequisite_members(t.prerequisites)
- //
- inline auto
- prerequisite_members (action a, target& t, bool members = true)
- {
- return prerequisite_members (a, t.prerequisites, members);
- }
-
- // prerequisite_members(reverse_iterate(t.prerequisites))
- //
- inline auto
- reverse_prerequisite_members (action a, target& t, bool members = true)
- {
- return prerequisite_members (
- a, butl::reverse_iterate (t.prerequisites), members);
- }
-
- // prerequisite_members(group_prerequisites (t))
- //
- inline auto
- group_prerequisite_members (action a, target& t, bool members = true)
- {
- return prerequisite_members (a, group_prerequisites (t), members);
- }
-
- // prerequisite_members(reverse_iterate (group_prerequisites (t)))
- //
- inline auto
- reverse_group_prerequisite_members (action a, target& t, bool members = true)
- {
- return prerequisite_members (
- a, butl::reverse_iterate (group_prerequisites (t)), members);
- }
-
- //
- //
- struct target_set
- {
- typedef std::map<target_key, std::unique_ptr<target>> map;
- typedef butl::map_iterator_adapter<map::const_iterator> iterator;
-
- iterator
- find (const target_key& k, tracer& trace) const;
-
- iterator
- find (const target_type& type,
- const dir_path& dir,
- const std::string& name,
- const std::string* ext,
- tracer& trace) const
- {
- return find (target_key {&type, &dir, &name, &ext}, trace);
- }
-
- // As above but ignore the extension and return the target or
- // nullptr instead of the iterator.
- //
- template <typename T>
- T*
- find (const dir_path& dir, const std::string& name) const
- {
- const std::string* e (nullptr);
- auto i (map_.find (target_key {&T::static_type, &dir, &name, &e}));
- return i != map_.end () ? static_cast<T*> (i->second.get ()) : nullptr;
- }
-
- iterator begin () const {return map_.begin ();}
- iterator end () const {return map_.end ();}
-
- std::pair<target&, bool>
- insert (const target_type&,
- dir_path dir,
- std::string name,
- const std::string* ext,
- tracer&);
-
- template <typename T>
- T&
- insert (const dir_path& dir,
- const std::string& name,
- const std::string* ext,
- tracer& t)
- {
- return static_cast<T&> (
- insert (T::static_type, dir, name, ext, t).first);
- }
-
- template <typename T>
- T&
- insert (const dir_path& dir, const std::string& name, tracer& t)
- {
- return static_cast<T&> (
- insert (T::static_type, dir, name, nullptr, t).first);
- }
-
- void
- clear () {map_.clear ();}
-
- private:
- map map_;
- };
-
- extern target_set targets;
-
- // Modification time-based target.
- //
- class mtime_target: public target
- {
- public:
- using target::target;
-
- // Generally, modification time for a target can only be queried
- // after a rule has been matched since that's where the path is
- // normally gets assigned. Normally, however, it would make sense
- // to first execute the rule to get the "updated" timestamp.
- //
- // The rule for groups that utilize the group state is as follows:
- // if it has any members that are mtime_targets, then the group
- // should be mtime_target and the members get the mtime from it.
- //
- timestamp
- mtime () const
- {
- const mtime_target* t (raw_state == target_state::group
- ? static_cast<const mtime_target*> (group)
- : this);
-
- if (t->mtime_ == timestamp_unknown)
- t->mtime_ = t->load_mtime ();
-
- return t->mtime_;
- }
-
- void
- mtime (timestamp mt)
- {
- // While we can cache the mtime at any time, it may be ignored
- // if the target state is group (see the mtime() accessor).
- //
- mtime_ = mt;
- }
-
- protected:
- virtual timestamp
- load_mtime () const = 0;
-
- public:
- static const target_type static_type;
-
- private:
- mutable timestamp mtime_ {timestamp_unknown};
- };
-
- // Filesystem path-based target.
- //
- class path_target: public mtime_target
- {
- public:
- using mtime_target::mtime_target;
-
- typedef build::path path_type;
-
- const path_type&
- path () const {return path_;}
-
- void
- path (path_type p) {assert (path_.empty ()); path_ = std::move (p);}
-
- // Derive a path from target's dir, name, and, if specified, ext.
- // If ext is not specified, then use default_ext and also update
- // the target's extension (this becomes important if later we need
- // to reliably determine whether this file has an extension; think
- // hxx{foo.bar.} and hxx.ext is empty).
- //
- // If name_prefix is not NULL, add it before the name part and after
- // the directory. Similarly, if name_suffix is not NULL, add it after
- // the name part and before the extension.
- //
- // Finally, if the path was already assigned to this target, then
- // this function verifies that the two are the same.
- //
- void
- derive_path (const char* default_ext = nullptr,
- const char* name_prefix = nullptr,
- const char* name_suffix = nullptr);
-
- public:
- static const target_type static_type;
-
- private:
- path_type path_;
- };
-
- // File target.
- //
- class file: public path_target
- {
- public:
- using path_target::path_target;
-
- protected:
- // Note that it is final in order to be consistent with file_rule,
- // search_existing_file().
- //
- virtual timestamp
- load_mtime () const final;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- // Alias target. It represents a list of targets (its prerequisites)
- // as a single "name".
- //
- class alias: public target
- {
- public:
- using target::target;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- // Directory target. Note that this is not a filesystem directory
- // but rather an alias target with the directory name. For actual
- // filesystem directory (creation), see fsdir.
- //
- class dir: public alias
- {
- public:
- using alias::alias;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- // While a filesystem directory is mtime-based, the semantics is
- // not very useful in our case. In particular, if another target
- // depends on fsdir{}, then all that's desired is the creation of
- // the directory if it doesn't already exist. In particular, we
- // don't want to update the target just because some unrelated
- // entry was created in that directory.
- //
- class fsdir: public target
- {
- public:
- using target::target;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class buildfile: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- // Common documentation file targets.
- //
- // @@ Maybe these should be in the built-in doc module?
- //
- class doc: public file
- {
- public:
- using file::file;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- // The problem with man pages is this: different platforms have
- // different sets of sections. What seems to be the "sane" set
- // is 1-9 (Linux and BSDs). SysV (e.g., Solaris) instead maps
- // 8 to 1M (system administration). The section determines two
- // things: the directory where the page is installed (e.g.,
- // /usr/share/man/man1) as well as the extension of the file
- // (e.g., test.1). Note also that there could be sub-sections,
- // e.g., 1p (for POSIX). Such a page would still go into man1
- // but will have the .1p extension (at least that's what happens
- // on Linux). The challenge is to somehow handle this in a
- // portable manner. So here is the plan:
- //
- // First of all, we have the man{} target type which can be used
- // for a custom man page. That is, you can have any extension and
- // install it anywhere you please:
- //
- // man{foo.X}: install = man/manX
- //
- // Then we have man1..9{} target types which model the "sane"
- // section set and that would be automatically installed into
- // correct locations on other platforms. In other words, the
- // idea is that you should be able to have the foo.8 file,
- // write man8{foo} and have it installed as man1m/foo.1m on
- // some SysV host.
- //
- // Re-mapping the installation directory is easy: to help with
- // that we have assigned install.man1..9 directory names. The
- // messy part is to change the extension. It seems the only
- // way to do that would be to have special logic for man pages
- // in the generic install rule. @@ This is still a TODO.
- //
- // Note that handling subsections with man1..9{} is easy, we
- // simply specify the extension explicitly, e.g., man{foo.1p}.
- //
- class man: public doc
- {
- public:
- using doc::doc;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- class man1: public man
- {
- public:
- using man::man;
-
- public:
- static const target_type static_type;
- virtual const target_type& dynamic_type () const {return static_type;}
- };
-
- // Common implementation of the target factory, extension, and
- // search functions.
- //
- template <typename T>
- target*
- target_factory (const target_type&, dir_path d, string n, const string* e)
- {
- return new T (move (d), move (n), e);
- }
-
- // Return fixed target extension.
- //
- template <const char* ext>
- const std::string&
- target_extension_fix (const target_key&, scope&);
-
- // Get the extension from the variable or use the default if none set.
- // Issue diagnostics and fail if the default is NULL.
- //
- template <const char* var, const char* def>
- const std::string&
- target_extension_var (const target_key&, scope&);
-
- // The default behavior, that is, look for an existing target in the
- // prerequisite's directory scope.
- //
- target*
- search_target (const prerequisite_key&);
-
- // First look for an existing target as above. If not found, then look
- // for an existing file in the target-type-specific list of paths.
- //
- target*
- search_file (const prerequisite_key&);
-
-}
-
-#include <build/target.ixx>
-#include <build/target.txx>
-
-#endif // BUILD_TARGET
diff --git a/build/target-key b/build/target-key
deleted file mode 100644
index 526a33a..0000000
--- a/build/target-key
+++ /dev/null
@@ -1,55 +0,0 @@
-// file : build/target-key -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_TARGET_KEY
-#define BUILD_TARGET_KEY
-
-#include <map>
-#include <string>
-#include <ostream>
-#include <functional> // reference_wrapper
-
-#include <butl/utility> // compare_c_string
-
-#include <build/types>
-#include <build/target-type>
-
-namespace build
-{
- // Light-weight (by being shallow-pointing) target key.
- //
- class target_key
- {
- public:
- mutable const target_type* type;
- mutable const dir_path* dir;
- mutable const std::string* name;
- mutable const std::string* const* ext; // Note: only *ext can be NULL.
-
- friend bool
- operator< (const target_key& x, const target_key& y)
- {
- const target_type* xt (x.type);
- const target_type* yt (y.type);
-
- //@@ TODO: use compare() to compare once.
-
- // Unspecified and specified extension are assumed equal. The
- // extension strings are from the pool, so we can just compare
- // pointers.
- //
- return
- (xt < yt) ||
- (xt == yt && *x.name < *y.name) ||
- (xt == yt && *x.name == *y.name && *x.dir < *y.dir) ||
- (xt == yt && *x.name == *y.name && *x.dir == *y.dir &&
- *x.ext != nullptr && *y.ext != nullptr && **x.ext < **y.ext);
- }
- };
-
- std::ostream&
- operator<< (std::ostream&, const target_key&); // Defined in target.cxx
-}
-
-#endif // BUILD_TARGET_KEY
diff --git a/build/target-type b/build/target-type
deleted file mode 100644
index e299fe2..0000000
--- a/build/target-type
+++ /dev/null
@@ -1,97 +0,0 @@
-// file : build/target-type -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_TARGET_TYPE
-#define BUILD_TARGET_TYPE
-
-#include <map>
-#include <string>
-#include <ostream>
-
-#include <build/types>
-
-namespace build
-{
- class scope;
- class target;
- class target_key;
- class prerequisite_key;
-
- // Target type.
- //
- // Note that we assume there is always a single instance of this class
- // for any target type. As a result, we can use address comparison to
- // determine if two target types are the same.
- //
- //
- struct target_type
- {
- const char* name;
- const target_type* base;
- target* (*factory) (const target_type&, dir_path, string, const string*);
- const string& (*extension) (const target_key&, scope&);
- target* (*search) (const prerequisite_key&);
- bool see_through; // A group with the default "see through" semantics.
-
- bool
- is_a (const target_type&) const; // Defined in target.cxx
-
- template <typename T>
- bool
- is_a () const {return is_a (T::static_type);}
- };
-
- inline bool
- operator< (const target_type& x, const target_type& y) {return &x < &y;}
-
- inline bool
- operator== (const target_type& x, const target_type& y) {return &x == &y;}
-
- inline bool
- operator!= (const target_type& x, const target_type& y) {return &x != &y;}
-
- inline std::ostream&
- operator<< (std::ostream& os, const target_type& tt) {return os << tt.name;}
-
- // Target type map.
- //
- struct target_type_ref
- {
- // Like reference_wrapper except it sometimes deletes the target type.
- //
- explicit
- target_type_ref (const target_type& r): p_ (&r), d_ (false) {}
-
- explicit
- target_type_ref (unique_ptr<target_type>&& p)
- : p_ (p.release ()), d_ (true) {}
-
- target_type_ref (target_type_ref&& r)
- : p_ (r.p_), d_ (r.d_) {r.p_ = nullptr;}
-
- ~target_type_ref () {if (p_ != nullptr && d_) delete p_;}
-
- explicit operator const target_type& () const {return *p_;}
- const target_type& get () const {return *p_;}
-
- private:
- const target_type* p_;
- bool d_;
- };
-
- using target_type_map_base = std::map<string, target_type_ref>;
-
- class target_type_map: public target_type_map_base
- {
- public:
- void
- insert (const target_type& tt) {emplace (tt.name, target_type_ref (tt));}
-
- template <typename T>
- void
- insert () {insert (T::static_type);}
- };
-}
-
-#endif // BUILD_TARGET_TYPE
diff --git a/build/target.cxx b/build/target.cxx
deleted file mode 100644
index 41fe54a..0000000
--- a/build/target.cxx
+++ /dev/null
@@ -1,537 +0,0 @@
-// file : build/target.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/target>
-
-#include <cassert>
-
-#include <butl/filesystem>
-
-#include <build/scope>
-#include <build/search>
-#include <build/algorithm>
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- // target_type
- //
- bool target_type::
- is_a (const target_type& tt) const
- {
- for (const target_type* p (this); p != nullptr; p = p->base)
- if (*p == tt)
- return true;
-
- return false;
- }
-
- // target_state
- //
- static const char* target_state_[] = {
- "unknown", "unchanged", "postponed", "changed", "failed", "group"};
-
- ostream&
- operator<< (ostream& os, target_state ts)
- {
- return os << target_state_[static_cast<uint8_t> (ts)];
- }
-
- // recipe
- //
- const recipe empty_recipe;
- const recipe noop_recipe (&noop_action);
- const recipe default_recipe (&default_action);
- const recipe group_recipe (&group_action);
-
- // target
- //
-
- void target::
- recipe (action_type a, recipe_type r)
- {
- assert (a > action || !recipe_);
-
- bool override (a == action && recipe_); // See action::operator<.
-
- // Only noop_recipe can be overridden.
- //
- if (override)
- {
- recipe_function** f (recipe_.target<recipe_function*> ());
- assert (f != nullptr && *f == &noop_action);
- }
-
- action = a;
- recipe_ = std::move (r);
-
- // Also reset the target state. If this is a noop recipe, then
- // mark the target unchanged so that we don't waste time executing
- // the recipe.
- //
- raw_state = target_state::unknown;
-
- if (recipe_function** f = recipe_.target<recipe_function*> ())
- {
- if (*f == &noop_action)
- raw_state = target_state::unchanged;
- }
-
- // This one is tricky: we don't want to reset the dependents count
- // if we are merely overriding with a "stronger" recipe.
- //
- if (!override)
- dependents = 0;
- }
-
- void target::
- reset (action_type)
- {
- prerequisite_targets.clear ();
- }
-
- group_view target::
- group_members (action_type) const
- {
- assert (false); // Not a group or doesn't expose its members.
- return group_view {nullptr, 0};
- }
-
- scope& target::
- base_scope () const
- {
- return scopes.find (dir);
- }
-
- scope& target::
- root_scope () const
- {
- // This is tricky to cache so we do the lookup for now.
- //
- scope* r (scopes.find (dir).root_scope ());
- assert (r != nullptr);
- return *r;
- }
-
- lookup<const value> target::
- operator[] (const variable& var) const
- {
- using result = lookup<const value>;
-
- if (auto p = vars.find (var))
- return result (p, &vars);
-
- if (group != nullptr)
- {
- if (auto p = group->vars.find (var))
- return result (p, &group->vars);
- }
-
- // We cannot simply delegate to scope's lookup() since we also need
- // to check the group.
- //
- for (const scope* s (&base_scope ()); s != nullptr; )
- {
- if (!s->target_vars.empty ())
- {
- if (auto l = s->target_vars.lookup (type (), name, var))
- return l;
-
- if (group != nullptr)
- {
- if (auto l = s->target_vars.lookup (group->type (), group->name, var))
- return l;
- }
- }
-
- if (auto r = s->vars.find (var))
- return result (r, &s->vars);
-
- switch (var.visibility)
- {
- case variable_visibility::scope:
- s = nullptr;
- break;
- case variable_visibility::project:
- s = s->root () ? nullptr : s->parent_scope ();
- break;
- case variable_visibility::normal:
- s = s->parent_scope ();
- break;
- }
- }
-
- return result ();
- }
-
- value& target::
- append (const variable& var)
- {
- auto l (operator[] (var));
-
- if (l && l.belongs (*this)) // Existing variable in this target.
- return const_cast<value&> (*l);
-
- value& r (assign (var));
-
- if (l)
- r = *l; // Copy value from the outer scope.
-
- return r;
- }
-
- ostream&
- operator<< (ostream& os, const target& t)
- {
- return os << target_key {&t.type (), &t.dir, &t.name, &t.ext};
- }
-
- // target_set
- //
- target_set targets;
-
- auto target_set::
- find (const target_key& k, tracer& trace) const -> iterator
- {
- iterator i (map_.find (k));
-
- if (i != end ())
- {
- target& t (**i);
-
- // Update the extension if the existing target has it unspecified.
- //
- const string* ext (*k.ext);
- if (t.ext != ext)
- {
- level5 ([&]{
- diag_record r (trace);
- r << "assuming target " << t << " is the same as the one with ";
- if (ext == nullptr)
- r << "unspecified extension";
- else if (ext->empty ())
- r << "no extension";
- else
- r << "extension " << *ext;
- });
-
- if (ext != nullptr)
- t.ext = ext;
- }
- }
-
- return i;
- }
-
- pair<target&, bool> target_set::
- insert (const target_type& tt,
- dir_path dir,
- string name,
- const string* ext,
- tracer& trace)
- {
- iterator i (find (target_key {&tt, &dir, &name, &ext}, trace));
- bool r (i == end ());
-
- if (r)
- {
- unique_ptr<target> pt (tt.factory (tt, move (dir), move (name), ext));
- i = map_.emplace (
- make_pair (target_key {&tt, &pt->dir, &pt->name, &pt->ext},
- move (pt))).first;
- }
-
- return pair<target&, bool> (**i, r);
- }
-
- ostream&
- operator<< (ostream& os, const target_key& k)
- {
- // If the name is empty, then we want to print the directory
- // inside {}, e.g., dir{bar/}, not bar/dir{}.
- //
- bool n (!k.name->empty ());
- string d (diag_relative (*k.dir, false));
-
- if (n)
- os << d;
-
- os << k.type->name << '{';
-
- if (n)
- {
- os << *k.name;
-
- if (*k.ext != nullptr && !(*k.ext)->empty ())
- os << '.' << **k.ext;
- }
- else
- os << d;
-
- os << '}';
-
- return os;
- }
-
- // path_target
- //
- void path_target::
- derive_path (const char* de, const char* np, const char* ns)
- {
- string n;
-
- if (np != nullptr)
- n += np;
-
- n += name;
-
- if (ns != nullptr)
- n += ns;
-
- // Update the extension.
- //
- // See also search_existing_file() if updating anything here.
- //
- if (ext == nullptr)
- {
- // If provided by the caller, then use that.
- //
- if (de != nullptr)
- ext = &extension_pool.find (de);
- //
- // Otherwis see if the target type has function that will
- // give us the default extension.
- //
- else if (auto f = type ().extension)
- ext = &f (key (), base_scope ()); // Already from the pool.
- else
- fail << "no default extension for target " << *this;
- }
-
- // Add the extension.
- //
- if (!ext->empty ())
- {
- n += '.';
- n += *ext;
- }
-
- path_type p (dir / path_type (move (n)));
- const path_type& ep (path ());
-
- if (ep.empty ())
- path (p);
- else if (p != ep)
- fail << "path mismatch for target " << *this <<
- info << "assigned '" << ep << "'" <<
- info << "derived '" << p << "'";
- }
-
- // file_target
- //
- timestamp file::
- load_mtime () const
- {
- const path_type& f (path ());
- assert (!f.empty ());
- return file_mtime (f);
- }
-
- // Search functions.
- //
-
- target*
- search_target (const prerequisite_key& pk)
- {
- // The default behavior is to look for an existing target in the
- // prerequisite's directory scope.
- //
- return search_existing_target (pk);
- }
-
- target*
- search_file (const prerequisite_key& pk)
- {
- // First see if there is an existing target.
- //
- if (target* t = search_existing_target (pk))
- return t;
-
- // Then look for an existing file in this target-type-specific
- // list of paths (@@ TODO: comes from the variable).
- //
- if (pk.tk.dir->relative ())
- {
- dir_paths sp;
- sp.push_back (pk.scope->src_path ()); // src_base
- return search_existing_file (pk, sp);
- }
- else
- return nullptr;
- }
-
- static target*
- search_alias (const prerequisite_key& pk)
- {
- // For an alias we don't want to silently create a target since it
- // will do nothing and it most likely not what the user intended.
- //
- target* t (search_existing_target (pk));
-
- if (t == nullptr)
- fail << "no explicit target for prerequisite " << pk;
-
- return t;
- }
-
- // type info
- //
-
- const target_type target::static_type
- {
- "target",
- nullptr,
- nullptr,
- nullptr,
- &search_target,
- false
- };
-
- const target_type mtime_target::static_type
- {
- "mtime_target",
- &target::static_type,
- nullptr,
- nullptr,
- &search_target,
- false
- };
-
- const target_type path_target::static_type
- {
- "path_target",
- &mtime_target::static_type,
- nullptr,
- nullptr,
- &search_target,
- false
- };
-
- template <typename T>
- static target*
- file_factory (const target_type&, dir_path d, string n, const string* e)
- {
- // The file target type doesn't imply any extension. So if one
- // wasn't specified, set it to empty rather than unspecified.
- // In other words, we always treat file{foo} as file{foo.}.
- //
- return new T (move (d),
- move (n),
- (e != nullptr ? e : &extension_pool.find ("")));
- }
-
- constexpr const char file_ext_var[] = "extension";
- constexpr const char file_ext_def[] = "";
-
- const target_type file::static_type
- {
- "file",
- &path_target::static_type,
- &file_factory<file>,
- &target_extension_var<file_ext_var, file_ext_def>,
- &search_file,
- false
- };
-
- const target_type alias::static_type
- {
- "alias",
- &target::static_type,
- &target_factory<alias>,
- nullptr, // Should never need.
- &search_alias,
- false
- };
-
- const target_type dir::static_type
- {
- "dir",
- &alias::static_type,
- &target_factory<dir>,
- nullptr, // Should never need.
- &search_alias,
- false
- };
-
- const target_type fsdir::static_type
- {
- "fsdir",
- &target::static_type,
- &target_factory<fsdir>,
- nullptr, // Should never need.
- &search_target,
- false
- };
-
- static const std::string&
- buildfile_target_extension (const target_key& tk, scope&)
- {
- // If the name is special 'buildfile', then there is no extension,
- // otherwise it is .build.
- //
- return extension_pool.find (*tk.name == "buildfile" ? "" : "build");
- }
-
- const target_type buildfile::static_type
- {
- "buildfile",
- &file::static_type,
- &file_factory<buildfile>,
- &buildfile_target_extension,
- &search_file,
- false
- };
-
- const target_type doc::static_type
- {
- "doc",
- &file::static_type,
- &file_factory<doc>,
- &target_extension_var<file_ext_var, file_ext_def>, // Same as file.
- &search_file,
- false
- };
-
- static target*
- man_factory (const target_type&, dir_path d, string n, const string* e)
- {
- if (e == nullptr)
- fail << "man target '" << n << "' must include extension (man section)";
-
- return new man (move (d), move (n), e);
- }
-
- const target_type man::static_type
- {
- "man",
- &doc::static_type,
- &man_factory,
- nullptr, // Should be specified explicitly.
- &search_file,
- false
- };
-
- constexpr const char man1_ext[] = "1";
- const target_type man1::static_type
- {
- "man1",
- &man::static_type,
- &file_factory<man1>,
- &target_extension_fix<man1_ext>,
- &search_file,
- false
- };
-}
diff --git a/build/target.ixx b/build/target.ixx
deleted file mode 100644
index 6bcd265..0000000
--- a/build/target.ixx
+++ /dev/null
@@ -1,85 +0,0 @@
-// file : build/target.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace build
-{
- // prerequisite_ref
- //
- inline bool prerequisite_ref::
- belongs (const target& t) const
- {
- const auto& p (t.prerequisites);
- return !(p.empty () || this < &p.front () || this > &p.back ());
- }
-
- // prerequisite_member
- //
- inline prerequisite& prerequisite_member::
- as_prerequisite (tracer& trace) const
- {
- if (target == nullptr)
- return prerequisite;
-
- // The use of the group's prerequisite scope is debatable.
- //
- scope& s (prerequisite.get ().scope);
- return s.prerequisites.insert (nullptr, key ().tk, s, trace).first;
- }
-
- // prerequisite_members
- //
- group_view
- resolve_group_members (action, target&); // <build/algorithm>
-
- template <typename T>
- inline auto prerequisite_members_range<T>::iterator::
- operator++ () -> iterator&
- {
- if (g_.count != 0)
- {
- if (++j_ <= g_.count)
- return *this;
-
- // Switch back to prerequisite iteration mode.
- //
- g_.count = 0;
- }
-
- ++i_;
-
- // Switch to member iteration mode.
- //
- if (r_->members_ && i_ != r_->e_ && i_->get ().type.see_through)
- {
- bool r (switch_members ());
- assert (r); // Group could not be resolved.
- }
-
- return *this;
- }
-
- template <typename T>
- inline bool prerequisite_members_range<T>::iterator::
- switch_members ()
- {
- do
- {
- g_ = resolve_group_members (r_->a_, search (*i_));
-
- // If members are not know, iterate over the group as itself.
- //
- if (g_.members == nullptr)
- {
- g_.count = 0;
- return false;
- }
- }
- while (g_.count == 0 && // Skip empty groups.
- ++i_ != r_->e_ &&
- i_->get ().type.see_through);
-
- j_ = 1; // Start from the first group member.
- return true;
- }
-}
diff --git a/build/target.txx b/build/target.txx
deleted file mode 100644
index 192cfbc..0000000
--- a/build/target.txx
+++ /dev/null
@@ -1,58 +0,0 @@
-// file : build/target.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/scope>
-#include <build/context> // extension_pool
-#include <build/diagnostics>
-#include <build/prerequisite>
-
-namespace build
-{
- template <const char* ext>
- const string&
- target_extension_fix (const target_key&, scope&)
- {
- return extension_pool.find (ext);
- }
-
- template <const char* var, const char* def>
- const string&
- target_extension_var (const target_key& tk, scope& s)
- {
- // Include target type/pattern-specific variables.
- //
- if (auto l = s.lookup (tk, var))
- {
- // Help the user here and strip leading '.' from the extension.
- //
- const string& e (as<string> (*l));
- return extension_pool.find (
- !e.empty () && e.front () == '.' ? string (e, 1) : e);
- }
-
- if (def != nullptr)
- return extension_pool.find (def);
-
- {
- diag_record dr;
- dr << error << "no default extension in variable '" << var << "'"
- << info << "required to derive file name for ";
-
- // This is a bit hacky: we may be dealing with a target (see
- // file::derive_path()) or prerequsite (see search_existing_file()).
- // So we are going to check if dir is absolute. If it is, then
- // we assume this is a target, otherwise -- prerequsite.
- //
- if (tk.dir->absolute ())
- dr << "target " << tk;
- else
- dr << "prerequisite " << prerequisite_key {nullptr, tk, &s};
-
- dr << info << "perhaps you forgot to add "
- << tk.type->name << "{*}: " << var << " = ...";
- }
-
- throw failed ();
- }
-}
diff --git a/build/test/module b/build/test/module
deleted file mode 100644
index 83c6d60..0000000
--- a/build/test/module
+++ /dev/null
@@ -1,24 +0,0 @@
-// file : build/test/module -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_TEST_MODULE
-#define BUILD_TEST_MODULE
-
-#include <build/types>
-#include <build/module>
-
-namespace build
-{
- namespace test
- {
- extern "C" void
- test_boot (scope&, const location&, unique_ptr<module>&);
-
- extern "C" bool
- test_init (
- scope&, scope&, const location&, unique_ptr<module>&, bool, bool);
- }
-}
-
-#endif // BUILD_TEST_MODULE
diff --git a/build/test/module.cxx b/build/test/module.cxx
deleted file mode 100644
index 9372103..0000000
--- a/build/test/module.cxx
+++ /dev/null
@@ -1,88 +0,0 @@
-// file : build/test/module.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/test/module>
-
-#include <build/scope>
-#include <build/target>
-#include <build/rule>
-#include <build/diagnostics>
-
-#include <build/test/operation>
-#include <build/test/rule>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace test
- {
- static rule rule_;
-
- extern "C" void
- test_boot (scope& root, const location&, unique_ptr<module>&)
- {
- tracer trace ("test::boot");
-
- level5 ([&]{trace << "for " << root.out_path ();});
-
- // Register the test operation.
- //
- root.operations.insert (test_id, test);
- }
-
- extern "C" bool
- test_init (scope& root,
- scope&,
- const location& l,
- unique_ptr<module>&,
- bool first,
- bool)
- {
- tracer trace ("test::init");
-
- if (!first)
- {
- warn (l) << "multiple test module initializations";
- return true;
- }
-
- const dir_path& out_root (root.out_path ());
- level5 ([&]{trace << "for " << out_root;});
-
- // Enter module variables.
- //
- {
- auto& v (var_pool);
-
- v.find ("test", bool_type);
- v.find ("test.input", name_type);
- v.find ("test.output", name_type);
- v.find ("test.roundtrip", name_type);
- v.find ("test.options", strings_type);
- v.find ("test.arguments", strings_type);
- }
-
- // Register rules.
- //
- {
- auto& r (root.rules);
-
- // Register our test running rule.
- //
- r.insert<target> (perform_test_id, "test", rule_);
-
- // Register our rule for the dist meta-operation. We need
- // to do this because we have "ad-hoc prerequisites" (test
- // input/output files) that need to be entered into the
- // target list.
- //
- r.insert<target> (dist_id, test_id, "test", rule_);
- }
-
- return true;
- }
- }
-}
diff --git a/build/test/operation b/build/test/operation
deleted file mode 100644
index 1942936..0000000
--- a/build/test/operation
+++ /dev/null
@@ -1,18 +0,0 @@
-// file : build/test/operation -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_TEST_OPERATION
-#define BUILD_TEST_OPERATION
-
-#include <build/operation>
-
-namespace build
-{
- namespace test
- {
- extern operation_info test;
- }
-}
-
-#endif // BUILD_TEST_OPERATION
diff --git a/build/test/operation.cxx b/build/test/operation.cxx
deleted file mode 100644
index 40c5b86..0000000
--- a/build/test/operation.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// file : build/test/operation.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/test/operation>
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace test
- {
- static operation_id
- test_pre (meta_operation_id mo)
- {
- // Run update as a pre-operation, unless we are disfiguring.
- //
- return mo != disfigure_id ? update_id : 0;
- }
-
- operation_info test {
- "test",
- "test",
- "testing",
- "has nothing to test", // We cannot "be tested".
- execution_mode::first,
- &test_pre,
- nullptr
- };
- }
-}
diff --git a/build/test/rule b/build/test/rule
deleted file mode 100644
index 17f32ce..0000000
--- a/build/test/rule
+++ /dev/null
@@ -1,30 +0,0 @@
-// file : build/test/rule -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_TEST_RULE
-#define BUILD_TEST_RULE
-
-#include <build/rule>
-#include <build/operation>
-
-namespace build
-{
- namespace test
- {
- class rule: public build::rule
- {
- public:
- virtual match_result
- match (action, target&, const std::string&) const;
-
- virtual recipe
- apply (action, target&, const match_result&) const;
-
- static target_state
- perform_test (action, target&);
- };
- }
-}
-
-#endif // BUILD_TEST_RULE
diff --git a/build/test/rule.cxx b/build/test/rule.cxx
deleted file mode 100644
index 3a4c91d..0000000
--- a/build/test/rule.cxx
+++ /dev/null
@@ -1,439 +0,0 @@
-// file : build/test/rule.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/test/rule>
-
-#include <butl/process>
-
-#include <build/scope>
-#include <build/target>
-#include <build/algorithm>
-#include <build/diagnostics>
-
-#include <build/config/utility> // add_options()
-
-using namespace std;
-using namespace butl;
-
-namespace build
-{
- namespace test
- {
- match_result rule::
- match (action a, target& t, const std::string&) const
- {
- // First determine if this is a test. This is controlled by
- // the test target variable and text.<tt> scope variables.
- // Also, it feels redundant to specify, say, "test = true"
- // and "test.output = test.out" -- the latter already says
- // this is a test. So take care of that as well.
- //
- bool r (false);
- lookup<const value> l;
-
- // @@ This logic doesn't take into account target type/pattern-
- // specific variables.
- //
- // @@ Perhaps a find_any(<list-of-vars>)?
- //
- for (auto p (t.vars.find_namespace ("test"));
- p.first != p.second;
- ++p.first)
- {
- const variable& var (p.first->first);
- const value& val (p.first->second);
-
- // If we have test, then always use that.
- //
- if (var.name == "test")
- {
- l = lookup<const value> (val, t);
- break;
- }
-
- // Otherwise check for variables that would indicate this
- // is a test.
- //
- if (var.name == "test.input" ||
- var.name == "test.output" ||
- var.name == "test.roundtrip" ||
- var.name == "test.options" ||
- var.name == "test.arguments")
- {
- r = true;
- break;
- }
- }
-
- if (!r)
- {
- // See if there is a scope variable.
- //
- if (!l.defined ())
- l = t.base_scope ()[
- var_pool.find (string("test.") + t.type ().name, bool_type)];
-
- r = l && as<bool> (*l);
- }
-
- // If this is the update pre-operation, then all we really need to
- // do is say we are not a match and the standard matching machinery
- // will (hopefully) find the rule to update this target.
- //
- // There is one thing that compilates this simple approach: test
- // input/output. While normally they will be existing (in src_base)
- // files, they could also be auto-generated. In fact, they could
- // only be needed for testing, which means the normall update won't
- // even know about them (nor clean, for that matter; this is why we
- // need cleantest).
- //
- // To make generated input/output work we will have to cause their
- // update ourselves. I other words, we may have to do some actual
- // work for (update, test), and not simply "guide" (update, 0) as
- // to which targets need updating. For how exactly we are going to
- // do it, see apply() below.
- //
- match_result mr (t, r);
-
- // If this is the update pre-operation, change the recipe action
- // to (update, 0) (i.e., "unconditional update").
- //
- if (r && a.operation () == update_id)
- mr.recipe_action = action (a.meta_operation (), update_id);
-
- return mr;
- }
-
- recipe rule::
- apply (action a, target& t, const match_result& mr) const
- {
- tracer trace ("test::rule::apply");
-
- if (!mr.bvalue) // Not a test.
- return noop_recipe;
-
- // Ok, if we are here, then this means:
- //
- // 1. This target is a test.
- // 2. The action is either
- // a. (perform, test, 0) or
- // b. (*, update, install)
- //
- // In both cases, the next step is to see if we have test.{input,
- // output,roundtrip}.
- //
-
- // First check the target-specific vars since they override any
- // scope ones.
- //
- auto il (t.vars["test.input"]);
- auto ol (t.vars["test.output"]);
- auto rl (t.vars["test.roundtrip"]);
- auto al (t.vars["test.arguments"]); // Should be input or arguments.
-
- if (al)
- {
- if (il)
- fail << "both test.input and test.arguments specified for "
- << "target " << t;
-
- if (rl)
- fail << "both test.roundtrip and test.arguments specified for "
- << "target " << t;
- }
-
- scope& bs (t.base_scope ());
-
- if (!il && !ol && !rl)
- {
- string n ("test.");
- n += t.type ().name;
-
- const variable& in (var_pool.find (n + ".input", name_type));
- const variable& on (var_pool.find (n + ".output", name_type));
- const variable& rn (var_pool.find (n + ".roundtrip", name_type));
-
- // We should only keep value(s) that were specified together
- // in the innermost scope.
- //
- // @@ Shouldn't we stop at project root?
- //
- for (scope* s (&bs); s != nullptr; s = s->parent_scope ())
- {
- ol = s->vars[on];
-
- if (!al) // Not overriden at target level by test.arguments?
- {
- il = s->vars[in];
- rl = s->vars[rn];
- }
-
- if (il || ol || rl)
- break;
- }
- }
-
- const name* in;
- const name* on;
-
- // Reduce the roundtrip case to input/output.
- //
- if (rl)
- {
- if (il || ol)
- fail << "both test.roundtrip and test.input/output specified "
- << "for target " << t;
-
- in = on = &as<name> (*rl);
- }
- else
- {
- in = il ? &as<name> (*il) : nullptr;
- on = ol ? &as<name> (*ol) : nullptr;
- }
-
- // Resolve them to targets, which normally would be existing files
- // but could also be targets that need updating.
- //
- target* it (in != nullptr ? &search (*in, bs) : nullptr);
- target* ot (on != nullptr ? in == on ? it : &search (*on, bs) : nullptr);
-
- if (a.operation () == update_id)
- {
- // First see if input/output are existing, up-to-date files. This
- // is a common case optimization.
- //
- if (it != nullptr)
- {
- build::match (a, *it);
-
- if (it->state () == target_state::unchanged)
- {
- unmatch (a, *it);
- it = nullptr;
- }
- }
-
- if (ot != nullptr)
- {
- if (in != on)
- {
- build::match (a, *ot);
-
- if (ot->state () == target_state::unchanged)
- {
- unmatch (a, *ot);
- ot = nullptr;
- }
- }
- else
- ot = it;
- }
-
-
- // Find the "real" update rule, that is, the rule that would
- // have been found if we signalled that we do not match from
- // match() above.
- //
- recipe d (match_delegate (a, t).first);
-
- // If we have no input/output that needs updating, then simply
- // redirect to it.
- //
- if (it == nullptr && ot == nullptr)
- return d;
-
- // Ok, time to handle the worst case scenario: we need to
- // cause update of input/output targets and also delegate
- // to the real update.
- //
- return [it, ot, dr = move (d)] (action a, target& t) -> target_state
- {
- // Do the general update first.
- //
- target_state r (execute_delegate (dr, a, t));
-
- if (it != nullptr)
- r |= execute (a, *it);
-
- if (ot != nullptr)
- r |= execute (a, *ot);
-
- return r;
- };
- }
- else
- {
- // Cache the targets in our prerequsite targets lists where they
- // can be found by perform_test(). If we have either or both,
- // then the first entry is input and the second -- output (either
- // can be NULL).
- //
- if (it != nullptr || ot != nullptr)
- {
- auto& pts (t.prerequisite_targets);
- pts.resize (2, nullptr);
- pts[0] = it;
- pts[1] = ot;
- }
-
- return &perform_test;
- }
- }
-
- static void
- add_arguments (cstrings& args, const target& t, const char* n)
- {
- string var ("test.");
- var += n;
-
- auto l (t.vars[var]);
-
- if (!l)
- {
- var.resize (5);
- var += t.type ().name;
- var += '.';
- var += n;
- l = t.base_scope ()[var_pool.find (var, strings_type)];
- }
-
- if (l)
- config::append_options (args, as<strings> (*l));
- }
-
- // The format of args shall be:
- //
- // name1 arg arg ... nullptr
- // name2 arg arg ... nullptr
- // ...
- // nameN arg arg ... nullptr nullptr
- //
- static bool
- run_test (target& t,
- diag_record& dr,
- char const** args,
- process* prev = nullptr)
- {
- // Find the next process, if any.
- //
- char const** next (args);
- for (next++; *next != nullptr; next++) ;
- next++;
-
- // Redirect stdout to a pipe unless we are last, in which
- // case redirect it to stderr.
- //
- int out (*next == nullptr ? 2 : -1);
- bool pr, wr;
-
- try
- {
- if (prev == nullptr)
- {
- // First process.
- //
- process p (args, 0, out);
- pr = *next == nullptr || run_test (t, dr, next, &p);
- wr = p.wait ();
- }
- else
- {
- // Next process.
- //
- process p (args, *prev, out);
- pr = *next == nullptr || run_test (t, dr, next, &p);
- wr = p.wait ();
- }
- }
- catch (const process_error& e)
- {
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- throw failed ();
- }
-
- if (!wr)
- {
- if (pr) // First failure?
- dr << fail << "test " << t << " failed"; // Multi test: test 1.
-
- dr << error << "non-zero exit status: ";
- print_process (dr, args);
- }
-
- return pr && wr;
- }
-
- target_state rule::
- perform_test (action, target& t)
- {
- // @@ Would be nice to print what signal/core was dumped.
- //
- // @@ Doesn't have to be a file target if we have test.cmd.
- //
-
- file& ft (static_cast<file&> (t));
- assert (!ft.path ().empty ()); // Should have been assigned by update.
-
- cstrings args {ft.path ().string ().c_str ()};
-
- // Do we have options?
- //
- add_arguments (args, t, "options");
-
- // Do we have input?
- //
- auto& pts (t.prerequisite_targets);
- if (pts.size () != 0 && pts[0] != nullptr)
- {
- file& it (static_cast<file&> (*pts[0]));
- assert (!it.path ().empty ()); // Should have been assigned by update.
- args.push_back (it.path ().string ().c_str ());
- }
- // Maybe arguments then?
- //
- else
- add_arguments (args, t, "arguments");
-
- args.push_back (nullptr);
-
- // Do we have output?
- //
- if (pts.size () != 0 && pts[1] != nullptr)
- {
- file& ot (static_cast<file&> (*pts[1]));
- assert (!ot.path ().empty ()); // Should have been assigned by update.
-
- args.push_back ("diff");
- args.push_back ("-u");
- args.push_back (ot.path ().string ().c_str ());
- args.push_back ("-");
- args.push_back (nullptr);
- }
-
- args.push_back (nullptr); // Second.
-
- if (verb >= 2)
- print_process (args);
- else if (verb)
- text << "test " << t;
-
- {
- diag_record dr;
-
- if (!run_test (t, dr, args.data ()))
- {
- dr << info << "test command line: ";
- print_process (dr, args);
- }
- }
-
- return target_state::changed;
- }
- }
-}
diff --git a/build/token b/build/token
deleted file mode 100644
index d98b2e1..0000000
--- a/build/token
+++ /dev/null
@@ -1,73 +0,0 @@
-// file : build/token -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_TOKEN
-#define BUILD_TOKEN
-
-#include <string>
-#include <iosfwd>
-#include <cstddef> // size_t
-#include <cstdint> // uint64_t
-#include <utility> // move
-
-namespace build
-{
- enum class token_type
- {
- eos,
- name,
- newline,
- pair_separator,
- colon,
- lcbrace,
- rcbrace,
- equal,
- equal_plus,
- plus_equal,
- dollar,
- lparen,
- rparen
- };
-
- class token
- {
- public:
- token_type type;
- bool separated; // Whitespace-separated from the previous token.
- bool quoted; // Name (or some part of it) was quoted.
-
- char pair; // Only valid for pair_separator.
- std::string value; // Only valid for name.
-
- std::uint64_t line;
- std::uint64_t column;
-
- public:
- token (token_type t, bool s, std::uint64_t l, std::uint64_t c)
- : type (t), separated (s), quoted (false), line (l), column (c) {}
-
- token (char p, bool s, std::uint64_t l, std::uint64_t c)
- : type (token_type::pair_separator),
- separated (s),
- quoted (false),
- pair (p),
- line (l),
- column (c) {}
-
- token (std::string n, bool s, bool q, std::uint64_t l, std::uint64_t c)
- : type (token_type::name),
- separated (s),
- quoted (q),
- value (std::move (n)),
- line (l),
- column (c) {}
- };
-
- // Output the token value in a format suitable for diagnostics.
- //
- std::ostream&
- operator<< (std::ostream&, const token&);
-}
-
-#endif // BUILD_TOKEN
diff --git a/build/token.cxx b/build/token.cxx
deleted file mode 100644
index cf47fd4..0000000
--- a/build/token.cxx
+++ /dev/null
@@ -1,35 +0,0 @@
-// file : build/token.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/token>
-
-#include <ostream>
-
-using namespace std;
-
-namespace build
-{
- ostream&
- operator<< (ostream& os, const token& t)
- {
- switch (t.type)
- {
- case token_type::eos: os << "<end-of-file>"; break;
- case token_type::newline: os << "<newline>"; break;
- case token_type::pair_separator: os << "<pair separator>"; break;
- case token_type::colon: os << ":"; break;
- case token_type::lcbrace: os << "{"; break;
- case token_type::rcbrace: os << "}"; break;
- case token_type::equal: os << "="; break;
- case token_type::equal_plus: os << "=+"; break;
- case token_type::plus_equal: os << "+="; break;
- case token_type::dollar: os << "$"; break;
- case token_type::lparen: os << "("; break;
- case token_type::rparen: os << ")"; break;
- case token_type::name: os << t.value; break;
- }
-
- return os;
- }
-}
diff --git a/build/types b/build/types
deleted file mode 100644
index 3028fc2..0000000
--- a/build/types
+++ /dev/null
@@ -1,55 +0,0 @@
-// file : build/types -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_TYPES
-#define BUILD_TYPES
-
-#include <vector>
-#include <string>
-#include <utility> // pair
-#include <memory> // unique_ptr, shared_ptr
-#include <cstddef> // size_t
-#include <functional> // reference_wrapper
-
-#include <butl/path>
-#include <butl/timestamp>
-
-#include <build/name>
-
-namespace build
-{
- // Commonly-used types.
- //
- using std::pair;
- using std::size_t;
- using std::string;
- using std::unique_ptr;
- using std::shared_ptr;
- using std::reference_wrapper;
- using std::vector;
-
- using strings = vector<string>;
- using cstrings = vector<const char*>;
-
- // <butl/path>
- //
- using butl::path;
- using butl::dir_path;
- using butl::basic_path;
- using butl::invalid_path;
-
- using paths = std::vector<path>;
- using dir_paths = std::vector<dir_path>;
-
- // <butl/timestamp>
- //
- using butl::system_clock;
- using butl::timestamp;
- using butl::duration;
- using butl::timestamp_unknown;
- using butl::timestamp_nonexistent;
- using butl::operator<<;
-}
-
-#endif // BUILD_TYPES
diff --git a/build/utility b/build/utility
deleted file mode 100644
index bcb6b58..0000000
--- a/build/utility
+++ /dev/null
@@ -1,85 +0,0 @@
-// file : build/utility -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_UTILITY
-#define BUILD_UTILITY
-
-#include <utility> // move(), make_pair()
-#include <cassert> // assert()
-#include <exception> // uncaught_exception()
-#include <unordered_set>
-
-#include <build/types>
-
-namespace build
-{
- using std::move;
- using std::make_pair;
-
- // Empty string and path.
- //
- extern const std::string empty_string;
- extern const path empty_path;
- extern const dir_path empty_dir_path;
-
- // Parse version string in the X.Y.Z[-{a|b}N] to a version integer in the
- // AABBCCDD form as describe in <build/version>. Throw invalid_argument
- // if the passed string is not a valid version.
- //
- unsigned int
- to_version (const string&);
-
- // Call a function if there is an exception.
- //
-
- // Means we are in the body of a destructor that is being called
- // as part of the exception stack unwindining. Used to compensate
- // for the deficiencies of uncaught_exception() until C++17
- // uncaught_exceptions() becomes available.
- //
- // @@ MT: will have to be TLS.
- //
- extern bool exception_unwinding_dtor;
-
- template <typename F>
- struct exception_guard;
-
- template <typename F>
- inline exception_guard<F>
- make_exception_guard (F f)
- {
- return exception_guard<F> (move (f));
- }
-
- template <typename F>
- struct exception_guard
- {
- exception_guard (F f): f_ (move (f)) {}
- ~exception_guard ()
- {
- if (std::uncaught_exception ())
- {
- exception_unwinding_dtor = true;
- f_ ();
- exception_unwinding_dtor = false;
- }
- }
-
- private:
- F f_;
- };
-
- // Pools (@@ perhaps move into a separate header).
- //
- struct string_pool: std::unordered_set<std::string>
- {
- const std::string&
- find (const char* s) {return *emplace (s).first;}
-
- const std::string&
- find (const std::string& s) {return *emplace (s).first;}
- };
-}
-
-#endif // BUILD_UTILITY
diff --git a/build/utility.cxx b/build/utility.cxx
deleted file mode 100644
index 2361c8b..0000000
--- a/build/utility.cxx
+++ /dev/null
@@ -1,94 +0,0 @@
-// file : build/utility.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/utility>
-
-#include <cstdlib> // strtol()
-
-using namespace std;
-
-namespace build
-{
- const string empty_string;
- const path empty_path;
- const dir_path empty_dir_path;
-
- unsigned int
- to_version (const string& s)
- {
- // See tests/version.
- //
-
- auto parse = [&s] (size_t& p, const char* m, long min = 0, long max = 99)
- -> unsigned int
- {
- if (s[p] == '-' || s[p] == '+') // stoi() allows these.
- throw invalid_argument (m);
-
- const char* b (s.c_str () + p);
- char* e;
- long r (strtol (b, &e, 10));
-
- if (b == e || r < min || r > max)
- throw invalid_argument (m);
-
- p = e - s.c_str ();
- return static_cast<unsigned int> (r);
- };
-
- auto bail = [] (const char* m) {throw invalid_argument (m);};
-
- unsigned int ma, mi, bf, ab (0);
-
- size_t p (0), n (s.size ());
- ma = parse (p, "invalid major version");
-
- if (p >= n || s[p] != '.')
- bail ("'.' expected after major version");
-
- mi = parse (++p, "invalid minor version");
-
- if (p >= n || s[p] != '.')
- bail ("'.' expected after minor version");
-
- bf = parse (++p, "invalid bugfix version");
-
- if (p < n)
- {
- if (s[p] != '-')
- bail ("'-' expected after bugfix version");
-
- char k (s[++p]);
-
- if (k != 'a' && k != 'b')
- bail ("'a' or 'b' expected in release component");
-
- ab = parse (++p, "invalid release component", 1, 49);
-
- if (p != n)
- bail ("junk after release component");
-
- if (k == 'b')
- ab += 50;
- }
-
- // AABBCCDD
- unsigned int r (ma * 1000000U +
- mi * 10000U +
- bf * 100U);
-
- if (ab != 0)
- {
- if (r == 0)
- bail ("0.0.0 version with release component");
-
- r -= 100;
- r += ab;
- }
-
- return r;
- }
-
- bool exception_unwinding_dtor = false;
-}
diff --git a/build/variable b/build/variable
deleted file mode 100644
index 799a35c..0000000
--- a/build/variable
+++ /dev/null
@@ -1,831 +0,0 @@
-// file : build/variable -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD_VARIABLE
-#define BUILD_VARIABLE
-
-#include <map>
-#include <vector>
-#include <cstddef> // nullptr_t
-#include <utility> // pair, make_pair()
-#include <iterator>
-#include <functional> // hash
-#include <type_traits> // conditional, is_reference, remove_reference, etc.
-#include <unordered_set>
-
-#include <butl/prefix-map>
-
-#include <build/types>
-#include <build/utility>
-
-#include <build/target-type>
-
-namespace build
-{
- struct variable;
-
- // If assign is NULL, then the value is assigned as is. If append
- // is NULL, then the names are appended to the end of the value
- // and assign is called, if not NULL. Variable is provided primarily
- // for diagnostics. Return true if the resulting value is not empty.
- //
- struct value_type
- {
- const char* name;
- bool (*const assign) (names&, const variable&);
- bool (*const append) (names&, names, const variable&);
- };
-
- enum class variable_visibility
- {
- scope, // This scope (no outer scopes).
- project, // This project (no outer projects).
- normal // All outer scopes.
- };
-
- // variable
- //
- // The two variables are considered the same if they have the same name.
- //
- struct variable
- {
- std::string name;
- const value_type* type; // If NULL, then not (yet) typed.
- variable_visibility visibility;
- char pairs; // Pair symbold or '\0' if not used.
- };
-
- inline bool
- operator== (const variable& x, const variable& y) {return x.name == y.name;}
-
- typedef std::reference_wrapper<const variable> variable_cref;
-
- // value
- //
- class value
- {
- public:
- // By default we create NULL value.
- //
- explicit value (const value_type* t = nullptr)
- : type (t), state_ (state_type::null) {}
-
- value (value&&) = default;
-
- value&
- operator= (std::nullptr_t)
- {
- data_.clear ();
- state_ = state_type::null;
- return *this;
- }
-
- value&
- operator= (value&&);
-
- value&
- operator= (const value& v)
- {
- if (&v != this)
- *this = value (v);
- return *this;
- }
-
- value&
- operator= (reference_wrapper<value> v) {return *this = v.get ();}
-
- value&
- operator= (reference_wrapper<const value> v) {return *this = v.get ();}
-
- value&
- append (value, const variable&); // Aka operator+=().
-
- value&
- prepend (value, const variable&); // Aka operator=+().
-
- // Forwarded to the representation type's assign()/append() (see below).
- //
- template <typename T> value& operator= (T);
- value& operator= (const char* v) {return *this = std::string (v);}
-
- template <typename T> value& operator+= (T);
- value& operator+= (const char* v) {return *this += std::string (v);}
-
- private:
- explicit value (const value&) = default;
-
- public:
- const value_type* type; // NULL means this value is not (yet) typed.
-
- bool null () const {return state_ == state_type::null;}
- bool empty () const {return state_ == state_type::empty;}
-
- explicit operator bool () const {return !null ();}
- bool operator== (std::nullptr_t) const {return null ();}
- bool operator!= (std::nullptr_t) const {return !null ();}
-
- // Raw data read interface.
- //
- using const_iterator = names::const_iterator;
-
- const_iterator begin () const {return data_.begin ();}
- const_iterator end () const {return data_.end ();}
-
- // Raw data write interface. Note that it triggers callbacks for
- // typed values. Variable is passed for diagnostics.
- //
- void
- assign (names, const variable&);
-
- void
- append (names, const variable&);
-
- void
- prepend (names, const variable&);
-
- public:
- // Don't use directly except in the implementation of representation
- // types, in which case take care to update state.
- //
- enum class state_type {null, empty, filled} state_;
- names data_;
- };
-
- //@@ Right now we assume that for each value type each value has a
- // unique representation. This is currently not the case for map.
- //
- inline bool
- operator== (const value& x, const value& y)
- {
- return x.state_ == y.state_ && x.data_ == y.data_;
- }
-
- inline bool
- operator!= (const value& x, const value& y) {return !(x == y);}
-
- // lookup
- //
- // A variable can be undefined, NULL, or contain a (potentially
- // empty) value.
- //
- struct variable_map;
-
- template <typename V>
- struct lookup
- {
- V* value;
- const variable_map* vars;
-
- bool
- defined () const {return value != nullptr;}
-
- // Note: returns true if defined and not NULL.
- //
- explicit operator bool () const {return defined () && !value->null ();}
-
- V& operator* () const {return *value;}
- V* operator-> () const {return value;}
-
- // Return true if this value belongs to the specified scope or target.
- // Note that it can also be a target type/pattern-specific value.
- //
- template <typename T>
- bool
- belongs (const T& x) const {return vars == &x.vars;}
-
- lookup (): value (nullptr), vars (nullptr) {}
- lookup (V* v, const variable_map* vs)
- : value (v), vars (v != nullptr ? vs : nullptr) {}
-
- template <typename T>
- lookup (V& v, const T& x): lookup (&v, &x.vars) {}
- };
-
- // Representation types.
- //
- template <typename T> struct value_traits;
-
- // Assign value type to the value.
- //
- template <typename T>
- void assign (value&, const variable&);
- void assign (value&, const value_type*, const variable&);
-
- template <typename T> typename value_traits<T>::type as (value&);
- template <typename T> typename value_traits<T>::const_type as (const value&);
-
- // "Assign" simple value type to the value stored in name. Return false
- // if the value is not valid for this type.
- //
- template <typename T> bool assign (name&);
-
- template <typename T> typename value_traits<T>::type as (name&);
- template <typename T> typename value_traits<T>::const_type as (const name&);
-
- // bool
- //
- template <typename D>
- struct bool_value
- {
- explicit
- operator bool () const {return d->value[0] == 't';}
-
- bool_value&
- operator= (bool v)
- {
- d->value = v ? "true" : "false";
- return *this;
- }
-
- bool_value&
- operator+= (bool v)
- {
- if (!*this && v)
- d->value = "true";
- return *this;
- }
-
- // Implementation details.
- //
- public:
- explicit bool_value (D& d): d (&d) {}
-
- bool_value (const bool_value&) = delete;
- bool_value& operator= (const bool_value&) = delete; // Rebind or deep?
-
- bool_value (bool_value&&) = default;
- bool_value& operator= (bool_value&&) = delete;
-
- D* d; // name
- };
-
- template <>
- struct value_traits<bool>
- {
- using type = bool_value<name>;
- using const_type = bool_value<const name>;
-
- static type as (name& n) {return type (n);}
- static const_type as (const name& n) {return const_type (n);}
-
- static type as (value&);
- static const_type as (const value&);
-
- static bool assign (name&);
- static void assign (value&, bool);
- static void append (value&, bool);
-
- static const build::value_type value_type;
- };
-
- extern const value_type* bool_type;
-
- // string
- //
- template <>
- struct value_traits<std::string>
- {
- using type = std::string&;
- using const_type = const std::string&;
-
- static type as (name& n) {return n.value;}
- static const_type as (const name& n) {return n.value;}
-
- static type as (value&);
- static const_type as (const value&);
-
- static bool assign (name&);
- static void assign (value&, std::string);
- static void append (value&, std::string);
-
- static const build::value_type value_type;
- };
-
- extern const value_type* string_type;
-
- // dir_path
- //
- template <>
- struct value_traits<dir_path>
- {
- using type = dir_path&;
- using const_type = const dir_path&;
-
- static type as (name& n) {return n.dir;}
- static const_type as (const name& n) {return n.dir;}
-
- static type as (value&);
- static const_type as (const value&);
-
- static bool assign (name&);
- static void assign (value&, dir_path);
- static void append (value&, dir_path);
-
- static const build::value_type value_type;
- };
-
- extern const value_type* dir_path_type;
-
- // name
- //
- template <>
- struct value_traits<name>
- {
- using type = name&;
- using const_type = const name&;
-
- static type as (name& n) {return n;}
- static const_type as (const name& n) {return n;}
-
- static type as (value&);
- static const_type as (const value&);
-
- static bool assign (name&) {return true;}
- static void assign (value&, name);
- static void append (value&, name) = delete;
-
- static const build::value_type value_type;
- };
-
- extern const value_type* name_type;
-
- // vector<T>
- //
- template <typename T, typename D>
- struct vector_value
- {
- using size_type = typename D::size_type;
-
- using value_type = typename value_traits<T>::type;
- using const_value_type = typename value_traits<T>::const_type;
-
- template <typename I, typename V, typename R>
- struct iterator_impl: I
- {
- using value_type = V;
- using pointer = value_type*;
- using reference = R;
- using difference_type = typename I::difference_type;
-
- iterator_impl () = default;
- iterator_impl (const I& i): I (i) {}
-
- // Note: operator->() is unavailable if R is a value.
- //
- reference operator* () const {return as<T> (I::operator* ());}
- pointer operator-> () const {return &as<T> (I::operator* ());}
- reference operator[] (difference_type n) const
- {
- return as<T> (I::operator[] (n));
- }
- };
-
- // Make iterator the same as const_iterator if our data type is const.
- //
- using const_iterator =
- iterator_impl<names::const_iterator, const T, const_value_type>;
- using iterator = typename std::conditional<
- std::is_const<D>::value,
- const_iterator,
- iterator_impl<names::iterator, T, value_type>>::type;
-
- public:
- vector_value&
- operator= (std::vector<T> v) {assign (std::move (v)); return *this;}
-
- vector_value&
- assign (std::vector<T>);
-
- template <typename D1>
- vector_value&
- assign (const vector_value<T, D1>&);
-
- template <typename D1>
- vector_value&
- append (const vector_value<T, D1>&);
-
- public:
- bool empty () const {return d->empty ();}
- size_type size () const {return d->size ();}
-
- value_type operator[] (size_type i) {return as<T> ((*d)[i]);}
- const_value_type operator[] (size_type i) const {return as<T> ((*d)[i]);}
-
- iterator begin () {return iterator (d->begin ());}
- iterator end () {return iterator (d->end ());}
-
- const_iterator begin () const {return const_iterator (d->begin ());}
- const_iterator end () const {return const_iterator (d->end ());}
-
- const_iterator cbegin () const {return const_iterator (d->begin ());}
- const_iterator cend () const {return const_iterator (d->end ());}
-
- // Implementation details.
- //
- public:
- explicit vector_value (D& d): d (&d) {}
-
- vector_value (const vector_value&) = delete;
- vector_value& operator= (const vector_value&) = delete; // Rebind or deep?
-
- vector_value (vector_value&&) = default;
- vector_value& operator= (vector_value&&) = default; //@@ TMP
-
- explicit vector_value (std::nullptr_t): d (nullptr) {} //@@ TMP
-
- D* d; // names
- };
-
- template <typename T>
- struct value_traits<std::vector<T>>
- {
- using type = vector_value<T, names>;
- using const_type = vector_value<T, const names>;
-
- static type as (value&);
- static const_type as (const value&);
-
- template <typename V> static void assign (value&, V);
- template <typename V> static void append (value&, V);
-
- static const std::string type_name;
- static const build::value_type value_type;
- };
-
- template <typename T, typename D>
- struct value_traits<vector_value<T, D>>: value_traits<std::vector<T>> {};
-
- using strings_value = vector_value<std::string, names>;
- using const_strings_value = vector_value<std::string, const names>;
-
- extern const value_type* strings_type; // vector<string> aka strings
- extern const value_type* dir_paths_type; // vector<dir_path> aka dir_paths
- extern const value_type* names_type; // vector<name> aka names
-
- // map<K, V>
- //
- template <typename K, typename V, typename D>
- struct map_value
- {
- template <typename F, typename S>
- struct pair
- {
- using first_type = typename std::conditional<
- std::is_reference<F>::value,
- std::reference_wrapper<typename std::remove_reference<F>::type>,
- F>::type;
-
- using second_type = typename std::conditional<
- std::is_reference<S>::value,
- std::reference_wrapper<typename std::remove_reference<S>::type>,
- S>::type;
-
- first_type first;
- second_type second;
- };
-
- template <typename I, typename T>
- struct iterator_impl
- {
- using value_type = T;
- using pointer = value_type*;
- using reference = value_type; // Note: value.
- using difference_type = typename I::difference_type;
- using iterator_category = std::bidirectional_iterator_tag;
-
- iterator_impl () = default;
- iterator_impl (const I& i): i_ (i) {}
-
- pointer operator-> () const = delete;
- reference operator* () const
- {
- return value_type {as<K> (*i_), as<V> (*(i_ + 1))};
- }
-
- iterator_impl& operator++ () {i_ += 2; return *this;}
- iterator_impl operator++ (int) {auto r (*this); operator++ (); return r;}
-
- iterator_impl& operator-- () {i_ -= 2; return *this;}
- iterator_impl operator-- (int) {auto r (*this); operator-- (); return r;}
-
- bool operator== (const iterator_impl& y) const {return i_ == y.i_;}
- bool operator!= (const iterator_impl& y) const {return i_ != y.i_;}
-
- private:
- I i_;
- };
-
- using size_type = typename D::size_type;
-
- using value_type = pair<typename value_traits<K>::const_type,
- typename value_traits<V>::type>;
-
- using const_value_type = pair<typename value_traits<K>::const_type,
- typename value_traits<V>::const_type>;
-
- // Make iterator the same as const_iterator if our data type is const.
- //
- using const_iterator =
- iterator_impl<names::const_iterator, const_value_type>;
- using iterator = typename std::conditional<
- std::is_const<D>::value,
- const_iterator,
- iterator_impl<names::iterator, value_type>>::type;
-
-
- public:
- map_value&
- operator= (std::map<K, V> m) {assign (std::move (m)); return *this;}
-
- map_value&
- assign (std::map<K, V>);
-
- bool empty () const {return d->empty ();}
- size_type size () const {return d->size ();}
-
- iterator find (const K&);
- const_iterator find (const K&) const;
-
- iterator begin () {return iterator (d->begin ());}
- iterator end () {return iterator (d->end ());}
-
- const_iterator begin () const {return const_iterator (d->begin ());}
- const_iterator end () const {return const_iterator (d->end ());}
-
- // Implementation details.
- //
- public:
- explicit map_value (D& d): d (&d) {}
-
- map_value (const map_value&) = delete;
- map_value& operator= (const map_value&) = delete; // Rebind or deep?
-
- map_value (map_value&&) = default;
- map_value& operator= (map_value&&) = delete;
-
- D* d; // names
- };
-
- template <typename K, typename V>
- struct value_traits<std::map<K, V>>
- {
- using type = map_value<K, V, names>;
- using const_type = map_value<K, V, const names>;
-
- static type as (value&);
- static const_type as (const value&);
-
- template <typename M> static void assign (value&, M);
- template <typename M> static void append (value&, M);
-
- static const std::string type_name;
- static const build::value_type value_type;
- };
-
- template <typename K, typename V, typename D>
- struct value_traits<map_value<K, V, D>>: value_traits<std::map<K, V>> {};
-}
-
-namespace std
-{
- template <>
- struct hash<build::variable>: hash<string>
- {
- size_t
- operator() (const build::variable& v) const noexcept
- {
- return hash<string>::operator() (v.name);
- }
- };
-}
-
-namespace butl
-{
- template <>
- struct compare_prefix<build::variable_cref>: compare_prefix<std::string>
- {
- typedef compare_prefix<std::string> base;
-
- explicit
- compare_prefix (char d): base (d) {}
-
- bool
- operator() (const build::variable& x, const build::variable& y) const
- {
- return base::operator() (x.name, y.name);
- }
-
- bool
- prefix (const build::variable& p, const build::variable& k) const
- {
- return base::prefix (p.name, k.name);
- }
- };
-}
-
-namespace build
-{
- // variable_pool
- //
- using variable_pool_base = std::unordered_set<variable>;
- struct variable_pool: private variable_pool_base
- {
- const variable&
- find (string name, const build::value_type* t = nullptr, char p = '\0')
- {
- return find (name, nullptr, t, p);
- }
-
- const variable&
- find (string name,
- variable_visibility v,
- const build::value_type* t = nullptr,
- char p = '\0')
- {
- return find (name, &v, t, p);
- }
-
- using variable_pool_base::clear;
-
- private:
- const variable&
- find (string name,
- const variable_visibility* vv,
- const build::value_type* t,
- char p)
- {
- auto r (
- insert (
- variable {
- std::move (name),
- t,
- vv != nullptr ? *vv : variable_visibility::normal,
- p}));
- const variable& v (*r.first);
-
- // Update type?
- //
- if (!r.second && t != nullptr && v.type != t)
- {
- assert (v.type == nullptr);
- const_cast<variable&> (v).type = t; // Not changing the key.
- }
-
- // Change visibility? While this might at first seem like a bad idea,
- // it can happen that the variable lookup happens before any values
- // were set, in which case the variable will be entered with the
- // default visibility.
- //
- if (!r.second && vv != nullptr && v.visibility != *vv)
- {
- assert (v.visibility == variable_visibility::normal); // Default.
- const_cast<variable&> (v).visibility = *vv; // Not changing the key.
- }
-
- return v;
- }
- };
-
- extern variable_pool var_pool;
-
- // variable_map
- //
- struct variable_map
- {
- using map_type = butl::prefix_map<variable_cref, value, '.'>;
- using size_type = map_type::size_type;
-
- template <typename I>
- struct iterator_adapter: I
- {
- iterator_adapter () = default;
- iterator_adapter (const I& i): I (i) {}
- typename I::reference operator* () const;
- typename I::pointer operator-> () const;
- };
-
- using const_iterator = iterator_adapter<map_type::const_iterator>;
-
- const value*
- find (const variable& var) const
- {
- auto i (m_.find (var));
- const value* r (i != m_.end () ? &i->second : nullptr);
-
- // First access after being assigned a type?
- //
- if (r != nullptr && var.type != nullptr && r->type != var.type)
- build::assign (const_cast<value&> (*r), var.type, var);
-
- return r;
- }
-
- value*
- find (const variable& var)
- {
- auto i (m_.find (var));
- value* r (i != m_.end () ? &i->second : nullptr);
-
- // First access after being assigned a type?
- //
- if (r != nullptr && var.type != nullptr && r->type != var.type)
- build::assign (*r, var.type, var);
-
- return r;
- }
-
- lookup<const value>
- operator[] (const variable& var) const
- {
- return lookup<const value> (find (var), this);
- }
-
- lookup<const value>
- operator[] (const std::string& name) const
- {
- return operator[] (var_pool.find (name));
- }
-
- // Non-const lookup. Only exposed on the map directly.
- //
- lookup<value>
- operator[] (const variable& var)
- {
- return lookup<value> (find (var), this);
- }
-
- lookup<value>
- operator[] (const std::string& name)
- {
- return operator[] (var_pool.find (name));
- }
-
- // The second member in the pair indicates whether the new
- // value (which will be NULL) was assigned.
- //
- std::pair<std::reference_wrapper<value>, bool>
- assign (const variable& var)
- {
- auto r (m_.emplace (var, value (var.type)));
- value& v (r.first->second);
-
- // First access after being assigned a type?
- //
- if (!r.second && var.type != nullptr && v.type != var.type)
- build::assign (v, var.type, var);
-
- return std::make_pair (std::reference_wrapper<value> (v), r.second);
- }
-
- std::pair<std::reference_wrapper<value>, bool>
- assign (const std::string& name)
- {
- return assign (var_pool.find (name));
- }
-
- std::pair<const_iterator, const_iterator>
- find_namespace (const std::string& ns) const
- {
- auto r (m_.find_prefix (var_pool.find (ns)));
- return std::make_pair (const_iterator (r.first),
- const_iterator (r.second));
- }
-
- const_iterator
- begin () const {return m_.begin ();}
-
- const_iterator
- end () const {return m_.end ();}
-
- bool
- empty () const {return m_.empty ();}
-
- size_type
- size () const {return m_.size ();}
-
- private:
- map_type m_;
- };
-
- // Target type/pattern-specific variables.
- //
- // @@ In quite a few places we assume that we can store a reference
- // to the returned value (e.g., install::lookup_install()). If
- // we "instantiate" the value on the fly, then we will need to
- // consider its lifetime.
- //
- using variable_pattern_map = std::map<std::string, variable_map>;
-
- struct variable_type_map: std::map<std::reference_wrapper<const target_type>,
- variable_pattern_map>
- {
- build::lookup<const value>
- lookup (const target_type&, const string& name, const variable&) const;
- };
-}
-
-#include <build/variable.ixx>
-#include <build/variable.txx>
-
-#endif // BUILD_VARIABLE
diff --git a/build/variable.cxx b/build/variable.cxx
deleted file mode 100644
index 6e02816..0000000
--- a/build/variable.cxx
+++ /dev/null
@@ -1,452 +0,0 @@
-// file : build/variable.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <build/variable>
-
-#include <iterator> // make_move_iterator()
-
-#include <build/utility>
-#include <build/diagnostics>
-
-using namespace std;
-
-namespace build
-{
- // value
- //
- void
- assign (value& v, const value_type* t, const variable& var)
- {
- if (v.type == nullptr)
- {
- v.type = t;
-
- if (v && t->assign != nullptr)
- v.state_ = t->assign (v.data_, var)
- ? value::state_type::filled
- : value::state_type::empty;
- }
- else
- fail << "variable '" << var.name << "' type mismatch" <<
- info << "value '" << v.data_ << "' is " << v.type->name <<
- info << (t == var.type ? "variable" : "new type") << " is "
- << (var.type != nullptr ? var.type->name : "untyped");
- }
-
- value& value::
- operator= (value&& v)
- {
- assert (type == nullptr || type == v.type);
-
- // Since the types are the same, we don't need to call
- // the callbacks.
- //
- type = v.type;
- state_ = v.state_;
- data_ = move (v.data_);
-
- return *this;
- }
-
- value& value::
- append (value v, const variable& var)
- {
- assert (type == v.type);
- append (move (v.data_), var);
- return *this;
- }
-
- value& value::
- prepend (value v, const variable& var)
- {
- assert (type == v.type);
- prepend (move (v.data_), var);
- return *this;
- }
-
- void value::
- append (names v, const variable& var)
- {
- // Treat append to NULL as assign.
- //
- if (!null () && type != nullptr && type->append != nullptr)
- {
- state_ = type->append (data_, move (v), var)
- ? state_type::filled
- : state_type::empty;
- return;
- }
-
- if (data_.empty ())
- data_ = move (v);
- else
- data_.insert (data_.end (),
- make_move_iterator (v.begin ()),
- make_move_iterator (v.end ()));
-
- state_ = (type != nullptr && type->assign != nullptr
- ? type->assign (data_, var)
- : !data_.empty ())
- ? state_type::filled
- : state_type::empty;
- }
-
- void value::
- prepend (names v, const variable& var)
- {
- // Reduce to append.
- //
- if (!null () && type != nullptr && type->append != nullptr)
- {
- state_ = type->append (v, move (data_), var)
- ? state_type::filled
- : state_type::empty;
- swap (data_, v);
- return;
- }
-
- if (data_.empty ())
- data_ = move (v);
- else
- {
- v.insert (v.end (),
- make_move_iterator (data_.begin ()),
- make_move_iterator (data_.end ()));
- swap (data_, v);
- }
-
- state_ = (type != nullptr && type->assign != nullptr
- ? type->assign (data_, var)
- : !data_.empty ())
- ? state_type::filled
- : state_type::empty;
- }
-
- // bool value
- //
- bool value_traits<bool>::
- assign (name& n)
- {
- if (n.simple ())
- {
- const string& s (n.value);
-
- if (s == "true" || s == "false")
- return true;
- }
-
- return false;
- }
-
- static bool
- bool_assign (names& v, const variable& var)
- {
- // Verify the value is either "true" or "false".
- //
- if (v.size () == 1)
- {
- name& n (v.front ());
-
- if (value_traits<bool>::assign (n))
- return true;
- }
-
- fail << "invalid bool variable '" << var.name << "' value '" << v << "'";
- return false;
- }
-
- static bool
- bool_append (names& v, names a, const variable& var)
- {
- // Translate append to OR.
- //
- bool_assign (a, var); // Verify "true" or "false".
-
- if (a.front ().value[0] == 't' && v.front ().value[0] == 'f')
- v = move (a);
-
- return true;
- }
-
- const value_type value_traits<bool>::value_type
- {
- "bool",
- &bool_assign,
- &bool_append
- };
-
- const value_type* bool_type = &value_traits<bool>::value_type;
-
- // string value
- //
- bool value_traits<string>::
- assign (name& n)
- {
- // The below code is quite convoluted because we don't want to
- // modify the name until we know it good (if it is not, then it
- // will most likely be printed by the caller in diagnostics).
-
- // Suspend project qualification.
- //
- const string* p (n.proj);
- n.proj = nullptr;
-
- // Convert directory to string.
- //
- if (n.directory ())
- {
- n.value = std::move (n.dir).string (); // Move string out of path.
-
- // Add / back to the end of the path unless it is already there.
- // Note that the string cannot be empty (n.directory () would
- // have been false).
- //
- if (!dir_path::traits::is_separator (n.value[n.value.size () - 1]))
- n.value += '/';
- }
-
- if (!n.simple ())
- {
- n.proj = p; // Restore.
- return false;
- }
-
- // Convert project qualification to its string representation.
- //
- if (p != nullptr)
- {
- string s (*p);
- s += '%';
- s += n.value;
- s.swap (n.value);
- }
-
- return true;
- }
-
- static bool
- string_assign (names& v, const variable& var)
- {
- // Verify/convert the value is/to a single simple name.
- //
- if (v.empty ())
- {
- v.emplace_back (name ()); // Canonical empty string representation.
- return false;
- }
- else if (v.size () == 1)
- {
- name& n (v.front ());
-
- if (value_traits<string>::assign (n))
- return !n.value.empty ();
- }
-
- fail << "invalid string variable '" << var.name << "' value '" << v << "'";
- return false;
- }
-
- static bool
- string_append (names& v, names a, const variable& var)
- {
- // Translate append to string concatenation.
- //
- string_assign (a, var); // Verify/convert value is/to string.
-
- if (v.front ().value.empty ())
- v = move (a);
- else
- v.front ().value += a.front ().value;
-
- return !v.front ().value.empty ();
- }
-
- const value_type value_traits<string>::value_type
- {
- "string",
- &string_assign,
- &string_append
- };
-
- const value_type* string_type = &value_traits<string>::value_type;
-
- // dir_path value
- //
- bool value_traits<dir_path>::
- assign (name& n)
- {
- if (n.directory ())
- return true;
-
- if (n.simple ())
- {
- try
- {
- n.dir = n.empty () ? dir_path () : dir_path (move (n.value));
- n.value.clear ();
- return true;
- }
- catch (const invalid_path&) {} // Fall through.
- }
-
- return false;
- }
-
- static bool
- dir_path_assign (names& v, const variable& var)
- {
- // Verify/convert the value is/to a single directory name.
- //
- if (v.empty ())
- {
- v.emplace_back (dir_path ()); // Canonical empty path representation.
- return false;
- }
- else if (v.size () == 1)
- {
- name& n (v.front ());
-
- if (value_traits<dir_path>::assign (n))
- return !n.dir.empty ();
- }
-
- fail << "invalid dir_path variable '" << var.name << "' "
- << "value '" << v << "'";
- return false;
- }
-
- static bool
- dir_path_append (names& v, names a, const variable& var)
- {
- // Translate append to path concatenation.
- //
- dir_path_assign (a, var); // Verify/convert value is/to dir_path.
-
- dir_path& d (a.front ().dir);
- if (d.relative ())
- return !(v.front ().dir /= d).empty ();
- else
- fail << "append of absolute path '" << d << "' to dir_path variable "
- << var.name;
-
- return false;
- }
-
- const value_type value_traits<dir_path>::value_type
- {
- "dir_path",
- &dir_path_assign,
- &dir_path_append
- };
-
- const value_type* dir_path_type = &value_traits<dir_path>::value_type;
-
- // name value
- //
- static bool
- name_assign (names& v, const variable& var)
- {
- // Verify the value is a single name.
- //
- if (v.size () == 1)
- return v.front ().empty ();
-
- fail << "invalid string variable '" << var.name << "' value '" << v << "'";
- return false;
- }
-
- static bool
- name_append (names&, names, const variable& var)
- {
- fail << "append to name variable '" << var.name << "'";
- return false;
- }
-
- const value_type value_traits<name>::value_type
- {
- "name",
- &name_assign,
- &name_append
- };
-
- const value_type* name_type = &value_traits<name>::value_type;
-
- // vector<T> value
- //
- const value_type* strings_type = &value_traits<strings>::value_type;
- const value_type* dir_paths_type = &value_traits<dir_paths>::value_type;
- const value_type* names_type = &value_traits<names>::value_type;
-
- // variable_set
- //
- variable_pool var_pool;
-
- // variable_type_map
- //
- lookup<const value> variable_type_map::
- lookup (const target_type& type,
- const string& name,
- const variable& var) const
- {
- using result = build::lookup<const value>;
-
- // Search across target type hierarchy.
- //
- for (auto tt (&type); tt != nullptr; tt = tt->base)
- {
- auto i (find (*tt));
-
- if (i == end ())
- continue;
-
- // Try to match the pattern, starting from the longest values
- // so that the more "specific" patterns (i.e., those that cover
- // fewer characters with the wildcard) take precedence. See
- // tests/variable/type-pattern.
- //
- const variable_pattern_map& m (i->second);
-
- for (auto j (m.rbegin ()); j != m.rend (); ++j)
- {
- const string& p (j->first);
-
- size_t nn (name.size ());
- size_t pn (p.size ());
-
- if (nn < pn - 1) // One for '*'.
- continue;
-
- size_t w (p.find ('*'));
- assert (w != string::npos);
-
- // Compare prefix.
- //
- if (w != 0 &&
- name.compare (0, w, p, 0, w) != 0)
- continue;
-
- ++w; // First suffix character.
- pn -= w; // Suffix length.
-
- // Compare suffix.
- //
- if (pn != 0 &&
- name.compare (nn - pn, pn, p, w, pn) != 0)
- continue;
-
- // Ok, this pattern matches. But is there a variable?
- //
- if (const value* v = j->second.find (var))
- {
- //@@ TODO: should we detect ambiguity? 'foo-*' '*-foo' and
- // 'foo-foo'? Right now the last defined will be used.
- //
- return result (v, &j->second);
- }
- }
- }
-
- return result ();
- }
-}
diff --git a/build/variable.ixx b/build/variable.ixx
deleted file mode 100644
index 16ea7c0..0000000
--- a/build/variable.ixx
+++ /dev/null
@@ -1,398 +0,0 @@
-// file : build/variable.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace build
-{
- // value
- //
- template <typename T>
- inline void
- assign (value& v, const variable& var)
- {
- auto t (&value_traits<T>::value_type);
-
- if (v.type != t)
- assign (v, t, var);
- }
-
- template <typename T>
- inline typename value_traits<T>::type
- as (value& v)
- {
- return value_traits<T>::as (v);
- }
-
- template <typename T>
- inline typename value_traits<T>::const_type
- as (const value& v)
- {
- return value_traits<T>::as (v);
- }
-
- template <typename T>
- inline bool
- assign (name& n)
- {
- return value_traits<T>::assign (n);
- }
-
- template <typename T>
- inline typename value_traits<T>::type
- as (name& n)
- {
- return value_traits<T>::as (n);
- }
-
- template <typename T>
- inline typename value_traits<T>::const_type
- as (const name& n)
- {
- return value_traits<T>::as (n);
- }
-
- template <typename T>
- inline value& value::
- operator= (T v)
- {
- value_traits<T>::assign (*this, std::move (v));
- return *this;
- }
-
- template <typename T>
- inline value& value::
- operator+= (T v)
- {
- value_traits<T>::append (*this, std::move (v));
- return *this;
- }
-
- inline void value::
- assign (names v, const variable& var)
- {
- data_ = std::move (v);
- state_ = (type != nullptr && type->assign != nullptr
- ? type->assign (data_, var)
- : !data_.empty ())
- ? state_type::filled
- : state_type::empty;
- }
-
- // bool value
- //
- inline bool_value<name> value_traits<bool>::
- as (value& v)
- {
- assert (v.type == bool_type);
- return bool_value<name> (v.data_.front ());
- }
-
- inline bool_value<const name> value_traits<bool>::
- as (const value& v)
- {
- assert (v.type == bool_type);
- return bool_value<const name> (v.data_.front ());
- }
-
- inline void value_traits<bool>::
- assign (value& v, bool x)
- {
- if (v.null ())
- {
- if (v.type == nullptr)
- v.type = bool_type;
- v.data_.emplace_back (name ());
- v.state_ = value::state_type::empty;
- }
-
- as (v) = x;
- v.state_ = value::state_type::filled;
- }
-
- inline void value_traits<bool>::
- append (value& v, bool x)
- {
- if (v.null ())
- assign (v, x);
- else
- as (v) += x; // Cannot be empty.
- }
-
- // string value
- //
- inline std::string& value_traits<std::string>::
- as (value& v)
- {
- assert (v.type == string_type);
- return v.data_.front ().value;
- }
-
- inline const std::string& value_traits<std::string>::
- as (const value& v)
- {
- assert (v.type == string_type);
- return v.data_.front ().value;
- }
-
- inline void value_traits<std::string>::
- assign (value& v, std::string x)
- {
- if (v.null ())
- {
- if (v.type == nullptr)
- v.type = string_type;
- v.data_.emplace_back (name ());
- v.state_ = value::state_type::empty;
- }
-
- v.state_ = (as (v) = std::move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- inline void value_traits<std::string>::
- append (value& v, std::string x)
- {
- if (v.null ())
- assign (v, std::move (x));
- else
- v.state_ = (as (v) += std::move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- // dir_path value
- //
- inline dir_path& value_traits<dir_path>::
- as (value& v)
- {
- assert (v.type == dir_path_type);
- return v.data_.front ().dir;
- }
-
- inline const dir_path& value_traits<dir_path>::
- as (const value& v)
- {
- assert (v.type == dir_path_type);
- return v.data_.front ().dir;
- }
-
- inline void value_traits<dir_path>::
- assign (value& v, dir_path x)
- {
- if (v.null ())
- {
- if (v.type == nullptr)
- v.type = dir_path_type;
- v.data_.emplace_back (name ());
- v.state_ = value::state_type::empty;
- }
-
- v.state_ = (as (v) = std::move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- inline void value_traits<dir_path>::
- append (value& v, dir_path x)
- {
- if (v.null ())
- assign (v, std::move (x));
- else
- v.state_ = (as (v) /= std::move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- // name value
- //
- inline name& value_traits<name>::
- as (value& v)
- {
- assert (v.type == name_type);
- return v.data_.front ();
- }
-
- inline const name& value_traits<name>::
- as (const value& v)
- {
- assert (v.type == name_type);
- return v.data_.front ();
- }
-
- inline void value_traits<name>::
- assign (value& v, name x)
- {
- if (v.null ())
- {
- if (v.type == nullptr)
- v.type = name_type;
- v.data_.emplace_back (name ());
- v.state_ = value::state_type::empty;
- }
-
- v.state_ = (as (v) = std::move (x)).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- // vector<T> value
- //
- template <typename T, typename D>
- inline vector_value<T, D>& vector_value<T, D>::
- assign (std::vector<T> v)
- {
- d->clear ();
- d->insert (d->end (),
- std::make_move_iterator (v.begin ()),
- std::make_move_iterator (v.end ()));
- return *this;
- }
-
- template <typename T, typename D>
- template <typename D1>
- inline vector_value<T, D>& vector_value<T, D>::
- assign (const vector_value<T, D1>& v)
- {
- d->clear ();
- d->insert (d->end (), v.begin (), v.end ());
- return *this;
- }
-
- template <typename T, typename D>
- template <typename D1>
- inline vector_value<T, D>& vector_value<T, D>::
- append (const vector_value<T, D1>& v)
- {
- d->insert (d->end (), v.begin (), v.end ());
- return *this;
- }
-
- template <typename T>
- inline vector_value<T, names> value_traits<std::vector<T>>::
- as (value& v)
- {
- assert (v.type == &value_traits<std::vector<T>>::value_type);
- return vector_value<T, names> (v.data_);
- }
-
- template <typename T>
- inline vector_value<T, const names> value_traits<std::vector<T>>::
- as (const value& v)
- {
- assert (v.type == &value_traits<std::vector<T>>::value_type);
- return vector_value<T, const names> (v.data_);
- }
-
- template <typename T>
- template <typename V>
- inline void value_traits<std::vector<T>>::
- assign (value& v, V x)
- {
- if (v.null ())
- {
- if (v.type == nullptr)
- v.type = &value_traits<std::vector<T>>::value_type;
- v.state_ = value::state_type::empty;
- }
-
- v.state_ = (as (v).assign (std::move (x))).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- template <typename T>
- template <typename V>
- inline void value_traits<std::vector<T>>::
- append (value& v, V x)
- {
- if (v.null ())
- assign (v, std::move (x));
- else
- v.state_ = (as (v).append (std::move (x))).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- // map<K, V> value
- //
- template <typename K, typename V>
- inline map_value<K, V, names> value_traits<std::map<K, V>>::
- as (value& v)
- {
- assert ((v.type == &value_traits<std::map<K, V>>::value_type));
- return map_value<K, V, names> (v.data_);
- }
-
- template <typename K, typename V>
- inline map_value<K, V, const names> value_traits<std::map<K, V>>::
- as (const value& v)
- {
- assert ((v.type == &value_traits<std::map<K, V>>::value_type));
- return map_value<K, V, const names> (v.data_);
- }
-
- template <typename K, typename V>
- template <typename M>
- inline void value_traits<std::map<K, V>>::
- assign (value& v, M x)
- {
- if (v.null ())
- {
- if (v.type == nullptr)
- v.type = &value_traits<std::map<K, V>>::value_type;
- v.state_ = value::state_type::empty;
- }
-
- v.state_ = (as (v).assign (std::move (x))).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- template <typename K, typename V>
- template <typename M>
- inline void value_traits<std::map<K, V>>::
- append (value& v, M x)
- {
- if (v.null ())
- assign (v, std::move (x));
- else
- v.state_ = (as (v).append (std::move (x))).empty ()
- ? value::state_type::empty
- : value::state_type::filled;
- }
-
- // variable_map::iterator_adapter
- //
- template <typename I>
- inline typename I::reference variable_map::iterator_adapter<I>::
- operator* () const
- {
- auto& r (I::operator* ());
- const variable& var (r.first);
- auto& val (r.second);
-
- // First access after being assigned a type?
- //
- if (var.type != nullptr && val.type != var.type)
- build::assign (const_cast<value&> (val), var.type, var);
-
- return r;
- }
-
- template <typename I>
- inline typename I::pointer variable_map::iterator_adapter<I>::
- operator-> () const
- {
- auto p (I::operator-> ());
- const variable& var (p->first);
- auto& val (p->second);
-
- // First access after being assigned a type?
- //
- if (var.type != nullptr && val.type != var.type)
- build::assign (const_cast<value&> (val), var.type, var);
-
- return p;
- }
-}
diff --git a/build/variable.txx b/build/variable.txx
deleted file mode 100644
index e661879..0000000
--- a/build/variable.txx
+++ /dev/null
@@ -1,168 +0,0 @@
-// file : build/variable.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <iterator> // make_move_iterator()
-
-#include <build/diagnostics>
-
-namespace build
-{
- // vector<T> value
- //
- template <typename T>
- bool
- vector_assign (names& v, const variable& var)
- {
- // Verify each element has valid value of T.
- //
- for (name& n: v)
- {
- if (!assign<T> (n))
- fail << "invalid " << value_traits<T>::value_type.name << " element "
- << "'" << n << "' in variable '" << var.name << "'";
- }
-
- return !v.empty ();
- }
-
- template <typename T>
- bool
- vector_append (names& v, names a, const variable& var)
- {
- // Verify that what we are appending is valid.
- //
- vector_assign<T> (a, var);
-
- if (v.empty ())
- v = move (a);
- else
- v.insert (v.end (),
- std::make_move_iterator (a.begin ()),
- std::make_move_iterator (a.end ()));
-
- return !v.empty ();
- }
-
- template <typename T>
- const std::string value_traits<std::vector<T>>::type_name = std::string (
- value_traits<T>::value_type.name) + 's';
-
- template <typename T>
- const value_type value_traits<std::vector<T>>::value_type
- {
- value_traits<std::vector<T>>::type_name.c_str (),
- &vector_assign<T>,
- &vector_append<T>
- };
-
- // map<K, V> value
- //
- template <typename K, typename V, typename D>
- map_value<K, V, D>& map_value<K, V, D>::
- assign (std::map<K, V> m)
- {
- d->clear ();
- for (auto& p: m)
- {
- d->emplace_back (p.first); // Const, can't move.
- d->back ().pair = '=';
- d->emplace_back (std::move (p.second));
- }
-
- return *this;
- }
-
- template <typename K, typename V, typename D>
- auto map_value<K, V, D>::
- find (const K& k) -> iterator
- {
- // @@ Scan backwards to handle duplicates.
- //
- for (auto i (d->rbegin ()); i != d->rend (); ++i)
- if (as<K> (*++i) == k)
- return iterator (--(i.base ()));
-
- return end ();
- }
-
- template <typename K, typename V, typename D>
- auto map_value<K, V, D>::
- find (const K& k) const -> const_iterator
- {
- // @@ Scan backwards to handle duplicates.
- //
- for (auto i (d->rbegin ()); i != d->rend (); ++i)
- if (as<K> (*++i) == k)
- return const_iterator (--(i.base ()));
-
- return end ();
- }
-
- template <typename K, typename V>
- bool
- map_assign (names& v, const variable& var)
- {
- // Verify we have a sequence of pairs and each lhs/rhs is a valid
- // value of K/V.
- //
- for (auto i (v.begin ()); i != v.end (); ++i)
- {
- if (i->pair == '\0')
- fail << value_traits<std::map<K, V>>::value_type.name << " key-value "
- << "pair expected instead of '" << *i << "' "
- << "in variable '" << var.name << "'";
-
- if (!assign<K> (*i))
- fail << "invalid " << value_traits<K>::value_type.name << " key "
- << "'" << *i << "' in variable '" << var.name << "'";
-
- ++i; // Got to have the second half of the pair.
-
- if (!assign<V> (*i))
- fail << "invalid " << value_traits<V>::value_type.name << " value "
- << "'" << *i << "' in variable '" << var.name << "'";
- }
-
- //@@ When doing sorting, note that assign() can convert the
- // value.
-
- //@@ Is sorting really the right trade-off (i.e., insertion
- // vs search)? Perhaps linear search is ok?
-
- return !v.empty ();
- }
-
- template <typename K, typename V>
- bool
- map_append (names& v, names a, const variable& var)
- {
- //@@ Not weeding out duplicates.
-
- // Verify that what we are appending is valid.
- //
- map_assign<K, V> (a, var);
-
- if (v.empty ())
- v = move (a);
- else
- v.insert (v.end (),
- std::make_move_iterator (a.begin ()),
- std::make_move_iterator (a.end ()));
-
- return !v.empty ();
- }
-
- template <typename K, typename V>
- const std::string value_traits<std::map<K, V>>::type_name = std::string (
- value_traits<K>::value_type.name) + '_' +
- value_traits<V>::value_type.name + "_map";
-
- template <typename K, typename V>
- const value_type value_traits<std::map<K, V>>::value_type
- {
- value_traits<std::map<K, V>>::type_name.c_str (),
- &map_assign<K, V>,
- &map_append<K, V>
- };
-}
diff --git a/build/version b/build/version
deleted file mode 100644
index 96a9274..0000000
--- a/build/version
+++ /dev/null
@@ -1,42 +0,0 @@
-// file : build/version -*- C++ -*-
-// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUILD2_VERSION // Note: using the version macro itself.
-
-#include <butl/version> // LIBBUTL_VERSION
-
-// Version format is AABBCCDD where
-//
-// AA - major version number
-// BB - minor version number
-// CC - bugfix version number
-// DD - alpha / beta (DD + 50) version number
-//
-// When DD is not 00, 1 is subtracted from AABBCC. For example:
-//
-// Version AABBCCDD
-// 2.0.0 02000000
-// 2.1.0 02010000
-// 2.1.1 02010100
-// 2.2.0-a1 02019901
-// 3.0.0-b2 02999952
-//
-#define BUILD2_VERSION 10000
-#define BUILD2_VERSION_STR "0.1.0"
-
-//@@ TMP
-//
-#define BUILD_VERSION BUILD2_VERSION
-#define BUILD_VERSION_STR BUILD2_VERSION_STR
-
-// Generally, we expect minor versions to be source code backwards-
-// compatible, thought we might have a minimum version requirement.
-//
-// Note: does not apply during early development.
-//
-#if LIBBUTL_VERSION != 10000
-# error incompatible libbutl version
-#endif
-
-#endif // BUILD2_VERSION