aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/b.cli162
-rw-r--r--build2/b.cxx9
-rw-r--r--build2/buildfile2
-rw-r--r--build2/config/init.cxx47
-rw-r--r--build2/config/module10
-rw-r--r--build2/config/module.cxx66
-rw-r--r--build2/config/operation7
-rw-r--r--build2/config/operation.cxx283
-rw-r--r--build2/config/utility.cxx44
-rw-r--r--build2/context.cxx6
-rw-r--r--build2/operation6
-rw-r--r--build2/scope9
-rw-r--r--build2/variable.txx2
-rw-r--r--unit-tests/function/buildfile2
-rw-r--r--unit-tests/lexer/buildfile2
-rw-r--r--unit-tests/scheduler/buildfile2
-rw-r--r--unit-tests/test/script/lexer/buildfile2
-rw-r--r--unit-tests/test/script/parser/buildfile2
18 files changed, 542 insertions, 121 deletions
diff --git a/build2/b.cli b/build2/b.cli
index 0d61c00..0d345bf 100644
--- a/build2/b.cli
+++ b/build2/b.cli
@@ -37,30 +37,32 @@ namespace build2
// let's not put this "extended" description into usage.
//
{
- "<meta-operation> <operation> <target> <src-base>",
+ "<meta-operation> <operation> <target> <parameters> <src-base>",
"",
"The buildspec has the following form:
- \c{<meta-operation>(<operation>...(<target>...))...}
+ \c{<meta-operation>(<operation>...(<target>...[,<parameters>]))...}
- All three components can be omitted. If <meta-operation> is omitted, then
- it defaults to \cb{perform}. If <operation> is omitted, then it defaults
- to the default operation for this meta-operation. For \cb{perform} it is
- \cb{update}. Finally, if <target> is omitted, then it defaults to the
- current directory. For example:
+ All components in the buildspec can be omitted. If <meta-operation> is
+ omitted, then it defaults to \cb{perform}. If <operation> is omitted,
+ then it defaults to the default operation for this meta-operation. For
+ \cb{perform} it is \cb{update}. Finally, if <target> is omitted, then it
+ defaults to the current working directory. Some operations and
+ meta-operations may take additional <parameters>. For example:
\
- b # perform(update(./))
- b foo/ # perform(update(foo/))
- b foo/ bar/ # perform(update(foo/ bar/))
- b update # perform(update(./))
- b 'clean(../)' # perform(clean(../))
- b perform # perform(update(./))
- b configure # configure(?(./))
- b 'configure(../)' # configure(?(../))
- b clean update # perform(clean(./) update(./))
- b configure update # configure(?(./)) perform(update(./))
+ b # perform(update(./))
+ b foo/ # perform(update(foo/))
+ b foo/ bar/ # perform(update(foo/ bar/))
+ b update # perform(update(./))
+ b 'clean(../)' # perform(clean(../))
+ b perform # perform(update(./))
+ b configure # configure(?(./))
+ b 'configure(../)' # configure(?(../))
+ b clean update # perform(clean(./) update(./))
+ b configure update # configure(?(./)) perform(update(./))
+ b 'create(conf/, cxx)' # create(?(conf/), cxx)
\
Notice the question mark used to show the (imaginary) default operation
@@ -97,13 +99,24 @@ namespace build2
b foo-out/exe{foo} # out_base=foo-out/ src_base=foo/
\
+ An exception to this requirement is a directory target in which case,
+ provided the directory has subdirectories, an \i{implied} \cb{buildfile}
+ with the following content is assumed:
+
+ \
+ # Implied directory buildfile: build all subdirectories.
+ #
+ ./: */
+ \
+
In the above example, we assumed that the \cb{build2} driver was able to
- determine the association between \cb{out_base} and \cb{src_base}. This is
+ determine the association between \cb{out_base} and \cb{src_base}. In
+ case \cb{src_base} and \cb{out_base} are not the same directotry, this is
achieved in one of two ways: the \cb{config} module (which implements the
- \cb{configure} and \cb{disfigure} meta-operations) saves this association
- as part of the configuration process. If, however, the association hasn't
- been saved, then we have to specify \cb{src_base} explicitly using the
- following extended <target> syntax:
+ \cb{configure}, \cb{disfigure}, and \cb{create} meta-operations) saves
+ this association as part of the configuration process. If, however, the
+ association hasn't been saved, then we have to specify \cb{src_base}
+ explicitly using the following extended <target> syntax:
\c{<src-base>/@<target>}
@@ -122,6 +135,13 @@ namespace build2
b 'clean(foo-out/exe{foo})' # no need to specify src_base
\
+ The ability to specify \cb{build2} variables as part of the command line
+ is normally used to pass configuration values, for example:
+
+ \
+ b config.cxx=clang++ config.cxx.coptions=-O3
+ \
+
The build system has the following built-in and pre-defined
meta-operations:
@@ -134,15 +154,96 @@ namespace build2
\li|\cb{configure}
Configure all operations supported by a project and save the result
- in the project's \cb{build2/config.build} file. Implemented by the
- \cb{config} module.|
+ in the project's \cb{build/config.build} file. Implemented by the
+ \cb{config} module. For example:
+
+ \
+ b config.cxx=clang++ config.cxx.coptions=-O3 \
+ config.install.root=/usr/local config.install.root.sudo=sudo \
+ configure
+ \
+
+ |
\li|\cb{disfigure}
Disfigure all operations supported by a project and remove the
- project's \cb{build2/config.build} file. Implemented by the
+ project's \cb{build/config.build} file. Implemented by the
\cb{config} module.|
+ \li|\cb{create}
+
+ Create and configure a \i{configuration} project. Implemented by the
+ \cb{config} module.
+
+ Normally a \cb{build2} project is created manually by writing the
+ \cb{bootstrap.build} and \cb{config.build} files, adding source
+ files, and so on. However, a special kind of project, which we call
+ \i{configuration}, is often useful. Such a project doesn't have any
+ source files of its own. Instead, it serves as an amalgamation for
+ building other projects as part of it. Doing it this way has two
+ major benefits: sub-projects automatically resolve their imports
+ to other projects in the amalgamation and sub-projects inherits their
+ configuration from the amalgamation (which means if we want to change
+ something, we only need to do it in one place).
+
+ As an example, let's assume we have two C++ projects: the
+ \cb{libhello} library in \cb{libhello/} and the \cb{hello} executable
+ that imports it in \cb{hello/}. And we want to build \cb{hello} with
+ \cb{clang++}.
+
+ One way to do it would be to configure and build each project in its
+ own directory, for example:
+
+ \
+ b 'configure(libhello/@libhello-clang/)' config.cxx=clang++
+ b 'configure(hello/@hello-clang/)' config.cxx=clang++ \
+ config.import.libhello=libhello-clang/
+ \
+
+ The two drawbacks, as mentioned above, are the need to explicitly
+ resolve the import and having to make changes in multiple places
+ should, for example, we want to switch from \cb{clang++} to \cb{g++}.
+
+ We can, however, achieve the same end result but without any of the
+ drawbacks using the configuration project:
+
+ \
+ b 'create(clang, cxx)' config.cxx=clang++ # Creates clang/.
+ b 'configure(libhello/@clang/libhello/)'
+ b 'configure(hello/@clang/hello/)'
+ \
+
+ The targets passed to the \cb{create} meta-operation must be
+ directories which should either not exist or be empty. For each
+ such directory \cb{create} first initializes a project as described
+ below and then configures it by executing the \cb{configure}
+ meta-operation.
+
+ The first optional parameter to \cb{create} is the list of modules to
+ load in \cb{root.build}. By default, \cb{create} appends \cb{.config}
+ to the names of these modules so that only their configurations are
+ loaded. You can override this behavior by specifying the period
+ (\cb{.}) after the module name. You can also instruct \cb{create} to
+ use the optional module load by prefixing the module name with the
+ question mark (\cb{?}).
+
+ The second optional parameter is the list of modules to load in
+ \cb{bootstrap.build}. If not specified, then the \cb{test} and
+ \cb{install} modules are loaded by default. The \cb{config} module
+ is always loaded first.
+
+ Besides creating project's \cb{bootstrap.build} and \cb{root.build},
+ \cb{create} also writes the root \cb{buildfile} with the following
+ contents:
+
+ \
+ ./: {*/ -build/}
+ \
+
+ If used, this \cb{buildfile} will build all the sub-projects
+ currently present in the configuration.|
+
\li|\cb{dist}
Prepare a distribution containing all files necessary to perform all
@@ -170,16 +271,7 @@ namespace build2
Install a target. Performs \cb{update} as a pre-operation. Implemented
by the \cb{install} module.||
- The ability to specify \cb{build2} variables as part of the command line
- is normally used to pass configuration values, for example:
-
- \
- b config.cxx=clang++ config.cxx.coptions=-O3 \
- config.install.root=/usr/local config.install.root.sudo=sudo \
- configure
- \
-
- Note also that buildspec and command line variable values are treated as
+ Note that buildspec and command line variable values are treated as
\cb{buildfile} fragments and so can use quoting and escaping as well as
contain variable expansions and evaluation contexts. However, to be more
usable on various platforms, escaping in these two situations is limited
diff --git a/build2/b.cxx b/build2/b.cxx
index 6e5aef9..d2ad368 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -423,7 +423,8 @@ main (int argc, char* argv[])
// Can modify params, opspec, change meta-operation name.
//
if (auto f = meta_operation_table[m].process)
- current_mname = &f (mparams, opspecs, lifted != nullptr, l);
+ current_mname = &f (
+ var_ovs, mparams, opspecs, lifted != nullptr, l);
}
}
}
@@ -508,9 +509,9 @@ main (int argc, char* argv[])
{
const string& v (tn.value);
- // Handle a few common cases as special: empty name, '.',
- // '..', as well as dir{foo/bar} (without trailing '/').
- // This code must be consistent with find_target_type().
+ // Handle a few common cases as special: empty name, '.', '..', as
+ // well as dir{foo/bar} (without trailing '/'). This code must be
+ // consistent with find_target_type() and other places.
//
if (v.empty () || v == "." || v == ".." || tn.type == "dir")
out_base = dir_path (v);
diff --git a/build2/buildfile b/build2/buildfile
index 856346b..90faa6e 100644
--- a/build2/buildfile
+++ b/build2/buildfile
@@ -67,7 +67,7 @@ exe{b}: \
cli/{hxx cxx}{ rule } \
cli/{hxx cxx}{ target } \
config/{hxx cxx}{ init } \
- config/{hxx }{ module } \
+ config/{hxx cxx}{ module } \
config/{hxx cxx}{ operation } \
config/{hxx txx cxx}{ utility } \
cxx/{hxx cxx}{ init } \
diff --git a/build2/config/init.cxx b/build2/config/init.cxx
index 682d817..65aa7bb 100644
--- a/build2/config/init.cxx
+++ b/build2/config/init.cxx
@@ -22,18 +22,36 @@ namespace build2
{
namespace config
{
- const string module::name ("config");
- const uint64_t module::version (1);
-
void
- boot (scope& rs, const location& loc, unique_ptr<module_base>&)
+ boot (scope& rs, const location& loc, unique_ptr<module_base>& mod)
{
tracer trace ("config::boot");
const dir_path& out_root (rs.out_path ());
l5 ([&]{trace << "for " << out_root;});
- // Register meta-operations.
+ const string& mname (*current_mname);
+ const string& oname (*current_oname);
+
+ // Only create the module if we are configuring or creating. This is a
+ // bit tricky since the build2 core may not yet know if this is the
+ // case. But we know.
+ //
+ if (( mname == "configure" || mname == "create") ||
+ (mname.empty () && (oname == "configure" || oname == "create")))
+ {
+ unique_ptr<module> m (new module);
+
+ // Adjust priority for the import pseudo-module so that
+ // config.import.* values come first in config.build.
+ //
+ m->save_module ("import", INT32_MIN);
+
+ mod = move (m);
+ }
+
+ // Register meta-operations. Note that we don't register create_id
+ // since it will be pre-processed into configure.
//
rs.meta_operations.insert (configure_id, configure);
rs.meta_operations.insert (disfigure_id, disfigure);
@@ -54,11 +72,10 @@ namespace build2
//
const variable& c_v (vp.insert<uint64_t> ("config.version", false));
- // Don't load it if we are disfiguring. This is a bit tricky since the
- // build2 core may not yet know it is disfiguring. But we know.
+ // Don't load it if we are disfiguring. The same situation as with
+ // module loading above.
//
- if (*current_mname != disfigure.name &&
- (!current_mname->empty () || *current_oname != disfigure.name))
+ if (mname != "disfigure" && (!mname.empty () || oname != "disfigure"))
{
path f (out_root / config_file);
@@ -96,7 +113,7 @@ namespace build2
init (scope& rs,
scope&,
const location& l,
- unique_ptr<module_base>& mod,
+ unique_ptr<module_base>&,
bool first,
bool,
const variable_map& config_hints)
@@ -113,16 +130,6 @@ namespace build2
assert (config_hints.empty ()); // We don't known any hints.
- // Only create the module if we are configuring.
- //
- if (current_mif->id == configure_id)
- mod.reset (new module);
-
- // Adjust priority for the import pseudo-module so that config.import.*
- // values come first in config.build.
- //
- config::save_module (rs, "import", INT32_MIN);
-
// Register alias and fallback rule for the configure meta-operation.
//
{
diff --git a/build2/config/module b/build2/config/module
index ddd54d6..3b58aa0 100644
--- a/build2/config/module
+++ b/build2/config/module
@@ -79,8 +79,14 @@ namespace build2
{
config::saved_modules saved_modules;
- static const string name; // init.cxx
- static const uint64_t version; // init.cxx
+ void
+ save_variable (const variable&, uint64_t flags = 0);
+
+ void
+ save_module (const char* name, int prio = 0);
+
+ static const string name;
+ static const uint64_t version;
};
}
}
diff --git a/build2/config/module.cxx b/build2/config/module.cxx
new file mode 100644
index 0000000..6d54e46
--- /dev/null
+++ b/build2/config/module.cxx
@@ -0,0 +1,66 @@
+// file : build2/config/module.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <build2/config/module>
+
+using namespace std;
+
+namespace build2
+{
+ namespace config
+ {
+ void module::
+ save_variable (const variable& var, uint64_t flags)
+ {
+ const string& n (var.name);
+
+ // First try to find the module with the name that is the longest
+ // prefix of this variable name.
+ //
+ auto& sm (saved_modules);
+ auto i (sm.end ());
+
+ if (!sm.empty ())
+ {
+ i = sm.upper_bound (n);
+
+ // Get the greatest less than, if any. We might still not be a
+ // suffix. And we still have to check the last element if
+ // upper_bound() returned end().
+ //
+ if (i == sm.begin () || !sm.key_comp ().prefix ((--i)->first, n))
+ i = sm.end ();
+ }
+
+ // If no module matched, then create one based on the variable name.
+ //
+ if (i == sm.end ())
+ {
+ // @@ For now with 'config.' prefix.
+ //
+ i = sm.insert (string (n, 0, n.find ('.', 7)));
+ }
+
+ // Don't insert duplicates. The config.import vars are particularly
+ // susceptible to duplication.
+ //
+ saved_variables& sv (i->second);
+ auto j (sv.find (var));
+
+ if (j == sv.end ())
+ sv.push_back (saved_variable {var, flags});
+ else
+ assert (j->flags == flags);
+ }
+
+ void module::
+ save_module (const char* name, int prio)
+ {
+ saved_modules.insert (string ("config.") += name, prio);
+ }
+
+ const string module::name ("config");
+ const uint64_t module::version (1);
+ }
+}
diff --git a/build2/config/operation b/build2/config/operation
index f2f0538..425869c 100644
--- a/build2/config/operation
+++ b/build2/config/operation
@@ -16,6 +16,13 @@ namespace build2
{
extern const meta_operation_info configure;
extern const meta_operation_info disfigure;
+
+ const string&
+ preprocess_create (const variable_overrides&,
+ values&,
+ vector_view<opspec>&,
+ bool,
+ const location&);
}
}
diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx
index a8874fd..2285dd9 100644
--- a/build2/config/operation.cxx
+++ b/build2/config/operation.cxx
@@ -7,6 +7,7 @@
#include <set>
#include <build2/file>
+#include <build2/spec>
#include <build2/scope>
#include <build2/target>
#include <build2/context>
@@ -413,7 +414,6 @@ namespace build2
// disfigure
//
-
static void
load_project (scope& root)
{
@@ -640,5 +640,286 @@ namespace build2
nullptr, // operation post
nullptr, // meta-operation post
};
+
+ // create
+ //
+ static void
+ create_project (const dir_path& d,
+ const strings& bmod, // Bootstrap modules.
+ const strings& rmod, // Root modules.
+ const variable_overrides& var_ovs)
+ {
+ // If the directory exists, verify it's empty. Otherwise, create it.
+ //
+ if (exists (d))
+ {
+ if (!empty (d))
+ fail << "directory " << d << " exists and is not empty";
+ }
+ else
+ mkdir_p (d);
+
+ // Create the build/ subdirectory.
+ //
+ mkdir (d / build_dir);
+
+ // Write build/bootstrap.build.
+ //
+ {
+ path f (d / bootstrap_file);
+
+ if (verb)
+ text << (verb >= 2 ? "cat >" : "save ") << f;
+
+ try
+ {
+ ofdstream ofs (f);
+
+ ofs << "# Generated by the create meta-operation. Edit if you " <<
+ "know what you are doing." << endl
+ << "#" << endl
+ << "project =" << endl
+ << endl
+ << "using config" << endl;
+
+ for (const string& m: bmod)
+ {
+ if (m != "config")
+ ofs << "using " << m << endl;
+ }
+
+ ofs.close ();
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to write " << f << ": " << e;
+ }
+ }
+
+ // Write build/root.build.
+ //
+ {
+ path f (d / root_file);
+
+ if (verb)
+ text << (verb >= 2 ? "cat >" : "save ") << f;
+
+ try
+ {
+ ofdstream ofs (f);
+
+ ofs << "# Generated by the create meta-operation. Edit if you " <<
+ "know what you are doing." << endl
+ << "#" << endl;
+
+ for (const string& cm: rmod)
+ {
+ // If the module name start with '?', then use optional load.
+ //
+ bool opt (cm.front () == '?');
+ string m (cm, opt ? 1 : 0);
+
+ // Append .config unless the module name ends with '.', in which
+ // case strip it.
+ //
+ if (m.back () == '.')
+ m.pop_back ();
+ else
+ m += ".config";
+
+ ofs << "using" << (opt ? "?" : "") << " " << m << endl;
+ }
+
+ ofs.close ();
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to write " << f << ": " << e;
+ }
+ }
+
+ // Write root buildfile.
+ //
+ {
+ path f (d / "buildfile");
+
+ if (verb)
+ text << (verb >= 2 ? "cat >" : "save ") << f;
+
+ try
+ {
+ ofdstream ofs (f);
+
+ ofs << "# Generated by the create meta-operation. Edit if you " <<
+ "know what you are doing." << endl
+ << "#" << endl
+ << "./: {*/ -build/}" << endl;
+
+ ofs.close ();
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to write " << f << ": " << e;
+ }
+ }
+
+ // Well, this wasn't too bad. There is just one little snag: since there
+ // aren't any sub-projects yet, any config.import.* values that the user
+ // may want to specify won't be saved in config.build. So let's go ahead
+ // and mark them all to be saved. To do this, however, we need the
+ // config module (which is where this information is stored). And the
+ // module is created by init() during bootstrap. So what we are going to
+ // do is bootstrap the newly created project, similar to the way main()
+ // does it (except here we can omit all the guessing/sanity checks).
+ //
+ current_oname = &empty_string; // Make sure valid.
+
+ scope& gs (*scope::global_);
+ scope& rs (create_root (gs, d, d));
+
+ if (!bootstrapped (rs))
+ {
+ bootstrap_out (rs);
+ setup_root (rs);
+ bootstrap_src (rs);
+ }
+
+ module& m (*rs.modules.lookup<module> (module::name));
+
+ // Save all the global config.import.* variables.
+ //
+ variable_pool& vp (var_pool.rw (rs));
+ for (auto p (gs.vars.find_namespace (vp.insert ("config.import")));
+ p.first != p.second;
+ ++p.first)
+ {
+ const variable& var (p.first->first);
+
+ // Annoyingly, this is one of the __override/__prefix/__suffix
+ // values. So we strip the last component.
+ //
+ size_t n (var.name.size ());
+
+ if (var.name.compare (n - 11, 11, ".__override") == 0)
+ n -= 11;
+ else if (var.name.compare (n - 9, 9, ".__prefix") == 0)
+ n -= 9;
+ else if (var.name.compare (n - 9, 9, ".__suffix") == 0)
+ n -= 9;
+
+ m.save_variable (*vp.find (string (var.name, 0, n)));
+ }
+
+ // Now project-specific. For now we just save all of them and let
+ // save_config() above weed out the ones that don't apply.
+ //
+ for (const variable_override& vo: var_ovs)
+ {
+ const variable& var (vo.var);
+
+ if (var.name.compare (0, 14, "config.import.") == 0)
+ m.save_variable (var);
+ }
+ }
+
+ const string&
+ preprocess_create (const variable_overrides& var_ovs,
+ values& params,
+ vector_view<opspec>& spec,
+ bool lifted,
+ const location& l)
+ {
+ tracer trace ("create_project");
+
+ // The overall plan is to create the project(s), update the buildspec,
+ // clear the parameters, and then continue as if we were the configure
+ // meta-operation.
+
+ // Start with process parameters. The first parameter, if any, is a list
+ // of root.build modules. The second parameter, if any, is a list of
+ // bootstrap.build modules. If the second is not specified, then the
+ // default is test and install (config is mandatory).
+ //
+ strings bmod {"test", "install"};
+ strings rmod;
+ try
+ {
+ size_t n (params.size ());
+
+ if (n > 0)
+ rmod = convert<strings> (move (params[0]));
+
+ if (n > 1)
+ bmod = convert<strings> (move (params[1]));
+
+ if (n > 2)
+ fail (l) << "unexpected parameters for meta-operation create";
+ }
+ catch (const invalid_argument& e)
+ {
+ fail (l) << "invalid module name: " << e.what ();
+ }
+
+ // Now handle each target in each operation spec.
+ //
+ for (const opspec& os: spec)
+ {
+ // First do some sanity checks: there should be no explicit operation
+ // and our targets should all be directories.
+ //
+ if (!lifted && !os.name.empty ())
+ fail (l) << "explicit operation specified for meta-operation create";
+
+ for (const targetspec& ts: os)
+ {
+ const name& tn (ts.name);
+
+ // Figure out the project directory. This code must be consistent
+ // with find_target_type() and other places.
+ //
+ dir_path d;
+
+ if (tn.simple () &&
+ (tn.empty () || tn.value == "." || tn.value == ".."))
+ d = dir_path (tn.value);
+ else if (tn.directory ())
+ d = tn.dir;
+ else if (tn.typed () && tn.type == "dir")
+ d = tn.dir / dir_path (tn.value);
+ else
+ fail(l) << "non-directory target '" << ts << "' in "
+ << "meta-operation create";
+
+ if (d.relative ())
+ d = work / d;
+
+ d.normalize (true);
+
+ // If src_base was explicitly specified, make sure it is the same as
+ // the project directory.
+ //
+ if (!ts.src_base.empty ())
+ {
+ dir_path s (ts.src_base);
+
+ if (s.relative ())
+ s = work / s;
+
+ s.normalize (true);
+
+ if (s != d)
+ fail(l) << "different src/out directories for target '" << ts
+ << "' in meta-operation create";
+ }
+
+ l5 ([&]{trace << "creating project in " << d;});
+
+ create_project (d, bmod, rmod, var_ovs);
+ }
+ }
+
+ params.clear ();
+ return configure.name;
+ }
}
}
diff --git a/build2/config/utility.cxx b/build2/config/utility.cxx
index 08faeeb..ea66e92 100644
--- a/build2/config/utility.cxx
+++ b/build2/config/utility.cxx
@@ -139,47 +139,7 @@ namespace build2
// could we be configuring it? Good question.
//
if (module* m = r.modules.lookup<module> (module::name))
- {
- const string& n (var.name);
-
- // First try to find the module with the name that is the longest
- // prefix of this variable name.
- //
- saved_modules& sm (m->saved_modules);
- auto i (sm.end ());
-
- if (!sm.empty ())
- {
- i = sm.upper_bound (n);
-
- // Get the greatest less than, if any. We might still not be a
- // suffix. And we still have to check the last element if
- // upper_bound() returned end().
- //
- if (i == sm.begin () || !sm.key_comp ().prefix ((--i)->first, n))
- i = sm.end ();
- }
-
- // If no module matched, then create one based on the variable name.
- //
- if (i == sm.end ())
- {
- // @@ For now with 'config.' prefix.
- //
- i = sm.insert (string (n, 0, n.find ('.', 7)));
- }
-
- // Don't insert duplicates. The config.import vars are particularly
- // susceptible to duplication.
- //
- saved_variables& sv (i->second);
- auto j (sv.find (var));
-
- if (j == sv.end ())
- sv.push_back (saved_variable {var, flags});
- else
- assert (j->flags == flags);
- }
+ m->save_variable (var, flags);
}
void
@@ -189,7 +149,7 @@ namespace build2
return;
if (module* m = r.modules.lookup<module> (module::name))
- m->saved_modules.insert (string ("config.") += name, prio);
+ m->save_module (name, prio);
}
}
}
diff --git a/build2/context.cxx b/build2/context.cxx
index e4e7054..fa3edfa 100644
--- a/build2/context.cxx
+++ b/build2/context.cxx
@@ -18,6 +18,8 @@
#include <build2/lexer>
#include <build2/parser>
+#include <build2/config/operation> // config::preprocess_create().
+
using namespace std;
using namespace butl;
@@ -226,6 +228,8 @@ namespace build2
meta_operation_table.insert ("perform");
meta_operation_table.insert ("configure");
meta_operation_table.insert ("disfigure");
+ meta_operation_table.insert (
+ meta_operation_data ("create", &config::preprocess_create));
meta_operation_table.insert ("dist");
operation_table.clear ();
@@ -451,7 +455,7 @@ namespace build2
fail << "typed override of variable " << n;
// Global and scope overrides we can enter directly. Project ones will
- // be entered by the caller for for each amalgamation/project.
+ // be entered by the caller for each amalgamation/project.
//
if (c == '!' || !dir.empty ())
{
diff --git a/build2/operation b/build2/operation
index 1f3e217..3558c0b 100644
--- a/build2/operation
+++ b/build2/operation
@@ -106,7 +106,8 @@ namespace build2
const meta_operation_id perform_id = 2;
const meta_operation_id configure_id = 3;
const meta_operation_id disfigure_id = 4;
- const meta_operation_id dist_id = 5;
+ const meta_operation_id create_id = 5;
+ const meta_operation_id dist_id = 6;
// The default operation is a special marker that can be used to indicate
// that no operation was explicitly specified by the user. If adding
@@ -356,7 +357,8 @@ namespace build2
// If lifted is true then the operation name in opspec is bogus (has
// been lifted) and the default/empty name should be assumed instead.
//
- using process_func = const string& (values&,
+ using process_func = const string& (const variable_overrides&,
+ values&,
vector_view<opspec>&,
bool lifted,
const location&);
diff --git a/build2/scope b/build2/scope
index c15d973..1ce28fc 100644
--- a/build2/scope
+++ b/build2/scope
@@ -260,13 +260,8 @@ namespace build2
scope&
global () {return *global_;}
- private:
- static scope* global_;
-
- // Entities that can access bypassing the lock proof.
- //
- friend int main (int, char*[]);
- friend variable_overrides reset (const strings&);
+ public:
+ static scope* global_; // Normally not accessed directly.
private:
friend class parser;
diff --git a/build2/variable.txx b/build2/variable.txx
index 5a1b79c..038f107 100644
--- a/build2/variable.txx
+++ b/build2/variable.txx
@@ -44,7 +44,7 @@ namespace build2
return move (v).as<T> ();
throw invalid_argument (
- string ("invalid ") + value_traits<T>::type_name +
+ string ("invalid ") + value_traits<T>::value_type.name +
" value: conversion from " + v.type->name);
}
diff --git a/unit-tests/function/buildfile b/unit-tests/function/buildfile
index 8c14899..e61737c 100644
--- a/unit-tests/function/buildfile
+++ b/unit-tests/function/buildfile
@@ -9,7 +9,7 @@ src = token lexer diagnostics utility variable name b-options types-parsers \
context scope parser target operation rule prerequisite file module function \
functions-builtin functions-path functions-process-path functions-string \
functions-target-triplet algorithm search dump filesystem scheduler \
-config/{utility init operation} spec
+config/{utility init operation module} spec
exe{driver}: cxx{driver} ../../build2/cxx{$src} ../../build2/liba{b} $libs test{call syntax}
diff --git a/unit-tests/lexer/buildfile b/unit-tests/lexer/buildfile
index efcd45c..d3bbbfe 100644
--- a/unit-tests/lexer/buildfile
+++ b/unit-tests/lexer/buildfile
@@ -9,7 +9,7 @@ src = token lexer diagnostics utility variable name b-options types-parsers \
context scope parser target operation rule prerequisite file module function \
functions-builtin functions-path functions-process-path functions-string \
functions-target-triplet algorithm search dump filesystem scheduler \
-config/{utility init operation} spec
+config/{utility init operation module} spec
exe{driver}: cxx{driver} ../../build2/cxx{$src} ../../build2/liba{b} $libs \
test{*}
diff --git a/unit-tests/scheduler/buildfile b/unit-tests/scheduler/buildfile
index 2742c73..0a12414 100644
--- a/unit-tests/scheduler/buildfile
+++ b/unit-tests/scheduler/buildfile
@@ -9,7 +9,7 @@ src = token lexer diagnostics utility variable name b-options types-parsers \
context scope parser target operation rule prerequisite file module function \
functions-builtin functions-path functions-process-path functions-string \
functions-target-triplet algorithm search dump filesystem scheduler \
-config/{utility init operation} spec
+config/{utility init operation module} spec
exe{driver}: cxx{driver} ../../build2/cxx{$src} ../../build2/liba{b} $libs
diff --git a/unit-tests/test/script/lexer/buildfile b/unit-tests/test/script/lexer/buildfile
index f46f835..c5f3d2a 100644
--- a/unit-tests/test/script/lexer/buildfile
+++ b/unit-tests/test/script/lexer/buildfile
@@ -9,7 +9,7 @@ src = token lexer diagnostics utility variable name b-options types-parsers \
context scope parser target operation rule prerequisite file module function \
functions-builtin functions-path functions-process-path functions-string \
functions-target-triplet algorithm search dump filesystem scheduler \
-config/{utility init operation} test/script/{token lexer} spec
+config/{utility init operation module} test/script/{token lexer} spec
exe{driver}: cxx{driver} ../../../../build2/cxx{$src} ../../../../build2/liba{b} $libs \
test{command-line first-token second-token command-expansion variable-line \
diff --git a/unit-tests/test/script/parser/buildfile b/unit-tests/test/script/parser/buildfile
index 1c06ad2..5ca2d0b 100644
--- a/unit-tests/test/script/parser/buildfile
+++ b/unit-tests/test/script/parser/buildfile
@@ -8,7 +8,7 @@ import libs = libbutl%lib{butl}
src = token lexer parser diagnostics utility variable name context target \
scope prerequisite file module operation rule b-options algorithm search \
filesystem function functions-builtin functions-path functions-process-path \
-functions-string functions-target-triplet config/{utility init operation} \
+functions-string functions-target-triplet config/{utility init operation module} \
dump types-parsers test/{target script/{token lexer parser regex script}} \
scheduler spec