aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/b.cxx11
-rw-r--r--build/bin/module.cxx37
-rw-r--r--build/bin/rule.cxx4
-rw-r--r--build/buildfile6
-rw-r--r--build/cli/module.cxx20
-rw-r--r--build/cli/rule.cxx2
-rw-r--r--build/config/operation.cxx57
-rw-r--r--build/config/utility48
-rw-r--r--build/config/utility.cxx45
-rw-r--r--build/config/utility.ixx17
-rw-r--r--build/config/utility.txx64
-rw-r--r--build/context.cxx15
-rw-r--r--build/cxx/compile.cxx46
-rw-r--r--build/cxx/link.cxx40
-rw-r--r--build/cxx/module.cxx63
-rw-r--r--build/cxx/utility.txx4
-rw-r--r--build/dump.cxx10
-rw-r--r--build/file7
-rw-r--r--build/file.cxx241
-rw-r--r--build/install/module.cxx139
-rw-r--r--build/install/rule.cxx86
-rw-r--r--build/install/utility20
-rw-r--r--build/name8
-rw-r--r--build/operation.cxx3
-rw-r--r--build/parser15
-rw-r--r--build/parser.cxx86
-rw-r--r--build/scope36
-rw-r--r--build/scope.cxx27
-rw-r--r--build/target22
-rw-r--r--build/target.cxx32
-rw-r--r--build/target.txx6
-rw-r--r--build/test/module.cxx12
-rw-r--r--build/test/rule.cxx83
-rw-r--r--build/variable735
-rw-r--r--build/variable.cxx333
-rw-r--r--build/variable.ixx403
-rw-r--r--build/variable.txx160
-rw-r--r--tests/amalgam/config/buildfile2
-rw-r--r--tests/amalgam/test/buildfile2
39 files changed, 1990 insertions, 957 deletions
diff --git a/build/b.cxx b/build/b.cxx
index 11280e1..412fb12 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -31,6 +31,7 @@
#include <build/diagnostics>
#include <build/context>
#include <build/utility>
+#include <build/variable>
#include <build/token>
#include <build/lexer>
@@ -408,14 +409,14 @@ main (int argc, char* argv[])
// See if the bootstrap process set/changed src_root.
//
{
- auto v (rs.assign ("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 (v.as<const dir_path&> ());
+ const dir_path& p (as<dir_path> (v));
if (src_root.empty ())
src_root = p;
@@ -448,7 +449,7 @@ main (int argc, char* argv[])
v = src_root;
}
- rs.src_path_ = &v.as<const dir_path&> ();
+ rs.src_path_ = &as<dir_path> (v);
}
// At this stage we should have both roots and out_base figured
@@ -468,9 +469,9 @@ main (int argc, char* argv[])
// Why don't we support it? Because things are already complex
// enough here.
//
- if (auto v = rs.vars["subprojects"])
+ if (auto l = rs.vars["subprojects"])
{
- for (const name& n: v.as<const list_value&> ())
+ for (const name& n: *l)
{
if (n.pair != '\0')
continue; // Skip project names.
diff --git a/build/bin/module.cxx b/build/bin/module.cxx
index e03c658..4c5cca5 100644
--- a/build/bin/module.cxx
+++ b/build/bin/module.cxx
@@ -25,16 +25,16 @@ namespace build
// Default config.bin.*.lib values.
//
- static const list_value exe_lib (names {name ("shared"), name ("static")});
- static const list_value liba_lib ("static");
- static const list_value libso_lib ("shared");
+ static const strings exe_lib {"shared", "static"};
+ static const strings liba_lib {"static"};
+ static const strings libso_lib {"shared"};
extern "C" void
bin_init (scope& r,
scope& b,
const location&,
std::unique_ptr<module>&,
- bool)
+ bool first)
{
tracer trace ("bin::init");
level4 ([&]{trace << "for " << b.path ();});
@@ -75,6 +75,21 @@ namespace build
rs.insert<lib> (install_id, "bin.lib", lib_);
}
+ // Enter module variables.
+ //
+ if (first)
+ {
+ variable_pool.find ("config.bin.lib", string_type);
+ variable_pool.find ("config.bin.exe.lib", strings_type);
+ variable_pool.find ("config.bin.liba.lib", strings_type);
+ variable_pool.find ("config.bin.libso.lib", strings_type);
+
+ variable_pool.find ("bin.lib", string_type);
+ variable_pool.find ("bin.exe.lib", strings_type);
+ variable_pool.find ("bin.liba.lib", strings_type);
+ variable_pool.find ("bin.libso.lib", strings_type);
+ }
+
// Configure.
//
using config::required;
@@ -92,7 +107,7 @@ namespace build
// config.bin.lib
//
{
- auto v (b.assign ("bin.lib"));
+ value& v (b.assign ("bin.lib"));
if (!v)
v = required (r, "config.bin.lib", "both").first;
}
@@ -100,7 +115,7 @@ namespace build
// config.bin.exe.lib
//
{
- auto v (b.assign ("bin.exe.lib"));
+ value& v (b.assign ("bin.exe.lib"));
if (!v)
v = required (r, "config.bin.exe.lib", exe_lib).first;
}
@@ -108,7 +123,7 @@ namespace build
// config.bin.liba.lib
//
{
- auto v (b.assign ("bin.liba.lib"));
+ value& v (b.assign ("bin.liba.lib"));
if (!v)
v = required (r, "config.bin.liba.lib", liba_lib).first;
}
@@ -116,14 +131,14 @@ namespace build
// config.bin.libso.lib
//
{
- auto v (b.assign ("bin.libso.lib"));
+ value& v (b.assign ("bin.libso.lib"));
if (!v)
v = required (r, "config.bin.libso.lib", libso_lib).first;
}
// Configure "installability" of our target types.
//
- install::path<exe> (b, "bin"); // Install into install.bin.
+ 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
@@ -143,9 +158,9 @@ namespace build
//
// Everyone is happy then?
//
- install::path<libso> (b, "lib"); // Install into install.lib.
+ install::path<libso> (b, dir_path ("lib")); // Install into install.lib.
- install::path<liba> (b, "lib"); // Install into install.lib.
+ install::path<liba> (b, dir_path ("lib")); // Install into install.lib.
install::mode<liba> (b, "644");
}
}
diff --git a/build/bin/rule.cxx b/build/bin/rule.cxx
index a09bffd..21db183 100644
--- a/build/bin/rule.cxx
+++ b/build/bin/rule.cxx
@@ -46,7 +46,7 @@ namespace build
// 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 (t["bin.lib"].as<const string&> ());
+ const string& type (as<string> (*t["bin.lib"]));
bool ar (type == "static" || type == "both");
bool so (type == "shared" || type == "both");
@@ -121,7 +121,7 @@ namespace build
// prerequisite vs prerequisite_target.
//
//
- const string& type (t["bin.lib"].as<const string&> ());
+ const string& type (as<string> (*t["bin.lib"]));
bool ar (type == "static" || type == "both");
bool so (type == "shared" || type == "both");
diff --git a/build/buildfile b/build/buildfile
index 7522dc2..ce917f6 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -8,13 +8,13 @@ config = config/{operation module utility}
bin = bin/{target rule module}
cxx = cxx/{target compile link install module utility}
cli = cli/{target rule module}
-test = test/{operation rule module}
-install = install/{operation rule module}
+test1 = test/{operation rule module}
+install1 = install/{operation rule module}
exe{b}: cxx{b algorithm name operation spec scope variable target \
prerequisite rule file module context search diagnostics token \
lexer parser path-io utility dump options $config $bin $cxx $cli \
- $test $install} $libs
+ $test1 $install1} $libs
#@@ TODO
#
diff --git a/build/cli/module.cxx b/build/cli/module.cxx
index f112cd4..7702589 100644
--- a/build/cli/module.cxx
+++ b/build/cli/module.cxx
@@ -78,6 +78,16 @@ namespace build
rs.insert<cxx::ixx> (clean_id, "cli.compile", compile_);
}
+ // Enter module variables.
+ //
+ if (first)
+ {
+ variable_pool.find ("config.cli", string_type); //@@ VAR type
+
+ variable_pool.find ("config.cli.options", strings_type);
+ variable_pool.find ("cli.options", strings_type);
+ }
+
// Configure.
//
@@ -85,13 +95,13 @@ namespace build
//
if (first)
{
- auto r (config::required (root, "config.cli", "cli"));
+ auto p (config::required (root, "config.cli", "cli"));
// If we actually set a new value, test it by trying to execute.
//
- if (r.second)
+ if (p.second)
{
- const string& cli (r.first);
+ const string& cli (as<string> (p.first));
const char* args[] = {cli.c_str (), "--version", nullptr};
if (verb)
@@ -142,8 +152,8 @@ namespace build
// cli.* variables. See the cxx module for more information on
// this merging semantics and some of its tricky aspects.
//
- if (auto* v = config::optional<list_value> (root, "config.cli.options"))
- base.assign ("cli.options") += *v;
+ if (const value& v = config::optional (root, "config.cli.options"))
+ base.assign ("cli.options") += as<strings> (v);
}
}
}
diff --git a/build/cli/rule.cxx b/build/cli/rule.cxx
index 247b5b4..a892d7c 100644
--- a/build/cli/rule.cxx
+++ b/build/cli/rule.cxx
@@ -223,7 +223,7 @@ namespace build
path rels (relative (s->path ()));
scope& rs (t.root_scope ());
- const string& cli (rs["config.cli"].as<const string&> ());
+ const string& cli (as<string> (*rs["config.cli"]));
cstrings args {cli.c_str ()};
diff --git a/build/config/operation.cxx b/build/config/operation.cxx
index a602325..913ae29 100644
--- a/build/config/operation.cxx
+++ b/build/config/operation.cxx
@@ -81,9 +81,9 @@ namespace build
<< "# feel free to edit." << endl
<< "#" << endl;
- if (auto v = root.vars["amalgamation"])
+ if (auto l = root.vars["amalgamation"])
{
- const dir_path& d (v.as<const dir_path&> ());
+ const dir_path& d (as<dir_path> (*l));
ofs << "# Base configuration inherited from " << d << endl
<< "#" << endl;
@@ -97,27 +97,23 @@ namespace build
++p.first)
{
const variable& var (p.first->first);
- const value_ptr& pval (p.first->second);
+ const value& val (p.first->second);
// Warn the user if the value that we are saving differs
// from the one they specified on the command line.
//
- if (auto gval = (*global_scope)[var])
+ auto l ((*global_scope)[var]);
+ if (l.defined () && *l != val)
{
- if (pval == nullptr || !pval->compare (gval.as<const value&> ()))
- warn << "variable " << var.name << " configured value "
- << "differs from command line value" <<
- info << "reconfigure the project to use command line value";
+ warn << "variable " << var.name << " configured value "
+ << "differs from command line value" <<
+ info << "reconfigure the project to use command line value";
}
- if (pval != nullptr)
+ if (val)
{
- //@@ TODO: assuming list
- //
- const list_value& lv (dynamic_cast<const list_value&> (*pval));
-
- ofs << var.name << " = " << lv << endl;
- //text << var.name << " = " << lv;
+ ofs << var.name << " = " << val.data_ << endl;
+ //text << var.name << " = " << val.data_;
}
else
{
@@ -171,14 +167,12 @@ namespace build
// Configure subprojects that have been loaded.
//
- if (auto v = root.vars["subprojects"])
+ if (auto l = root.vars["subprojects"])
{
- for (const name& n: v.as<const list_value&> ())
+ for (auto p: as<subprojects> (*l))
{
- if (n.pair != '\0')
- continue; // Skip project names.
-
- dir_path out_nroot (out_root / n.dir);
+ 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
@@ -271,30 +265,27 @@ namespace build
// Disfigure subprojects. Since we don't load buildfiles during
// disfigure, we do it for all known subprojects.
//
- if (auto v = root.vars["subprojects"])
+ if (auto l = root.vars["subprojects"])
{
- for (const name& n: v.as<const list_value&> ())
+ for (auto p: as<subprojects> (*l))
{
- if (n.pair != '\0')
- continue; // Skip project names.
+ const dir_path& pd (p.second);
// Create and bootstrap subproject's root scope.
//
- dir_path out_nroot (out_root / n.dir);
+ 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);
- auto val (nroot.assign ("src_root"));
+ value& val (nroot.assign ("src_root"));
if (!val)
- val = is_src_root (out_nroot)
- ? out_nroot
- : (src_root / n.dir);
+ val = is_src_root (out_nroot) ? out_nroot : (src_root / pd);
- nroot.src_path_ = &val.as<const dir_path&> ();
+ nroot.src_path_ = &as<dir_path> (val);
bootstrap_src (nroot);
@@ -304,9 +295,9 @@ namespace build
// which means there could be empty parent directories left
// behind. Clean them up.
//
- if (!n.dir.simple () && out_root != src_root)
+ if (!pd.simple () && out_root != src_root)
{
- for (dir_path d (n.dir.directory ());
+ for (dir_path d (pd.directory ());
!d.empty ();
d = d.directory ())
{
diff --git a/build/config/utility b/build/config/utility
index ef3ceed..82f71fe 100644
--- a/build/config/utility
+++ b/build/config/utility
@@ -6,15 +6,16 @@
#define BUILD_CONFIG_UTILITY
#include <string>
-#include <utility> // pair
+#include <utility> // pair
+#include <functional> // reference_wrapper
#include <build/types>
+#include <build/variable>
#include <build/diagnostics>
namespace build
{
class scope;
- class list_value;
namespace config
{
@@ -24,35 +25,36 @@ namespace build
// whether the variable has actually been set.
//
template <typename T>
- std::pair<const T&, bool>
- required (scope& root, const char* name, const T& default_value);
+ std::pair<std::reference_wrapper<const value>, bool>
+ required (scope& root, const variable&, const T& default_value);
template <typename T>
- inline std::pair<const T&, bool>
+ inline std::pair<std::reference_wrapper<const value>, bool>
required (scope& root, const std::string& name, const T& default_value)
{
- return required<T> (root, name.c_str (), default_value);
+ return required (root, variable_pool.find (name), default_value);
}
- std::pair<const std::string&, bool>
- required (scope& root, const char* name, const char* default_value);
+ inline std::pair<std::reference_wrapper<const value>, bool>
+ required (scope& root, const std::string& name, const char* default_value)
+ {
+ return required (root, name, std::string (default_value));
+ }
// 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 pointer to the value, which can be NULL.
+ // Return the value, which can be NULL.
//
- template <typename T>
- const T*
- optional (scope& root, const char* name);
+ const value&
+ optional (scope& root, const variable& var);
- template <typename T>
- inline const T*
+ inline const value&
optional (scope& root, const std::string& name)
{
- return optional<T> (root, name.c_str ());
+ return optional (root, variable_pool.find (name));
}
// Check whether there are any variables specified from the
@@ -60,8 +62,8 @@ namespace build
// 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 config.build. We call it omitted/delayed
- // configuration.
+ // values to the config.build file . We call it omitted/
+ // delayed configuration.
//
bool
specified (scope& root, const std::string& ns);
@@ -70,19 +72,20 @@ namespace build
//
// Add all the values from a variable to the C-string list. T is
- // either target or scope.
+ // 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 list value directly. Variable name is for
- // diagnostics.
+ // As above but from the strings value directly.
//
void
- append_options (cstrings& args, const list_value&, const char* var);
+ append_options (cstrings& args, const const_strings_value&);
- // Check if a specified option is present. T is either target or scope.
+ // Check if a specified option is present in the variable value.
+ // T is either target or scope.
//
template <typename T>
bool
@@ -91,5 +94,6 @@ namespace build
}
#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
index e2afc80..ce723fe 100644
--- a/build/config/utility.cxx
+++ b/build/config/utility.cxx
@@ -10,32 +10,14 @@ namespace build
{
namespace config
{
- // The same as the template except it is a bit more efficient
- // when it comes to not creating the default value string
- // unnecessarily.
- //
- pair<const string&, bool>
- required (scope& root, const char* name, const char* def_value)
+ const value&
+ optional (scope& root, const variable& var)
{
- string r;
- const variable& var (variable_pool.find (name));
+ auto l (root[var]);
- if (auto v = root[var])
- {
- const string& s (v.as<const string&> ());
-
- if (!v.belongs (*global_scope)) // A value from (some) config.build.
- return pair<const string&, bool> (s, false);
-
- r = s;
- }
- else
- r = def_value;
-
- auto v (root.assign (var));
- v = move (r);
-
- return pair<const string&, bool> (v.as<const string&> (), true);
+ return l.defined ()
+ ? l.belongs (*global_scope) ? (root.assign (var) = *l) : *l
+ : root.assign (var); // NULL
}
bool
@@ -54,17 +36,14 @@ namespace build
}
void
- append_options (cstrings& args, const list_value& lv, const char* var)
+ append_options (cstrings& args, const const_strings_value& sv)
{
- for (const name& n: lv)
+ if (!sv.empty ())
{
- if (n.simple ())
- args.push_back (n.value.c_str ());
- else if (n.directory ())
- args.push_back (n.dir.string ().c_str ());
- else
- fail << "expected option instead of " << n <<
- info << "in variable " << var;
+ 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
new file mode 100644
index 0000000..4e32119
--- /dev/null
+++ b/build/config/utility.ixx
@@ -0,0 +1,17 @@
+// 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
index cffdecf..943d308 100644
--- a/build/config/utility.txx
+++ b/build/config/utility.txx
@@ -2,79 +2,37 @@
// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-#include <utility> // move()
-
#include <build/scope>
-#include <build/variable>
namespace build
{
namespace config
{
template <typename T>
- std::pair<const T&, bool>
- required (scope& root, const char* name, const T& def_value)
- {
- T r;
- const variable& var (variable_pool.find (name));
-
- if (auto v = root[var])
- {
- const T& s (v.as<const T&> ());
-
- if (!v.belongs (*global_scope)) // A value from (some) config.build.
- return std::pair<const T&, bool> (s, false);
-
- r = s;
- }
- else
- r = def_value;
-
- auto v (root.assign (var));
- v = std::move (r);
-
- return std::pair<const T&, bool> (v.as<const T&> (), true);
- }
-
- template <typename T>
- const T*
- optional (scope& root, const char* name)
+ std::pair<std::reference_wrapper<const value>, bool>
+ required (scope& root, const variable& var, const T& def_value)
{
- const T* r (nullptr);
- const variable& var (variable_pool.find (name));
+ using result = std::pair<std::reference_wrapper<const value>, bool>;
- auto v (root[var]);
-
- if (v.defined ())
+ if (auto l = root[var])
{
- if (v.belongs (*global_scope))
- root.assign (var) = v;
-
- r = v.null () ? nullptr : &v.as<const T&> ();
+ return l.belongs (*global_scope)
+ ? result (root.assign (var) = *l, true)
+ : result (*l, false);
}
else
- root.assign (var) = nullptr;
-
- return r;
- }
-
- template <typename T>
- void
- append_options (cstrings& args, T& s, const char* var)
- {
- if (auto val = s[var])
- append_options (args, val.template as<const list_value&> (), var);
+ return result (root.assign (var) = def_value, true);
}
template <typename T>
bool
find_option (const char* option, T& s, const char* var)
{
- if (auto val = s[var])
+ if (auto l = s[var])
{
- for (const name& n: val.template as<const list_value&> ())
+ for (const std::string& s: as<strings> (*l))
{
- if (n.simple () && n.value == option)
+ if (s == option)
return true;
}
}
diff --git a/build/context.cxx b/build/context.cxx
index 259c6ea..530ac7a 100644
--- a/build/context.cxx
+++ b/build/context.cxx
@@ -57,7 +57,20 @@ namespace build
// Enter builtin variables.
//
- variable_pool.insert (variable ("subprojects", '='));
+ variable_pool.find ("work", dir_path_type);
+ variable_pool.find ("home", dir_path_type);
+
+ variable_pool.find ("src_root", dir_path_type);
+ variable_pool.find ("out_root", dir_path_type);
+ variable_pool.find ("src_base", dir_path_type);
+ variable_pool.find ("out_base", dir_path_type);
+
+ variable_pool.find ("project", string_type);
+ variable_pool.find ("amalgamation", dir_path_type);
+
+ // Shouldn't be typed since the value requires pre-processing.
+ //
+ variable_pool.find ("subprojects", nullptr, '=');
// Create global scope. For Win32 we use the empty path since there
// is no "real" root path. On POSIX, however, this is a real path.
diff --git a/build/cxx/compile.cxx b/build/cxx/compile.cxx
index 8dcad1e..65806ed 100644
--- a/build/cxx/compile.cxx
+++ b/build/cxx/compile.cxx
@@ -166,23 +166,23 @@ namespace build
{
ext_map m;
- if (auto val = r["h.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &h::static_type;
+ if (auto l = r["h.ext"])
+ m[&extension_pool.find (as<string> (*l))] = &h::static_type;
- if (auto val = r["c.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &c::static_type;
+ if (auto l = r["c.ext"])
+ m[&extension_pool.find (as<string> (*l))] = &c::static_type;
- if (auto val = r["hxx.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &hxx::static_type;
+ if (auto l = r["hxx.ext"])
+ m[&extension_pool.find (as<string> (*l))] = &hxx::static_type;
- if (auto val = r["ixx.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &ixx::static_type;
+ if (auto l = r["ixx.ext"])
+ m[&extension_pool.find (as<string> (*l))] = &ixx::static_type;
- if (auto val = r["txx.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &txx::static_type;
+ if (auto l = r["txx.ext"])
+ m[&extension_pool.find (as<string> (*l))] = &txx::static_type;
- if (auto val = r["cxx.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &cxx::static_type;
+ if (auto l = r["cxx.ext"])
+ m[&extension_pool.find (as<string> (*l))] = &cxx::static_type;
return m;
}
@@ -215,26 +215,24 @@ namespace build
const dir_path& out_base (t.dir);
const dir_path& out_root (rs->path ());
- if (auto val = t[var])
+ if (auto l = t[var])
{
- const list_value& l (val.template as<const list_value&> ());
+ const auto& v (as<strings> (*l));
- // Assume the names have already been vetted by append_options().
- //
- for (auto i (l.begin ()), e (l.end ()); i != e; ++i)
+ for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
{
- // -I can either be in the -Ifoo or -I foo form.
+ // -I can either be in the "-Ifoo" or "-I foo" form.
//
dir_path d;
- if (i->value == "-I")
+ if (*i == "-I")
{
if (++i == e)
break; // Let the compiler complain.
- d = i->simple () ? dir_path (i->value) : i->dir;
+ d = dir_path (*i);
}
- else if (i->value.compare (0, 2, "-I") == 0)
- d = dir_path (i->value, 2, string::npos);
+ else if (i->compare (0, 2, "-I") == 0)
+ d = dir_path (*i, 2, string::npos);
else
continue;
@@ -368,7 +366,7 @@ namespace build
tracer trace ("cxx::compile::inject_prerequisites");
scope& rs (t.root_scope ());
- const string& cxx (rs["config.cxx"].as<const string&> ());
+ const string& cxx (as<string> (*rs["config.cxx"]));
cstrings args {cxx.c_str ()};
@@ -715,7 +713,7 @@ namespace build
path rels (relative (s->path ()));
scope& rs (t.root_scope ());
- const string& cxx (rs["config.cxx"].as<const string&> ());
+ const string& cxx (as<string> (*rs["config.cxx"]));
cstrings args {cxx.c_str ()};
diff --git a/build/cxx/link.cxx b/build/cxx/link.cxx
index 97c9696..1b900c2 100644
--- a/build/cxx/link.cxx
+++ b/build/cxx/link.cxx
@@ -59,10 +59,10 @@ namespace build
case type::so: var = "bin.libso.lib"; break;
}
- const list_value& lv (t[var].as<const list_value&> ());
- return lv[0].value == "shared"
- ? lv.size () > 1 && lv[1].value == "static" ? order::so_a : order::so
- : lv.size () > 1 && lv[1].value == "shared" ? order::a_so : order::a;
+ 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;
}
link::search_paths link::
@@ -73,32 +73,24 @@ namespace build
// Extract user-supplied search paths (i.e., -L).
//
- if (auto val = bs["cxx.loptions"])
+ if (auto l = bs["cxx.loptions"])
{
- const list_value& l (val.as<const list_value&> ());
+ const auto& v (as<strings> (*l));
- for (auto i (l.begin ()), e (l.end ()); i != e; ++i)
+ for (auto i (v.begin ()), e (v.end ()); i != e; ++i)
{
- if (!i->simple ())
- continue;
-
- // -L can either be in the -Lfoo or -L foo form.
+ // -L can either be in the "-Lfoo" or "-L foo" form.
//
dir_path d;
- if (i->value == "-L")
+ if (*i == "-L")
{
if (++i == e)
break; // Let the compiler complain.
- if (i->simple ())
- d = dir_path (i->value);
- else if (i->directory ())
- d = i->dir;
- else
- break; // Let the compiler complain.
+ d = dir_path (*i);
}
- else if (i->value.compare (0, 2, "-L") == 0)
- d = dir_path (i->value, 2, string::npos);
+ else if (i->compare (0, 2, "-L") == 0)
+ d = dir_path (*i, 2, string::npos);
else
continue;
@@ -114,7 +106,7 @@ namespace build
cstrings args;
string std_storage;
- args.push_back (rs["config.cxx"].as<const string&> ().c_str ());
+ 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");
@@ -550,7 +542,7 @@ namespace build
// Determine the library type to link.
//
bool lso (true);
- const string& at ((*l)["bin.lib"].as<const string&> ());
+ const string& at (as<string> (*(*l)["bin.lib"]));
if (!lo)
lo = link_order (t);
@@ -776,10 +768,8 @@ namespace build
}
else
{
- args.push_back (rs["config.cxx"].as<const string&> ().c_str ());
-
+ args.push_back (as<string> (*rs["config.cxx"]).c_str ());
append_options (args, t, "cxx.coptions");
-
append_std (args, t, storage1);
if (so)
diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx
index 042fb53..7f200cd 100644
--- a/build/cxx/module.cxx
+++ b/build/cxx/module.cxx
@@ -91,6 +91,43 @@ namespace build
rs.insert<libso> (install_id, "cxx.install", install::instance);
}
+ // 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)
+ {
+ variable_pool.find ("config.cxx", string_type); //@@ VAR type
+
+ variable_pool.find ("config.cxx.poptions", strings_type);
+ variable_pool.find ("config.cxx.coptions", strings_type);
+ variable_pool.find ("config.cxx.loptions", strings_type);
+ variable_pool.find ("config.cxx.libs", strings_type);
+
+ variable_pool.find ("cxx.poptions", strings_type);
+ variable_pool.find ("cxx.coptions", strings_type);
+ variable_pool.find ("cxx.loptions", strings_type);
+ variable_pool.find ("cxx.libs", strings_type);
+
+ variable_pool.find ("cxx.export.poptions", strings_type);
+ variable_pool.find ("cxx.export.coptions", strings_type);
+ variable_pool.find ("cxx.export.loptions", strings_type);
+ variable_pool.find ("cxx.export.libs", strings_type);
+
+ variable_pool.find ("cxx.std", string_type);
+
+ variable_pool.find ("h.ext", string_type);
+ variable_pool.find ("c.ext", string_type);
+ variable_pool.find ("hxx.ext", string_type);
+ variable_pool.find ("ixx.ext", string_type);
+ variable_pool.find ("txx.ext", string_type);
+ variable_pool.find ("cxx.ext", string_type);
+ }
+
// Configure.
//
@@ -104,7 +141,7 @@ namespace build
//
if (p.second)
{
- const string& cxx (p.first);
+ const string& cxx (as<string> (p.first));
const char* args[] = {cxx.c_str (), "-dumpversion", nullptr};
if (verb)
@@ -157,27 +194,27 @@ namespace build
// using cxx
// cxx.coptions += <overriding options> # Note: '+='.
//
- if (auto* v = config::optional<list_value> (r, "config.cxx.poptions"))
- b.assign ("cxx.poptions") += *v;
+ if (const value& v = config::optional (r, "config.cxx.poptions"))
+ b.assign ("cxx.poptions") += as<strings> (v);
- if (auto* v = config::optional<list_value> (r, "config.cxx.coptions"))
- b.assign ("cxx.coptions") += *v;
+ if (const value& v = config::optional (r, "config.cxx.coptions"))
+ b.assign ("cxx.coptions") += as<strings> (v);
- if (auto* v = config::optional<list_value> (r, "config.cxx.loptions"))
- b.assign ("cxx.loptions") += *v;
+ if (const value& v = config::optional (r, "config.cxx.loptions"))
+ b.assign ("cxx.loptions") += as<strings> (v);
- if (auto* v = config::optional<list_value> (r, "config.cxx.libs"))
- b.assign ("cxx.libs") += *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, "include"); // Install into install.include.
- path<ixx> (b, "include");
- path<txx> (b, "include");
- path<h> (b, "include");
+ 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"));
}
}
}
diff --git a/build/cxx/utility.txx b/build/cxx/utility.txx
index 7d9d686..561a54e 100644
--- a/build/cxx/utility.txx
+++ b/build/cxx/utility.txx
@@ -12,9 +12,9 @@ namespace build
void
append_std (cstrings& args, T& t, std::string& s)
{
- if (auto val = t["cxx.std"])
+ if (auto l = t["cxx.std"])
{
- const std::string& v (val.template as<const string&> ());
+ const std::string& v (as<string> (*l));
// Translate 11 to 0x and 14 to 1y for compatibility with
// older versions of the compiler.
diff --git a/build/dump.cxx b/build/dump.cxx
index e426f0b..f3b8c4b 100644
--- a/build/dump.cxx
+++ b/build/dump.cxx
@@ -19,18 +19,14 @@ using namespace std;
namespace build
{
static void
- dump_variable (ostream& os, const variable& var, const value_ptr& val)
+ dump_variable (ostream& os, const variable& var, const value& val)
{
os << var.name << " = ";
- if (val == nullptr)
+ if (val.null ())
os << "[null]";
else
- {
- //@@ TODO: assuming it is a list.
- //
- os << dynamic_cast<list_value&> (*val);
- }
+ os << val.data_;
}
static void
diff --git a/build/file b/build/file
index 8a5f9fa..64d8ad2 100644
--- a/build/file
+++ b/build/file
@@ -5,6 +5,9 @@
#ifndef BUILD_FILE
#define BUILD_FILE
+#include <map>
+#include <string>
+
#include <build/types>
#include <build/variable> // list_value
@@ -15,6 +18,8 @@ namespace build
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
@@ -114,7 +119,7 @@ namespace build
// there is a package foo available in repository bar. Wanna
// download and use it?"
//
- list_value
+ names
import (scope& base, name, const location&);
target&
diff --git a/build/file.cxx b/build/file.cxx
index e5deecf..b303740 100644
--- a/build/file.cxx
+++ b/build/file.cxx
@@ -4,7 +4,6 @@
#include <build/file>
-#include <map>
#include <fstream>
#include <utility> // move()
@@ -135,13 +134,13 @@ namespace build
// consistent.
//
{
- auto v (rs.assign ("out_root"));
+ value& v (rs.assign ("out_root"));
if (!v)
v = out_root;
else
{
- const dir_path& p (v.as<const dir_path&> ());
+ const dir_path& p (as<dir_path> (v));
if (p != out_root)
fail << "new out_root " << out_root << " does not match "
@@ -151,13 +150,13 @@ namespace build
if (!src_root.empty ())
{
- auto v (rs.assign ("src_root"));
+ value& v (rs.assign ("src_root"));
if (!v)
v = src_root;
else
{
- const dir_path& p (v.as<const dir_path&> ());
+ const dir_path& p (as<dir_path> (v));
if (p != src_root)
fail << "new src_root " << src_root << " does not match "
@@ -188,7 +187,7 @@ namespace build
// 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_ptr
+ static value
extract_variable (const path& bf, const char* var)
{
ifstream ifs (bf.string ());
@@ -201,34 +200,32 @@ namespace build
{
path rbf (diag_relative (bf));
- lexer l (ifs, rbf.string ());
- token t (l.next ());
+ lexer lex (ifs, rbf.string ());
+ token t (lex.next ());
token_type tt;
if (t.type () != token_type::name || t.name () != var ||
- ((tt = l.next ().type ()) != token_type::equal &&
+ ((tt = lex.next ().type ()) != token_type::equal &&
tt != token_type::plus_equal))
fail << "variable '" << var << "' expected as first line in " << rbf;
parser p;
temp_scope tmp (*global_scope);
- p.parse_variable (l, tmp, t.name (), tt);
+ p.parse_variable (lex, tmp, t.name (), tt);
- auto val (tmp.vars[var]);
- assert (val.defined ());
- value_ptr& vp (val);
- return move (vp); // Steal the value, the scope is going away.
+ auto l (tmp.vars[var]);
+ assert (l.defined ());
+ value& v (*l);
+ return move (v); // Steal the value, the scope is going away.
}
catch (const std::ios_base::failure&)
{
fail << "failed to read from " << bf;
}
- return nullptr;
+ return value (); // Never reaches.
}
- using subprojects = map<string, dir_path>;
-
// Extract the project name from bootstrap.build.
//
static string
@@ -243,7 +240,7 @@ namespace build
// have to discover its src_root.
//
const dir_path* src_root;
- value_ptr src_root_vp; // Need it to live until the end.
+ 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;
@@ -255,9 +252,8 @@ namespace build
src_root = &fallback_src_root;
else
{
- src_root_vp = extract_variable (f, "src_root");
- value_proxy v (&src_root_vp, nullptr); // Read-only.
- src_root = &v.as<const dir_path&> ();
+ src_root_v = extract_variable (f, "src_root");
+ src_root = &as<dir_path> (src_root_v);
level4 ([&]{trace << "extracted src_root " << *src_root << " for "
<< out_root;});
}
@@ -265,9 +261,8 @@ namespace build
string name;
{
- value_ptr vp (extract_variable (*src_root / bootstrap_file, "project"));
- value_proxy v (&vp, nullptr); // Read-only.
- name = move (v.as<string&> ());
+ value v (extract_variable (*src_root / bootstrap_file, "project"));
+ name = move (as<string> (v));
}
level4 ([&]{trace << "extracted project name " << name << " for "
@@ -367,11 +362,11 @@ namespace build
// amalgamated.
//
{
- auto rp (root.vars.assign("amalgamation")); // Set NULL by default.
- auto& val (rp.first);
+ auto rp (root.vars.assign ("amalgamation")); // Set NULL by default.
+ value& v (rp.first);
- if (!val.null () && val.empty ()) // Convert empty to NULL.
- val = nullptr;
+ if (v && v.empty ()) // Convert empty to NULL.
+ v = nullptr;
if (scope* aroot = root.parent_scope ()->root_scope ())
{
@@ -383,14 +378,14 @@ namespace build
//
if (!rp.second)
{
- if (val.null ())
+ if (!v)
{
fail << out_root << " cannot be amalgamated" <<
info << "amalgamated by " << ad;
}
else
{
- const dir_path& vd (val.as<const dir_path&> ());
+ const dir_path& vd (as<dir_path> (v));
if (vd != rd)
{
@@ -405,7 +400,7 @@ namespace build
// Otherwise, use the outer root as our amalgamation.
//
level4 ([&]{trace << out_root << " amalgamated as " << rd;});
- val = move (rd);
+ v = move (rd);
}
}
else if (rp.second)
@@ -421,7 +416,7 @@ namespace build
{
dir_path rd (ad.relative (out_root));
level4 ([&]{trace << out_root << " amalgamated as " << rd;});
- val = move (rd);
+ v = move (rd);
}
}
}
@@ -439,8 +434,9 @@ namespace build
// the NULL value indicates that we found no subprojects.
//
{
- auto rp (root.vars.assign("subprojects")); // Set NULL by default.
- auto& val (rp.first);
+ const variable& var (variable_pool.find ("subprojects"));
+ auto rp (root.vars.assign(var)); // Set NULL by default.
+ value& v (rp.first);
if (rp.second)
{
@@ -459,34 +455,21 @@ namespace build
if (out_root != src_root)
find_subprojects (sps, src_root, src_root, false);
- // Transform our map to list_value.
- //
- if (!sps.empty ())
- {
- list_value_ptr vp (new list_value);
- for (auto& p: sps)
- {
- vp->emplace_back (p.first);
- vp->back ().pair = '=';
- vp->emplace_back (move (p.second));
- }
- val = move (vp);
- }
+ if (!sps.empty ()) // Keep it NULL if no subprojects.
+ v = move (sps);
}
- else if (!val.null ())
+ else if (v)
{
// Convert empty to NULL.
//
- if (val.empty ())
- val = nullptr;
+ if (v.empty ())
+ v = nullptr;
else
{
- // Scan the value and convert it to the "canonical" form,
- // that is, a list of dir=simple pairs.
+ // Pre-scan the value and convert it to the "canonical" form,
+ // that is, a list of simple=dir pairs.
//
- list_value& lv (val.as<list_value&> ());
-
- for (auto i (lv.begin ()); i != lv.end (); ++i)
+ for (auto i (v.data_.begin ()); i != v.data_.end (); ++i)
{
bool p (i->pair != '\0');
@@ -494,25 +477,19 @@ namespace build
{
// Project name.
//
- if (!i->simple () || i->empty ())
+ 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.
}
- name& n (*i);
-
- if (n.simple ())
- {
- n.dir = dir_path (move (n.value));
- n.value.clear ();
- }
-
- if (!n.directory ())
- fail << "expected directory instead of '" << n << "' in the "
+ 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)
@@ -521,12 +498,18 @@ namespace build
// was specified by the user so it is most likely in our
// src.
//
- i = lv.emplace (i, find_project_name (out_root / n.dir,
- src_root / n.dir));
+ i = v.data_.emplace (
+ i,
+ find_project_name (out_root / d, src_root / d));
+
i->pair = '=';
++i;
}
}
+
+ // Make it of the map type.
+ //
+ assign<subprojects> (v, var);
}
}
}
@@ -537,12 +520,12 @@ namespace build
void
create_bootstrap_outer (scope& root)
{
- auto v (root.vars["amalgamation"]);
+ auto l (root.vars["amalgamation"]);
- if (!v)
+ if (!l)
return;
- const dir_path& d (v.as<const dir_path&> ());
+ const dir_path& d (as<dir_path> (*l));
dir_path out_root (root.path () / d);
out_root.normalize ();
@@ -560,21 +543,21 @@ namespace build
scope& rs (create_root (out_root, dir_path ()));
bootstrap_out (rs); // #3 happens here, if at all.
- auto val (rs.assign ("src_root"));
+ value& v (rs.assign ("src_root"));
- if (!val)
+ if (!v)
{
if (is_src_root (out_root)) // #2
- val = out_root;
+ v = out_root;
else // #1
{
dir_path src_root (root.src_path () / d);
src_root.normalize ();
- val = move (src_root);
+ v = move (src_root);
}
}
- rs.src_path_ = &val.as<const dir_path&> ();
+ rs.src_path_ = &as<dir_path> (v);
bootstrap_src (rs);
create_bootstrap_outer (rs);
@@ -588,9 +571,9 @@ namespace build
scope&
create_bootstrap_inner (scope& root, const dir_path& out_base)
{
- if (auto v = root.vars["subprojects"])
+ if (auto l = root.vars["subprojects"])
{
- for (const name& n: v.as<const list_value&> ())
+ for (const name& n: *l)
{
if (n.pair != '\0')
continue; // Skip project names.
@@ -605,14 +588,14 @@ namespace build
scope& rs (create_root (out_root, dir_path ()));
bootstrap_out (rs);
- auto val (rs.assign ("src_root"));
+ value& v (rs.assign ("src_root"));
- if (!val)
- val = is_src_root (out_root)
+ if (!v)
+ v = is_src_root (out_root)
? out_root
: (root.src_path () / n.dir);
- rs.src_path_ = &val.as<const dir_path&> ();
+ rs.src_path_ = &as<dir_path> (v);
bootstrap_src (rs);
@@ -646,8 +629,8 @@ namespace build
source_once (bf, root, root);
}
- list_value
- import (scope& ibase, name target, const location& l)
+ names
+ import (scope& ibase, name target, const location& loc)
{
tracer trace ("import");
@@ -659,7 +642,7 @@ namespace build
if (target.unqualified ())
{
target.proj = &project_name_pool.find ("");
- return list_value (move (target));
+ return names {move (target)};
}
// Otherwise, get the project name and convert the target to
@@ -680,14 +663,16 @@ namespace build
//
for (scope* r (&iroot);; r = r->parent_scope ()->root_scope ())
{
- if (auto v = r->vars["subprojects"])
+ if (auto l = r->vars["subprojects"])
{
- // @@ Map sure would have been handy.
- //
- if (const name* n = v.as<const list_value&> ().find_pair (project))
+ const auto& m (as<subprojects> (*l));
+ auto i (m.find (project));
+
+ if (i != m.end ())
{
- out_root = r->path () / n->dir;
- fallback_src_root = r->src_path () / n->dir;
+ const dir_path& d ((*i).second);
+ out_root = r->path () / d;
+ fallback_src_root = r->src_path () / d;
break;
}
}
@@ -700,42 +685,34 @@ namespace build
//
if (out_root.empty ())
{
- string var ("config.import." + project);
+ const variable& var (
+ variable_pool.find ("config.import." + project,
+ dir_path_type));
- if (auto v = iroot[var])
+ if (auto l = iroot[var])
{
- if (!v.belongs (*global_scope)) // A value from (some) config.build.
- out_root = v.as<const dir_path&> ();
- else
+ 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. Also,
- // for usability's sake, treat a simple name that doesn't end
- // with '/' as a directory.
+ // Process the path by making it absolute and normalized.
//
- list_value& lv (v.as<list_value&> ());
-
- if (lv.size () != 1 || lv[0].empty () || lv[0].typed ())
- fail (l) << "invalid " << var << " value " << lv;
-
- name& n (lv[0]);
-
- if (n.directory ())
- out_root = n.dir;
- else
- out_root = dir_path (n.value);
-
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.
+ // value mismatch. Not very clean.
//
- if (n.dir != out_root)
- n = name (out_root);
+ dir_path& d (as<dir_path> (const_cast<value&> (*l)));
+ if (d != out_root)
+ d = out_root;
}
}
else
@@ -746,7 +723,7 @@ namespace build
//
target.proj = &project;
level4 ([&]{trace << "postponing " << target;});
- return list_value (move (target));
+ return names {move (target)};
}
}
@@ -761,13 +738,13 @@ namespace build
// Check that the bootstrap process set src_root.
//
- if (auto v = root.vars["src_root"])
+ if (auto l = root.vars["src_root"])
{
- const dir_path& p (v.as<const dir_path&> ());
+ const dir_path& p (as<dir_path> (*l));
if (!src_root.empty () && p != src_root)
- fail (l) << "bootstrapped src_root " << p << " does not match "
- << "discovered " << src_root;
+ fail (loc) << "bootstrapped src_root " << p << " does not match "
+ << "discovered " << src_root;
root.src_path_ = &p;
}
@@ -775,12 +752,12 @@ namespace build
//
else if (!fallback_src_root.empty ())
{
- auto v (root.assign ("src_root"));
+ value& v (root.assign ("src_root"));
v = move (fallback_src_root);
- root.src_path_ = &v.as<const dir_path&> ();
+ root.src_path_ = &as<dir_path> (v);
}
else
- fail (l) << "unable to determine src_root for imported " << project <<
+ fail (loc) << "unable to determine src_root for imported " << project <<
info << "consider configuring " << out_root;
bootstrap_src (root);
@@ -807,10 +784,10 @@ namespace build
// Also pass the target being imported.
//
{
- auto v (ts.assign ("target"));
+ value& v (ts.assign ("target"));
if (!target.empty ()) // Otherwise leave NULL.
- v = list_value {move (target)};
+ v = move (target);
}
// Load the export stub. Note that it is loaded in the context
@@ -821,7 +798,7 @@ namespace build
path es (root.src_path () / path ("build/export.build"));
ifstream ifs (es.string ());
if (!ifs.is_open ())
- fail (l) << "unable to open " << es;
+ fail (loc) << "unable to open " << es;
level4 ([&]{trace << "importing " << es;});
@@ -830,18 +807,18 @@ namespace build
try
{
- p.parse_buildfile (ifs, es, iroot, ts);
+ // @@ Should we verify these are all unqualified names? Or maybe
+ // there is a use-case for the export stub to return a qualified
+ // name?
+ //
+ return p.parse_export_stub (ifs, es, iroot, ts);
}
catch (const std::ios_base::failure&)
{
- fail (l) << "failed to read from " << es;
+ fail (loc) << "failed to read from " << 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?
- //
- return p.export_value ();
+ return names (); // Never reached.
}
target&
diff --git a/build/install/module.cxx b/build/install/module.cxx
index 834b0e8..610154a 100644
--- a/build/install/module.cxx
+++ b/build/install/module.cxx
@@ -29,54 +29,64 @@ namespace build
// to set all the install.* values to defaults, as if we had the
// default configuration.
//
+ template <typename T>
static void
- set_dir (bool spec,
+ set_var (bool spec,
scope& r,
const char* name,
- const char* path,
- const char* mode = nullptr,
- const char* dir_mode = nullptr,
- const char* cmd = nullptr,
- const char* options = nullptr)
+ const char* var,
+ const T* dv)
{
- auto set = [spec, &r, name] (const char* var, const char* dv)
- {
- string vn;
- const list_value* lv (nullptr);
-
- if (spec)
- {
- vn = "config.install.";
- vn += name;
- vn += var;
-
- lv = dv != nullptr
- ? &config::required (r, vn, list_value (dv)).first
- : config::optional<list_value> (r, vn);
- }
-
- vn = "install.";
- vn += name;
- vn += var;
- auto v (r.assign (vn));
-
- if (spec)
- {
- if (lv != nullptr && !lv->empty ())
- v = *lv;
- }
- else
- {
- if (dv != nullptr)
- v = dv;
- }
- };
-
- set ("", path);
- set (".mode", mode);
- set (".dir_mode", dir_mode);
- set (".cmd", cmd);
- set (".options", options);
+ string vn;
+ const value* cv (nullptr);
+
+ if (spec)
+ {
+ vn = "config.install.";
+ vn += name;
+ vn += var;
+ const variable& vr (
+ variable_pool.find (move (vn), &value_traits<T>::value_type));
+
+ cv = dv != nullptr
+ ? &config::required (r, vr, *dv).first.get ()
+ : &config::optional (r, vr);
+ }
+
+ vn = "install.";
+ vn += name;
+ vn += var;
+ const variable& vr (
+ variable_pool.find (move (vn), &value_traits<T>::value_type));
+
+ value& v (r.assign (vr));
+
+ if (spec)
+ {
+ if (cv != nullptr && *cv && !cv->empty ())
+ v = *cv;
+ }
+ else
+ {
+ if (dv != nullptr)
+ v = *dv;
+ }
+ }
+
+ static void
+ set_dir (bool s,
+ scope& r,
+ const char* name,
+ const dir_path& path,
+ const string& fmode = string (),
+ const string& dmode = string (),
+ const string& cmd = string ())
+ {
+ set_var (s, r, name, "", path.empty () ? nullptr : &path);
+ set_var (s, r, name, ".mode", fmode.empty () ? nullptr : &fmode);
+ set_var (s, r, name, ".dir_mode", dmode.empty () ? nullptr : &dmode);
+ set_var (s, r, name, ".cmd", cmd.empty () ? nullptr : &cmd);
+ set_var<strings> (s, r, name, ".options", nullptr);
}
static rule rule_;
@@ -120,6 +130,15 @@ namespace build
rs.insert<file> (install_id, "install", rule_);
}
+ // Enter module variables.
+ //
+ // Note that the set_dir() calls below enter some more.
+ //
+ if (first)
+ {
+ variable_pool.find ("install", dir_path_type);
+ }
+
// Configuration.
//
// Note that we don't use any defaults for root -- the location
@@ -129,31 +148,31 @@ namespace build
if (first)
{
bool s (config::specified (r, "config.install"));
- const string& n (r["project"].as<const string&> ());
+ const string& n (as<string> (*r["project"]));
- set_dir (s, r, "root", nullptr, nullptr, "755", "install");
- set_dir (s, r, "data_root", "root", "644");
- set_dir (s, r, "exec_root", "root", "755");
+ set_dir (s, r, "root", dir_path (), "", "755", "install");
+ set_dir (s, r, "data_root", dir_path ("root"), "644");
+ set_dir (s, r, "exec_root", dir_path ("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).c_str ());
+ set_dir (s, r, "sbin", dir_path ("exec_root/sbin"));
+ set_dir (s, r, "bin", dir_path ("exec_root/bin"));
+ set_dir (s, r, "lib", dir_path ("exec_root/lib"));
+ set_dir (s, r, "libexec", dir_path ("exec_root/libexec/" + n));
- set_dir (s, r, "data", ("data_root/share/" + n).c_str ());
- set_dir (s, r, "include", "data_root/include");
+ set_dir (s, r, "data", dir_path ("data_root/share/" + n));
+ set_dir (s, r, "include", dir_path ("data_root/include"));
- set_dir (s, r, "doc", ("data_root/share/doc/" + n).c_str ());
- set_dir (s, r, "man", "data_root/share/man");
+ set_dir (s, r, "doc", dir_path ("data_root/share/doc/" + n));
+ set_dir (s, r, "man", dir_path ("data_root/share/man"));
- set_dir (s, r, "man1", "man/man1");
+ set_dir (s, r, "man1", dir_path ("man/man1"));
}
// Configure "installability" for built-in target types.
//
- path<doc> (b, "doc"); // Install into install.doc.
- path<man> (b, "man"); // Install into install.man.
- path<man> (b, "man1"); // Install into install.man1.
+ 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.
}
}
}
diff --git a/build/install/rule.cxx b/build/install/rule.cxx
index f14547c..8f67001 100644
--- a/build/install/rule.cxx
+++ b/build/install/rule.cxx
@@ -21,48 +21,23 @@ namespace build
{
namespace install
{
- // Lookup the install or install.* variable and check that
- // the value makes sense. 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.
+ // 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 name*
- lookup (T& t, const char* var)
+ static const dir_path*
+ lookup (T& t, const string& var)
{
- auto v (t[var]);
+ auto l (t[var]);
- const name* r (nullptr);
+ if (!l)
+ return nullptr;
- if (!v)
- return r;
-
- const list_value& lv (v.template as<const list_value&> ());
-
- if (lv.empty ())
- return r;
-
- if (lv.size () == 1)
- {
- const name& n (lv.front ());
-
- if (n.simple () && n.value == "false")
- return r;
-
- if (!n.empty () && (n.simple () || n.directory ()))
- return &n;
- }
-
- fail << "expected directory instead of '" << lv << "' in "
- << "the " << var << " variable";
-
- return r;
+ const dir_path& r (as<dir_path> (*l));
+ return r.simple () && r.string () == "false" ? nullptr : &r;
}
- template <typename T>
- static inline const name*
- lookup (T& t, const string& var) {return lookup (t, var.c_str ());}
-
match_result rule::
match (action a, target& t, const std::string&) const
{
@@ -185,8 +160,8 @@ namespace build
struct install_dir
{
dir_path dir;
- string cmd;
- const list_value* options {nullptr};
+ string cmd; //@@ VAR type
+ const_strings_value options {nullptr};
string mode;
string dir_mode;
};
@@ -200,8 +175,8 @@ namespace build
cstrings args {base.cmd.c_str (), "-d"};
- if (base.options != nullptr)
- config::append_options (args, *base.options, "install.*.options");
+ if (base.options.d != nullptr) //@@ VAR
+ config::append_options (args, base.options);
args.push_back ("-m");
args.push_back (base.dir_mode.c_str ());
@@ -241,8 +216,8 @@ namespace build
cstrings args {base.cmd.c_str ()};
- if (base.options != nullptr)
- config::append_options (args, *base.options, "install.*.options");
+ if (base.options.d != nullptr) //@@ VAR
+ config::append_options (args, base.options);
args.push_back ("-m");
args.push_back (base.mode.c_str ());
@@ -277,10 +252,9 @@ namespace build
// creating leading directories as necessary.
//
static install_dir
- resolve (scope& s, const name& n, const string* var = nullptr)
+ resolve (scope& s, dir_path d, const string* var = nullptr)
{
install_dir r;
- dir_path d (n.simple () ? dir_path (n.value) : n.dir);
if (d.absolute ())
{
@@ -298,11 +272,11 @@ namespace build
// as the installation directory name, e.g., bin, sbin, lib,
// etc. Look it up and recurse.
//
- const string& dn (*d.begin ());
- const string var ("install." + dn);
- if (const name* n = lookup (s, var))
+ const string& sn (*d.begin ());
+ const string var ("install." + sn);
+ if (const dir_path* dn = lookup (s, var))
{
- r = resolve (s, *n, &var);
+ r = resolve (s, *dn, &var);
d = r.dir / dir_path (++d.begin (), d.end ());
d.normalize ();
@@ -310,7 +284,7 @@ namespace build
install (r, d); // install -d
}
else
- fail << "unknown installation directory name " << dn <<
+ fail << "unknown installation directory name " << sn <<
info << "did you forget to specify config." << var << "?";
}
@@ -320,12 +294,10 @@ namespace build
//
if (var != nullptr)
{
- if (auto v = s[*var + ".cmd"]) r.cmd = v.as<const string&> ();
- if (auto v = s[*var + ".mode"]) r.mode = v.as<const string&> ();
- if (auto v = s[*var + ".dir_mode"])
- r.dir_mode = v.as<const string&> ();
- if (auto v = s[*var + ".options"])
- r.options = &v.as<const list_value&> ();
+ 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.
@@ -351,12 +323,12 @@ namespace build
//
install_dir d (
resolve (t.base_scope (),
- t["install"].as<const name&> ())); // We know it's there.
+ as<dir_path> (*t["install"]))); // We know it's there.
// Override mode if one was specified.
//
- if (auto v = t["install.mode"])
- d.mode = v.as<const string&> ();
+ 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
index 5c703fc..9bc41f1 100644
--- a/build/install/utility
+++ b/build/install/utility
@@ -5,7 +5,11 @@
#ifndef BUILD_INSTALL_UTILITY
#define BUILD_INSTALL_UTILITY
+#include <string>
+#include <utility>
+
#include <build/scope>
+#include <build/types>
namespace build
{
@@ -15,20 +19,20 @@ namespace build
//
template <typename T>
inline void
- path (scope& s, const char* v)
+ path (scope& s, dir_path d)
{
- auto p (s.target_vars[T::static_type]["*"].assign ("install"));
- if (p.second) // Already set by the user?
- p.first = v;
+ 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, const char* v)
+ mode (scope& s, std::string m)
{
- auto m (s.target_vars[T::static_type]["*"].assign ("install.mode"));
- if (m.second) // Already set by the user?
- m.first = v;
+ 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);
}
}
}
diff --git a/build/name b/build/name
index 111b661..ecf86cc 100644
--- a/build/name
+++ b/build/name
@@ -33,11 +33,11 @@ namespace build
{
name () = default;
- explicit
- name (std::string v): value (std::move (v)) {}
+ 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)) {}
+ 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)) {}
diff --git a/build/operation.cxx b/build/operation.cxx
index 607658f..a92c912 100644
--- a/build/operation.cxx
+++ b/build/operation.cxx
@@ -67,8 +67,7 @@ namespace build
scope& base (scopes[out_base]);
base.assign ("out_base") = out_base;
- auto v (base.assign ("src_base") = src_base);
- base.src_path_ = &v.as<const dir_path&> ();
+ base.src_path_ = &as<dir_path> (base.assign ("src_base") = src_base);
// Load the buildfile unless it has already been loaded.
//
diff --git a/build/parser b/build/parser
index 6eefccd..c6d275b 100644
--- a/build/parser
+++ b/build/parser
@@ -24,6 +24,8 @@ namespace build
class parser
{
public:
+ typedef build::names names_type;
+
parser (): fail (&path_) {}
// Issue diagnostics and throw failed in case of an error.
@@ -37,19 +39,16 @@ namespace build
token
parse_variable (lexer&, scope&, std::string name, token_type kind);
- list_value
- export_value ()
+ names_type
+ parse_export_stub (std::istream& is, const path& p, scope& r, scope& b)
{
- list_value r (std::move (export_value_));
- export_value_.clear (); // Empty state.
- return r;
+ parse_buildfile (is, p, r, b);
+ return std::move (export_value_);
}
// Recursive descent parser.
//
private:
- typedef build::names names_type;
-
void
clause (token&, token_type&);
@@ -139,7 +138,7 @@ namespace build
const dir_path* out_root_;
const dir_path* src_root_;
target* default_target_;
- list_value export_value_;
+ names_type export_value_;
token peek_ {token_type::eos, false, 0, 0};
bool peeked_ {false};
diff --git a/build/parser.cxx b/build/parser.cxx
index 682a720..c728cb8 100644
--- a/build/parser.cxx
+++ b/build/parser.cxx
@@ -478,8 +478,8 @@ namespace build
//
if (src_root_ == nullptr)
{
- auto v (root_->vars["src_root"]);
- src_root_ = v ? &v.as<const dir_path&> () : nullptr;
+ auto l (root_->vars["src_root"]);
+ src_root_ = l ? &as<dir_path> (*l) : nullptr;
}
}
@@ -597,8 +597,8 @@ namespace build
scope_ = &scopes[out_base];
scope_->assign ("out_base") = move (out_base);
- auto v (scope_->assign ("src_base") = move (src_base));
- scope_->src_path_ = &v.as<const dir_path&> ();
+ scope_->src_path_ = &as<dir_path> (
+ scope_->assign ("src_base") = move (src_base));
target* odt (default_target_);
default_target_ = nullptr;
@@ -641,7 +641,9 @@ namespace build
//
// import [<var>=](<project>|<project>/<target>])+
//
- value_proxy val;
+ value* val (nullptr);
+ const build::variable* var (nullptr);
+
token_type at; // Assignment type.
if (tt == type::name)
{
@@ -649,9 +651,10 @@ namespace build
if (at == token_type::equal || at == token_type::plus_equal)
{
- val.rebind (at == token_type::equal
- ? scope_->assign (t.name ())
- : scope_->append (t.name ()));
+ var = &variable_pool.find (t.name ());
+ val = at == token_type::equal
+ ? &scope_->assign (*var)
+ : &scope_->append (*var);
next (t, tt); // Consume =/+=.
next (t, tt);
}
@@ -669,14 +672,14 @@ namespace build
{
// build::import() will check the name, if required.
//
- list_value r (build::import (*scope_, move (n), l));
+ names_type r (build::import (*scope_, move (n), l));
- if (val.defined ())
+ if (val != nullptr)
{
if (at == token_type::equal)
- val = move (r);
+ val->assign (move (r), *var);
else
- val += move (r);
+ val->append (move (r), *var);
}
}
@@ -701,9 +704,8 @@ namespace build
// The rest is a value. Parse it as names to get variable expansion.
// build::import() will check the names, if required.
//
- export_value_ = list_value (tt != type::newline && tt != type::eos
- ? names (t, tt)
- : names_type ());
+ if (tt != type::newline && tt != type::eos)
+ export_value_ = names (t, tt);
if (tt == type::newline)
next (t, tt);
@@ -785,28 +787,17 @@ namespace build
: names_type ());
if (assign)
{
- auto v (target_ != nullptr
- ? target_->assign (var)
- : scope_->assign (var));
- v = list_value (move (vns));
+ value& v (target_ != nullptr
+ ? target_->assign (var)
+ : scope_->assign (var));
+ v.assign (move (vns), var);
}
else
{
- auto v (target_ != nullptr
- ? target_->append (var)
- : scope_->append (var));
-
- // More efficient than calling operator+=.
- //
- if (v)
- {
- list_value& lv (v.as<list_value&> ());
- lv.insert (lv.end (),
- make_move_iterator (vns.begin ()),
- make_move_iterator (vns.end ()));
- }
- else
- v = list_value (move (vns)); // Same as assignment.
+ value& v (target_ != nullptr
+ ? target_->append (var)
+ : scope_->append (var));
+ v.append (move (vns), var);
}
}
@@ -1031,12 +1022,12 @@ namespace build
n = t.name ();
const auto& var (variable_pool.find (move (n)));
- auto val (target_ != nullptr ? (*target_)[var] : (*scope_)[var]);
+ auto l (target_ != nullptr ? (*target_)[var] : (*scope_)[var]);
- // Undefined namespace variables are not allowed.
+ // Undefined/NULL namespace variables are not allowed.
//
- if (!val && var.name.find ('.') != string::npos)
- fail (t) << "undefined namespace variable " << var.name;
+ if (!l && var.name.find ('.') != string::npos)
+ fail (t) << "undefined/null namespace variable " << var.name;
if (paren)
{
@@ -1048,15 +1039,10 @@ namespace build
tt = peek ();
- if (!val)
+ if (!l || l->empty ())
continue;
- //@@ TODO: assuming it is a list.
- //
- const list_value& lv (val.as<list_value&> ());
-
- if (lv.empty ())
- continue;
+ const names_type& lv (l->data_);
// Should we accumulate? If the buffer is not empty, then
// we continue accumulating (the case where we are separated
@@ -1077,11 +1063,11 @@ namespace build
const name& n (lv[0]);
- if (n.proj != nullptr)
+ if (n.qualified ())
fail (t) << "concatenating expansion of " << var.name
<< " contains project name";
- if (!n.type.empty ())
+ if (n.typed ())
fail (t) << "concatenating expansion of " << var.name
<< " contains type";
@@ -1452,10 +1438,10 @@ namespace build
// to their return values are not guaranteed to be stable (and,
// in fact, path()'s is not).
//
- out_root_ = &root_->vars["out_root"].as<const dir_path&> ();
+ out_root_ = &as<dir_path> (*root_->vars["out_root"]);
- auto v (root_->vars["src_root"]);
- src_root_ = v ? &v.as<const dir_path&> () : nullptr;
+ auto l (root_->vars["src_root"]);
+ src_root_ = l ? &as<dir_path> (*l) : nullptr;
}
return r;
diff --git a/build/scope b/build/scope
index 6edb43a..50c4f6b 100644
--- a/build/scope
+++ b/build/scope
@@ -68,37 +68,37 @@ namespace build
// Lookup, including in outer scopes. If you only want to lookup
// in this scope, do it on the the variables map directly.
//
- value_proxy
+ lookup<const value>
operator[] (const variable&) const;
- value_proxy
+ lookup<const value>
operator[] (const std::string& name) const
{
return operator[] (variable_pool.find (name));
}
- // Return a value_proxy 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.
+ // 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_proxy
- assign (const variable& var) {return vars.assign (var).first;}
+ value&
+ assign (const variable& var) {return vars.assign (var).first.get ();}
- value_proxy
- assign (const std::string& name) {return vars.assign (name).first;}
+ value&
+ assign (const std::string& name) {return vars.assign (name).first.get ();}
- // Return a value_proxy 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().
+ // 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_proxy
+ value&
append (const variable&);
- value_proxy
+ value&
append (const std::string& name)
{
return append (variable_pool.find (name));
diff --git a/build/scope.cxx b/build/scope.cxx
index d878726..e6d165e 100644
--- a/build/scope.cxx
+++ b/build/scope.cxx
@@ -12,30 +12,33 @@ namespace build
{
// scope
//
- value_proxy scope::
+ lookup<const value> scope::
operator[] (const variable& var) const
{
- for (const scope* s (this); s != nullptr; s = s->parent_scope ())
+ const value* r (nullptr);
+ const scope* s (this);
+
+ for (; s != nullptr; s = s->parent_scope ())
{
- if (const value_ptr* v = s->vars.find (var))
- return value_proxy (v, &s->vars);
+ if ((r = s->vars.find (var)) != nullptr)
+ break;
}
- return value_proxy ();
+ return lookup<const value> (r, &s->vars);
}
- value_proxy scope::
+ value& scope::
append (const variable& var)
{
- value_proxy val (operator[] (var));
+ auto l (operator[] (var));
- if (val && val.belongs (*this)) // Existing variable in this scope.
- return val;
+ if (l && l.belongs (*this)) // Existing variable in this scope.
+ return const_cast<value&> (*l);
- value_proxy r (assign (var));
+ value& r (assign (var));
- if (val)
- r = val; // Copy value from the outer scope.
+ if (l)
+ r = *l; // Copy value from the outer scope.
return r;
}
diff --git a/build/target b/build/target
index 9e15550..2c05ea1 100644
--- a/build/target
+++ b/build/target
@@ -262,33 +262,33 @@ namespace build
// 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 the variable map directly.
+ // on the variable map directly.
//
- value_proxy
+ lookup<const value>
operator[] (const variable&) const;
- value_proxy
+ lookup<const value>
operator[] (const std::string& name) const
{
return operator[] (variable_pool.find (name));
}
- // Return a value_proxy suitable for assignment. See class scope
- // for details.
+ // Return a value suitable for assignment. See class scope for
+ // details.
//
- value_proxy
+ value&
assign (const variable& var) {return vars.assign (var).first;}
- value_proxy
+ value&
assign (const std::string& name) {return vars.assign (name).first;}
- // Return a value_proxy suitable for appending. See class scope
- // for details.
+ // Return a value suitable for appending. See class scope for
+ // details.
//
- value_proxy
+ value&
append (const variable&);
- value_proxy
+ value&
append (const std::string& name)
{
return append (variable_pool.find (name));
diff --git a/build/target.cxx b/build/target.cxx
index aaf8a5e..6315d04 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -116,16 +116,18 @@ namespace build
return *r;
}
- value_proxy target::
+ lookup<const value> target::
operator[] (const variable& var) const
{
+ using result = lookup<const value>;
+
if (auto p = vars.find (var))
- return value_proxy (p, &vars);
+ return result (p, &vars);
if (group != nullptr)
{
if (auto p = group->vars.find (var))
- return value_proxy (p, &group->vars);
+ return result (p, &group->vars);
}
// Cannot simply delegate to scope's operator[] since we also
@@ -135,7 +137,7 @@ namespace build
{
if (!s->target_vars.empty ())
{
- auto find_specific = [&var, s] (const target& t) -> value_proxy
+ auto find_specific = [&var, s] (const target& t) -> result
{
// Search across target type hierarchy.
//
@@ -154,10 +156,10 @@ namespace build
continue;
if (auto p = j->second.find (var))
- return value_proxy (p, &j->second);
+ return result (p, &j->second);
}
- return value_proxy ();
+ return result ();
};
if (auto p = find_specific (*this))
@@ -171,24 +173,24 @@ namespace build
}
if (auto p = s->vars.find (var))
- return value_proxy (p, &s->vars);
+ return result (p, &s->vars);
}
- return value_proxy ();
+ return result ();
}
- value_proxy target::
+ value& target::
append (const variable& var)
{
- value_proxy val (operator[] (var));
+ auto l (operator[] (var));
- if (val && val.belongs (*this)) // Existing variable in this target.
- return val;
+ if (l && l.belongs (*this)) // Existing variable in this target.
+ return const_cast<value&> (*l);
- value_proxy r (assign (var));
+ value& r (assign (var));
- if (val)
- r = val; // Copy value from the outer scope.
+ if (l)
+ r = *l; // Copy value from the outer scope.
return r;
}
diff --git a/build/target.txx b/build/target.txx
index bded149..a293018 100644
--- a/build/target.txx
+++ b/build/target.txx
@@ -20,9 +20,9 @@ namespace build
const std::string&
target_extension_var (const target_key& tk, scope& s)
{
- auto val (s[var]);
+ auto l (s[var]);
- if (!val)
+ if (!l)
{
diag_record dr;
dr << fail << "no default extension in variable " << var
@@ -42,6 +42,6 @@ namespace build
}
}
- return extension_pool.find (val.as<const std::string&> ());
+ return extension_pool.find (as<std::string> (*l));
}
}
diff --git a/build/test/module.cxx b/build/test/module.cxx
index cba930f..9495275 100644
--- a/build/test/module.cxx
+++ b/build/test/module.cxx
@@ -59,6 +59,18 @@ namespace build
//
rs.insert<target> (test_id, "test", rule_);
}
+
+ // Enter module variables.
+ //
+ if (first)
+ {
+ variable_pool.find ("test", bool_type);
+ variable_pool.find ("test.input", name_type);
+ variable_pool.find ("test.output", name_type);
+ variable_pool.find ("test.roundtrip", name_type);
+ variable_pool.find ("test.options", strings_type);
+ variable_pool.find ("test.arguments", strings_type);
+ }
}
}
}
diff --git a/build/test/rule.cxx b/build/test/rule.cxx
index 10b628d..eecd613 100644
--- a/build/test/rule.cxx
+++ b/build/test/rule.cxx
@@ -11,6 +11,8 @@
#include <build/algorithm>
#include <build/diagnostics>
+#include <build/config/utility> // add_options()
+
using namespace std;
using namespace butl;
@@ -28,7 +30,7 @@ namespace build
// this is a test. So take care of that as well.
//
bool r (false);
- value_proxy v;
+ lookup<const value> l;
// @@ This logic doesn't take into account target type/pattern-
// specific variables.
@@ -40,13 +42,13 @@ namespace build
++p.first)
{
const variable& var (p.first->first);
- const value_ptr& val (p.first->second);
+ const value& val (p.first->second);
// If we have test, then always use that.
//
if (var.name == "test")
{
- v.rebind (value_proxy (val, t));
+ l = lookup<const value> (val, t);
break;
}
@@ -68,10 +70,11 @@ namespace build
{
// See if there is a scope variable.
//
- if (!v)
- v.rebind (t.base_scope ()[string("test.") + t.type ().name]);
+ if (!l.defined ())
+ l = t.base_scope ()[
+ variable_pool.find (string("test.") + t.type ().name, bool_type)];
- r = v && v.as<bool> ();
+ r = l && as<bool> (*l);
}
// If this is the update pre-operation, then all we really need to
@@ -129,50 +132,47 @@ namespace build
// First check the target-specific vars since they override any
// scope ones.
//
- auto iv (t.vars["test.input"]);
- auto ov (t.vars["test.output"]);
- auto rv (t.vars["test.roundtrip"]);
-
- // Can either be input or arguments.
- //
- auto av (t.vars["test.arguments"]);
+ 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 (av)
+ if (al)
{
- if (iv)
+ if (il)
fail << "both test.input and test.arguments specified for "
<< "target " << t;
- if (rv)
+ if (rl)
fail << "both test.roundtrip and test.arguments specified for "
<< "target " << t;
}
scope& bs (t.base_scope ());
- if (!iv && !ov && !rv)
+ if (!il && !ol && !rl)
{
string n ("test.");
n += t.type ().name;
- const variable& in (variable_pool.find (n + ".input"));
- const variable& on (variable_pool.find (n + ".output"));
- const variable& rn (variable_pool.find (n + ".roundtrip"));
+ const variable& in (variable_pool.find (n + ".input", name_type));
+ const variable& on (variable_pool.find (n + ".output", name_type));
+ const variable& rn (variable_pool.find (n + ".roundtrip", name_type));
// We should only keep value(s) that were specified together
// in the innermost scope.
//
for (scope* s (&bs); s != nullptr; s = s->parent_scope ())
{
- ov.rebind (s->vars[on]);
+ ol = s->vars[on];
- if (!av) // Not overriden at target level by test.arguments?
+ if (!al) // Not overriden at target level by test.arguments?
{
- iv.rebind (s->vars[in]);
- rv.rebind (s->vars[rn]);
+ il = s->vars[in];
+ rl = s->vars[rn];
}
- if (iv || ov || rv)
+ if (il || ol || rl)
break;
}
}
@@ -182,18 +182,18 @@ namespace build
// Reduce the roundtrip case to input/output.
//
- if (rv)
+ if (rl)
{
- if (iv || ov)
+ if (il || ol)
fail << "both test.roundtrip and test.input/output specified "
<< "for target " << t;
- in = on = rv.as<const name*> ();
+ in = on = &as<name> (*rl);
}
else
{
- in = iv ? iv.as<const name*> () : nullptr;
- on = ov ? ov.as<const name*> () : nullptr;
+ in = il ? &as<name> (*il) : nullptr;
+ on = ol ? &as<name> (*ol) : nullptr;
}
// Resolve them to targets, which normally would be existing files
@@ -283,35 +283,24 @@ namespace build
}
static void
- add_arguments (cstrings& args, target& t, const char* n)
+ add_arguments (cstrings& args, const target& t, const char* n)
{
string var ("test.");
var += n;
- auto v (t.vars[var]);
+ auto l (t.vars[var]);
- if (!v)
+ if (!l)
{
var.resize (5);
var += t.type ().name;
var += '.';
var += n;
- v.rebind (t.base_scope ()[var]);
+ l = t.base_scope ()[variable_pool.find (var, strings_type)];
}
- if (v)
- {
- for (const name& n: v.as<const list_value&> ())
- {
- if (n.simple ())
- args.push_back (n.value.c_str ());
- else if (n.directory ())
- args.push_back (n.dir.string ().c_str ());
- else
- fail << "expected argument instead of " << n <<
- info << "in variable " << var;
- }
- }
+ if (l)
+ config::append_options (args, as<strings> (*l));
}
// The format of args shall be:
diff --git a/build/variable b/build/variable
index bc78289..3bd8922 100644
--- a/build/variable
+++ b/build/variable
@@ -6,13 +6,14 @@
#define BUILD_VARIABLE
#include <map>
+#include <vector>
#include <string>
-#include <memory> // unique_ptr
#include <cstddef> // nullptr_t
#include <utility> // move(), pair, make_pair()
#include <cassert>
+#include <iterator>
#include <functional> // hash, reference_wrapper
-#include <typeindex>
+#include <type_traits> // conditional, is_reference, remove_reference, etc.
#include <unordered_set>
#include <butl/prefix-map>
@@ -22,12 +23,18 @@
namespace build
{
- struct value;
+ 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
{
- std::type_index id;
- value* (*const factory) ();
+ const std::string name;
+ bool (*const assign) (names&, const variable&);
+ bool (*const append) (names&, names, const variable&);
};
// variable
@@ -37,11 +44,12 @@ namespace build
struct variable
{
explicit
- variable (std::string n, char p = '\0'): name (std::move (n)), pairs (p) {}
+ variable (std::string n, const value_type* t = nullptr, char p = '\0')
+ : name (std::move (n)), pairs (p), type (t) {}
std::string name;
char pairs;
- //const value_type* type = nullptr; // If NULL, then no fixed type.
+ const value_type* type; // If NULL, then not (yet) typed.
};
inline bool
@@ -51,119 +59,121 @@ namespace build
// value
//
- struct value;
- typedef std::unique_ptr<value> value_ptr;
-
- struct value
+ class value
{
public:
- virtual value_ptr
- clone () const = 0;
+ // By default we create NULL value.
+ //
+ explicit value (const value_type* t = nullptr)
+ : type (t), state_ (state_type::null) {}
- virtual bool
- compare (const value&) const = 0;
+ value (value&&) = default;
- virtual
- ~value () = default;
- };
-
- class list_value: public value, public names
- {
- public:
- using names::names;
-
- list_value () = default;
- explicit list_value (names d): names (std::move (d)) {}
- explicit list_value (name n) {emplace_back (std::move (n));}
- explicit list_value (dir_path d) {emplace_back (std::move (d));}
- explicit list_value (std::string s) {emplace_back (std::move (s));}
- explicit list_value (const char* s) {emplace_back (s);}
+ value&
+ operator= (std::nullptr_t)
+ {
+ data_.clear ();
+ state_ = state_type::null;
+ return *this;
+ }
- virtual value_ptr
- clone () const {return value_ptr (new list_value (*this));}
+ value&
+ operator= (value&&);
- virtual bool
- compare (const value& v) const
+ value&
+ operator= (const value& v)
{
- const list_value* lv (dynamic_cast<const list_value*> (&v));
- return lv != nullptr && static_cast<const names&> (*this) == *lv;
+ if (&v != this)
+ *this = value (v);
+ return *this;
}
- // Pair (i.e., key-value) search. Note that this funtion assumes
- // the list contains only pairs and keys are simple names. Returns
- // NULL if not found.
- //
- const name*
- find_pair (const std::string& key) const
+ value&
+ operator= (std::reference_wrapper<const value> v)
{
- for (auto i (begin ()); i != end (); i += 2)
- if (i->value == key)
- return &*++i;
- return nullptr;
+ return *this = v.get ();
}
- };
- typedef std::unique_ptr<list_value> list_value_ptr;
- // value_proxy
- //
- // A variable can be undefined, null, or contain some actual value.
- // Note that once value_proxy is bound to a value, the only way to
- // rebind it to a different value is by using explicit rebind(). In
- // particular, assigning one value proxy to another will assing the
- // values.
- //
- struct variable_map;
+ value&
+ append (value, const variable&); // Aka operator+=().
- struct value_proxy
- {
- bool
- defined () const {return p != nullptr;}
+ // 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);}
- bool
- null () const {return *p == nullptr;}
+ template <typename T> value& operator+= (T);
+ value& operator+= (const char* v) {return *this += std::string (v);}
- bool
- empty () const;
+ private:
+ explicit value (const value&) = default;
+
+ public:
+ const value_type* type; // NULL means this value is not (yet) typed.
- explicit operator bool () const {return defined () && !null ();}
- explicit operator value_ptr& () const {return *p;}
+ bool null () const {return state_ == state_type::null;}
+ bool empty () const {return state_ == state_type::empty;}
- // Get interface. See available specializations below.
+ explicit operator bool () const {return !null ();}
+
+ // Raw data read interface.
//
- template <typename T>
- T
- as () const;
+ using const_iterator = names::const_iterator;
+
+ const_iterator begin () const {return data_.begin ();}
+ const_iterator end () const {return data_.end ();}
- // Assign.
+ // Raw data write interface. Note that it triggers callbacks for
+ // typed values. Variable is passed for diagnostics.
//
- const value_proxy&
- operator= (value_ptr) const;
+ void
+ assign (names, const variable&);
- const value_proxy&
- operator= (const value_proxy&) const;
+ void
+ append (names, const variable&);
- const value_proxy&
- operator= (list_value) const;
+ 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_;
+ };
- const value_proxy&
- operator= (std::string) const;
+ //@@ 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;
- const value_proxy&
- operator= (dir_path) const;
+ template <typename V>
+ struct lookup
+ {
+ V* value;
+ const variable_map* vars;
- const value_proxy&
- operator= (std::nullptr_t) const;
+ bool
+ defined () const {return value != nullptr;}
- // Append.
+ // Note: returns true if defined and not NULL.
//
- const value_proxy&
- operator+= (const value_proxy&) const;
+ explicit operator bool () const {return defined () && !value->null ();}
- const value_proxy&
- operator+= (const list_value&) const;
-
- const value_proxy&
- operator+= (std::string) const; // Append simple name to list_value.
+ 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.
@@ -172,92 +182,395 @@ namespace build
bool
belongs (const T& x) const {return vars == &x.vars;}
- // Implementation details.
- //
- const variable_map* vars; // Variable map to which this value belongs.
-
- value_proxy (): vars (nullptr), p (nullptr) {}
- value_proxy (value_ptr* p, const variable_map* v)
- : vars (p != nullptr ? v : nullptr), p (p) {}
+ lookup (): value (nullptr), vars (nullptr) {}
+ lookup (V* v, const variable_map* vs)
+ : value (v), vars (v != nullptr ? vs : nullptr) {}
template <typename T>
- value_proxy (value_ptr& p, const T& x)
- : value_proxy (&p, &x.vars) {}
+ 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&);
- // @@ To do this properly we seem to need ro_value_proxy?
+ 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.
//
- value_proxy (const value_ptr* p, const variable_map* v)
- : value_proxy (const_cast<value_ptr*> (p), v) {}
+ public:
+ explicit bool_value (D& d): d (&d) {}
- template <typename T>
- value_proxy (const value_ptr& p, const T& x)
- : value_proxy (const_cast<value_ptr&> (p), x) {}
+ bool_value (const bool_value&) = delete;
+ bool_value& operator= (const bool_value&) = delete; // Rebind or deep?
- void
- rebind (const value_proxy& x) {vars = x.vars; p = x.p;}
+ bool_value (bool_value&&) = default;
+ bool_value& operator= (bool_value&&) = delete;
- private:
- value_ptr* p;
+ D* d; // name
};
template <>
- inline value& value_proxy::
- as<value&> () const {return **p;}
+ struct value_traits<bool>
+ {
+ using type = bool_value<name>;
+ using const_type = bool_value<const name>;
- template <>
- inline const value& value_proxy::
- as<const value&> () const {return **p;}
+ 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 <>
- inline list_value& value_proxy::
- as<list_value&> () const
+ struct value_traits<std::string>
{
- list_value* lv (dynamic_cast<list_value*> (p->get ()));
- assert (lv != nullptr);
- return *lv;
- }
+ using type = std::string&;
+ using const_type = const std::string&;
- template <>
- inline const list_value& value_proxy::
- as<const list_value&> () const {return as<list_value&> ();}
+ 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 <>
- inline const name* value_proxy::
- as<const name*> () const
+ struct value_traits<dir_path>
{
- const auto& lv (as<const list_value&> ());
- assert (lv.size () < 2);
- return lv.empty () ? nullptr : &lv.front ();
- }
+ 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 <>
- inline const name& value_proxy::
- as<const name&> () const
+ struct value_traits<name>
{
- const auto& lv (as<const list_value&> ());
- assert (lv.size () == 1);
- return lv.front ();
- }
+ using type = name&;
+ using const_type = const name&;
- template <>
- std::string& value_proxy::
- as<std::string&> () const;
+ static type as (name& n) {return n;}
+ static const_type as (const name& n) {return n;}
- template <>
- const std::string& value_proxy::
- as<const std::string&> () const;
+ static type as (value&);
+ static const_type as (const value&);
- template <>
- dir_path& value_proxy::
- as<dir_path&> () const;
+ static bool assign (name&) {return true;}
+ static void assign (value&, name);
+ static void append (value&, name) = delete;
- template <>
- const dir_path& value_proxy::
- as<const dir_path&> () const;
+ static const build::value_type value_type;
+ };
- template <>
- bool value_proxy::
- as<bool> () const;
+ 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 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 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
@@ -301,15 +614,27 @@ namespace build
{
// variable_pool
//
- struct variable_set: std::unordered_set<variable>
+ using variable_set_base = std::unordered_set<variable>;
+ struct variable_set: private variable_set_base
{
- // @@ Need to check/set type?
- //
const variable&
- find (std::string name) {return *emplace (std::move (name)).first;}
+ find (std::string name, const build::value_type* t = nullptr, char p = '\0')
+ {
+ auto r (emplace (std::move (name), t, 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; // Ok, not changing the key.
+ }
+
+ return v;
+ }
- const variable&
- insert (variable v) {return *emplace (std::move (v)).first;}
+ using variable_set_base::clear;
};
extern variable_set variable_pool;
@@ -318,40 +643,92 @@ namespace build
//
struct variable_map
{
- using map_type = butl::prefix_map<variable_cref, value_ptr, '.'>;
+ using map_type = butl::prefix_map<variable_cref, value, '.'>;
using size_type = map_type::size_type;
- using const_iterator = map_type::const_iterator;
- const value_ptr*
+ 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));
- return i != m_.end () ? &i->second : nullptr;
+ 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_proxy
+ 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 value_proxy (find (var), this);
+ return lookup<const value> (find (var), this);
}
- value_proxy
+ lookup<const value>
operator[] (const std::string& name) const
{
return operator[] (variable_pool.find (name));
}
- // The second member in the pair indicates whether new (NULL)
- // value was set.
+ // Non-const lookup. Only exposed on the map directly.
//
- std::pair<value_proxy, bool>
+ lookup<value>
+ operator[] (const variable& var)
+ {
+ return lookup<value> (find (var), this);
+ }
+
+ lookup<value>
+ operator[] (const std::string& name)
+ {
+ return operator[] (variable_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_ptr ()));
- return std::make_pair (value_proxy (&r.first->second, this), r.second);
+ 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<value_proxy, bool>
+ std::pair<std::reference_wrapper<value>, bool>
assign (const std::string& name)
{
return assign (variable_pool.find (name));
@@ -360,7 +737,9 @@ namespace build
std::pair<const_iterator, const_iterator>
find_namespace (const std::string& ns) const
{
- return m_.find_prefix (variable_pool.find (ns));
+ auto r (m_.find_prefix (variable_pool.find (ns)));
+ return std::make_pair (const_iterator (r.first),
+ const_iterator (r.second));
}
const_iterator
@@ -381,17 +760,17 @@ namespace build
// 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>;
using variable_type_map = std::map<std::reference_wrapper<const target_type>,
variable_pattern_map>;
-
- //@@ 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.
-
}
#include <build/variable.ixx>
+#include <build/variable.txx>
#endif // BUILD_VARIABLE
diff --git a/build/variable.cxx b/build/variable.cxx
index eab77e5..d215b02 100644
--- a/build/variable.cxx
+++ b/build/variable.cxx
@@ -4,86 +4,317 @@
#include <build/variable>
+#include <iterator> // make_move_iterator()
+
#include <build/utility>
+#include <build/diagnostics>
using namespace std;
namespace build
{
- variable_set variable_pool;
+ // 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;
+ }
+
+ 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 ()));
- // value_proxy
+ state_ = (type != nullptr && type->assign != nullptr
+ ? type->assign (data_, var)
+ : !data_.empty ())
+ ? state_type::filled
+ : state_type::empty;
+ }
+
+ // bool value
//
- template <>
- string& value_proxy::
- as<string&> () const
- {
- list_value& lv (as<list_value&> ());
- assert (lv.size () == 1);
- name& n (lv.front ());
- assert (n.simple ());
- return n.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;
}
- template <>
- const string& value_proxy::
- as<const string&> () const
+ static bool
+ bool_assign (names& v, const variable& var)
{
- const list_value& lv (as<list_value&> ());
- assert (lv.size () < 2);
+ // Verify the value is either "true" or "false".
+ //
+ if (v.size () == 1)
+ {
+ name& n (v.front ());
- if (lv.empty ())
- return empty_string;
+ 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".
- const name& n (lv.front ());
+ if (a.front ().value[0] == 't' && v.front ().value[0] == 'f')
+ v = move (a);
- assert (n.simple ());
- return n.value;
+ return true;
}
- template <>
- dir_path& value_proxy::
- as<dir_path&> () const
+ const value_type value_traits<bool>::value_type
{
- list_value& lv (as<list_value&> ());
- assert (lv.size () == 1);
- name& n (lv.front ());
- assert (n.directory ());
- return n.dir;
+ "bool",
+ &bool_assign,
+ &bool_append
+ };
+
+ const value_type* bool_type = &value_traits<bool>::value_type;
+
+ // string value
+ //
+ bool value_traits<string>::
+ assign (name& n)
+ {
+ // 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 += '/';
+ }
+
+ return n.simple ();
}
- template <>
- const dir_path& value_proxy::
- as<const dir_path&> () const
+ static bool
+ string_assign (names& v, const variable& var)
{
- const list_value& lv (as<list_value&> ());
- assert (lv.size () < 2);
+ // 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 (lv.empty ())
- return empty_dir_path;
+ if (value_traits<string>::assign (n))
+ return !n.value.empty ();
+ }
+
+ fail << "invalid string variable '" << var.name << "' value '" << v << "'";
+ return false;
+ }
- const name& n (lv.front ());
+ 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 (n.empty ())
- return empty_dir_path;
+ if (v.front ().value.empty ())
+ v = move (a);
+ else
+ v.front ().value += a.front ().value;
- assert (n.directory ());
- return n.dir;
+ return !v.front ().value.empty ();
}
- template <>
- bool value_proxy::
- as<bool> () const
+ 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)
{
- const list_value& lv (as<const list_value&> ());
- assert (lv.size () == 1);
- const name& n (lv.front ());
- assert (n.simple ());
- if (n.value == "true")
+ if (n.directory ())
return true;
- else if (n.value == "false")
+
+ 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
- assert (false); // Bool value should be 'true' or 'false'.
+ 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_set variable_pool;
}
diff --git a/build/variable.ixx b/build/variable.ixx
index b97815e..16ea7c0 100644
--- a/build/variable.ixx
+++ b/build/variable.ixx
@@ -4,108 +4,395 @@
namespace build
{
- // value_proxy
+ // value
//
- inline bool value_proxy::
- empty () const {return as<const list_value&> ().empty ();}
+ 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);
+ }
- inline const value_proxy& value_proxy::
- operator= (value_ptr v) const
+ template <typename T>
+ inline typename value_traits<T>::const_type
+ as (const name& n)
{
- *p = std::move (v);
+ 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 const value_proxy& value_proxy::
- operator= (const value_proxy& v) const
+ 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)
{
- if (this != &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)
- *p = v.as<const value&> ().clone ();
- else
- p->reset ();
+ if (v.type == nullptr)
+ v.type = bool_type;
+ v.data_.emplace_back (name ());
+ v.state_ = value::state_type::empty;
}
- return *this;
+ as (v) = x;
+ v.state_ = value::state_type::filled;
}
- inline const value_proxy& value_proxy::
- operator= (list_value v) const
+ inline void value_traits<bool>::
+ append (value& v, bool x)
{
- if (*p == nullptr)
- p->reset (new list_value (std::move (v)));
+ if (v.null ())
+ assign (v, x);
else
- //@@ Assuming it is a list_value.
- //
- as<list_value&> () = std::move (v);
+ as (v) += x; // Cannot be empty.
+ }
- return *this;
+ // string value
+ //
+ inline std::string& value_traits<std::string>::
+ as (value& v)
+ {
+ assert (v.type == string_type);
+ return v.data_.front ().value;
}
- inline const value_proxy& value_proxy::
- operator= (std::string v) const
+ inline const std::string& value_traits<std::string>::
+ as (const value& v)
{
- // In most cases this is used to initialize a new variable, so
- // don't bother trying to optimize for the case where p is not
- // NULL.
- //
- p->reset (new list_value (std::move (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;
}
- inline const value_proxy& value_proxy::
- operator= (dir_path v) const
+ template <typename T, typename D>
+ template <typename D1>
+ inline vector_value<T, D>& vector_value<T, D>::
+ assign (const vector_value<T, D1>& v)
{
- p->reset (new list_value (std::move (v)));
+ d->clear ();
+ d->insert (d->end (), v.begin (), v.end ());
return *this;
}
- inline const value_proxy& value_proxy::
- operator= (std::nullptr_t) const
+ template <typename T, typename D>
+ template <typename D1>
+ inline vector_value<T, D>& vector_value<T, D>::
+ append (const vector_value<T, D1>& v)
{
- p->reset ();
+ d->insert (d->end (), v.begin (), v.end ());
return *this;
}
- inline const value_proxy& value_proxy::
- operator+= (const value_proxy& v) const
+ 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)
{
- if (v && this != &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 (*p == nullptr)
- *this = v;
- else
- //@@ Assuming it is a list_value.
- //
- *this += v.as<const list_value&> ();
+ if (v.type == nullptr)
+ v.type = &value_traits<std::vector<T>>::value_type;
+ v.state_ = value::state_type::empty;
}
- return *this;
+ v.state_ = (as (v).assign (std::move (x))).empty ()
+ ? value::state_type::empty
+ : value::state_type::filled;
}
- inline const value_proxy& value_proxy::
- operator+= (const list_value& v) const
+ template <typename T>
+ template <typename V>
+ inline void value_traits<std::vector<T>>::
+ append (value& v, V x)
{
- if (*p == nullptr)
- *this = v;
+ 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 ())
{
- list_value& lv (as<list_value&> ());
- lv.insert (lv.end (), v.begin (), v.end ());
+ if (v.type == nullptr)
+ v.type = &value_traits<std::map<K, V>>::value_type;
+ v.state_ = value::state_type::empty;
}
- return *this;
+ v.state_ = (as (v).assign (std::move (x))).empty ()
+ ? value::state_type::empty
+ : value::state_type::filled;
}
- inline const value_proxy& value_proxy::
- operator+= (std::string v) const
+ template <typename K, typename V>
+ template <typename M>
+ inline void value_traits<std::map<K, V>>::
+ append (value& v, M x)
{
- if (*p == nullptr)
- *this = v;
+ if (v.null ())
+ assign (v, std::move (x));
else
- as<list_value&> ().emplace_back (std::move (v));
+ v.state_ = (as (v).append (std::move (x))).empty ()
+ ? value::state_type::empty
+ : value::state_type::filled;
+ }
- return *this;
+ // 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
new file mode 100644
index 0000000..1b32584
--- /dev/null
+++ b/build/variable.txx
@@ -0,0 +1,160 @@
+// 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 value_type value_traits<std::vector<T>>::value_type
+ {
+ value_traits<T>::value_type.name + 's',
+ &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 value_type value_traits<std::map<K, V>>::value_type
+ {
+ value_traits<K>::value_type.name + '_' +
+ value_traits<V>::value_type.name + "_map",
+ &map_assign<K, V>,
+ &map_append<K, V>
+ };
+}
diff --git a/tests/amalgam/config/buildfile b/tests/amalgam/config/buildfile
index 2f01ba3..56f7f9a 100644
--- a/tests/amalgam/config/buildfile
+++ b/tests/amalgam/config/buildfile
@@ -1,3 +1,3 @@
-d = #t/ #l/
+d = #t/ l/
.: $d
include $d
diff --git a/tests/amalgam/test/buildfile b/tests/amalgam/test/buildfile
index 3fadac3..5440dbf 100644
--- a/tests/amalgam/test/buildfile
+++ b/tests/amalgam/test/buildfile
@@ -3,6 +3,6 @@ using cxx
hxx.ext = hxx
cxx.ext = cxx
-import libs += amalgam-libtest
+import libs = amalgam-libtest%lib{test}
exe{driver}: cxx{driver} $libs