aboutsummaryrefslogtreecommitdiff
path: root/build2/config/operation.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build2/config/operation.cxx')
-rw-r--r--build2/config/operation.cxx283
1 files changed, 282 insertions, 1 deletions
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;
+ }
}
}