aboutsummaryrefslogtreecommitdiff
path: root/build2/config
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-03-16 18:14:16 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-03-17 09:37:20 +0200
commit83f8b6a45fc041586819537ca86be2eb534f79b0 (patch)
tree863800b9d5bd6a5d76dcdbe107edafca4cbf18fc /build2/config
parentf57ec74251b31cc532dc095801c1da17a7d8e0ac (diff)
Implement create meta-operation
Diffstat (limited to 'build2/config')
-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
6 files changed, 392 insertions, 65 deletions
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);
}
}
}