From 83f8b6a45fc041586819537ca86be2eb534f79b0 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 16 Mar 2017 18:14:16 +0200 Subject: Implement create meta-operation --- build2/config/operation.cxx | 283 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 282 insertions(+), 1 deletion(-) (limited to 'build2/config/operation.cxx') diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx index a8874fd6..2285dd93 100644 --- a/build2/config/operation.cxx +++ b/build2/config/operation.cxx @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -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::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& 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 (move (params[0])); + + if (n > 1) + bmod = convert (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; + } } } -- cgit