From 9fb791e9fad6c63fc1dac49f4d05ae63b8a3db9b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 5 Jan 2016 11:55:15 +0200 Subject: Rename build directory/namespace to build2 --- build2/b.cxx | 855 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 855 insertions(+) create mode 100644 build2/b.cxx (limited to 'build2/b.cxx') diff --git a/build2/b.cxx b/build2/b.cxx new file mode 100644 index 0000000..42c59f7 --- /dev/null +++ b/build2/b.cxx @@ -0,0 +1,855 @@ +// file : build2/b.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include // tzset() +#include // strerror() + +#include // getenv() +#include // getuid() +#include // uid_t +#include // struct passwd, getpwuid() + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace std; + +#include +#include +#include +#include +#include +#include +#include + +using namespace build2; + +int +main (int argc, char* argv[]) +{ + try + { + tracer trace ("main"); + + cl::argv_scanner scan (argc, argv, true); + options ops (scan); + + // Version. + // + if (ops.version ()) + { + cout << "build2 " << BUILD2_VERSION_STR<< endl + << "libbutl " << LIBBUTL_VERSION_STR << endl + << "Copyright (c) 2014-2015 Code Synthesis Ltd" << endl + << "This is free software released under the MIT license." << endl; + return 0; + } + + // Help. + // + if (ops.help ()) + { + ostream& o (cout); + + o << "Usage: " << argv[0] << " [options] [variables] [buildspec]" << endl + << "Options:" << endl; + + options::print_usage (o); + return 0; + } + + // Diagnostics verbosity. + // + verb = ops.verbose_specified () + ? ops.verbose () + : ops.v () ? 2 : ops.q () ? 0 : 1; + + // Initialize time conversion data that is used by localtime_r(). + // + tzset (); + + // Register builtin modules. + // + builtin_modules["config"] = module_functions {&config::config_boot, + &config::config_init}; + builtin_modules["dist"] = module_functions {&dist::dist_boot, + &dist::dist_init}; + builtin_modules["test"] = module_functions {&test::test_boot, + &test::test_init}; + builtin_modules["install"] = module_functions {&install::install_boot, + &install::install_init}; + + builtin_modules["bin"] = module_functions {nullptr, &bin::bin_init}; + builtin_modules["cxx"] = module_functions {nullptr, &cxx::cxx_init}; + builtin_modules["cli"] = module_functions {nullptr, &cli::cli_init}; + + // Figure out work and home directories. + // + work = dir_path::current (); + + if (const char* h = getenv ("HOME")) + home = dir_path (h); + else + { + struct passwd* pw (getpwuid (getuid ())); + + if (pw == nullptr) + { + const char* msg (strerror (errno)); + fail << "unable to determine home directory: " << msg; + } + + home = dir_path (pw->pw_dir); + } + + if (verb >= 5) + { + trace << "work dir: " << work; + trace << "home dir: " << home; + } + + // Initialize the dependency state. + // + reset (); + + // Parse command line variables. They should come before the + // buildspec. + // + int argi (1); + for (; argi != argc; argi++) + { + const char* s (argv[argi]); + + istringstream is (s); + is.exceptions (istringstream::failbit | istringstream::badbit); + lexer l (is, ""); + token t (l.next ()); + + if (t.type == token_type::eos) + continue; // Whitespace-only argument. + + // Unless this is a name followed by = or +=, assume it is + // a start of the buildspec. + // + if (t.type != token_type::name) + break; + + token_type tt (l.next ().type); + + if (tt != token_type::equal && + tt != token_type::equal_plus && + tt != token_type::plus_equal) + break; + + parser p; + t = p.parse_variable (l, *global_scope, t.value, tt); + + if (t.type != token_type::eos) + fail << "unexpected " << t << " in variable " << s; + } + + // Parse the buildspec. + // + buildspec bspec; + { + // Merge all the individual buildspec arguments into a single + // string. Instead, we could also parse them individually ( + // and merge the result). The benefit of doing it this way + // is potentially better diagnostics (i.e., we could have + // used , to give the idea about + // which argument is invalid). + // + string s; + for (; argi != argc;) + { + s += argv[argi]; + if (++argi != argc) + s += ' '; + } + + try + { + istringstream is (s); + is.exceptions (istringstream::failbit | istringstream::badbit); + + parser p; + bspec = p.parse_buildspec (is, ""); + } + catch (const istringstream::failure&) + { + fail << "unable to parse buildspec '" << s << "'"; + } + } + + level5 ([&]{trace << "buildspec: " << bspec;}); + + if (bspec.empty ()) + bspec.push_back (metaopspec ()); // Default meta-operation. + + for (metaopspec& ms: bspec) + { + if (ms.empty ()) + ms.push_back (opspec ()); // Default operation. + + meta_operation_id mid (0); // Not yet translated. + const meta_operation_info* mif (nullptr); + + bool lifted (false); // See below. + + for (opspec& os: ms) + { + const location l ("", 1, 0); //@@ TODO + + if (os.empty ()) // Default target: dir{}. + os.push_back (targetspec (name ("dir", string ()))); + + operation_id oid (0); // Not yet translated. + const operation_info* oif (nullptr); + + operation_id pre_oid (0); + const operation_info* pre_oif (nullptr); + + operation_id post_oid (0); + const operation_info* post_oif (nullptr); + + // We do meta-operation and operation batches sequentially (no + // parallelism). But multiple targets in an operation batch + // can be done in parallel. + // + action_targets tgs; + tgs.reserve (os.size ()); + + // If the previous operation was lifted to meta-operation, + // end the meta-operation batch. + // + if (lifted) + { + if (mif->meta_operation_post != nullptr) + mif->meta_operation_post (); + + level5 ([&]{trace << "end meta-operation batch " << mif->name + << ", id " << static_cast (mid);}); + + mid = 0; + lifted = false; + } + + for (targetspec& ts: os) + { + name& tn (ts.name); + + // First figure out the out_base of this target. The logic + // is as follows: if a directory was specified in any form, + // then that's the out_base. Otherwise, we check if the name + // value has a directory prefix. This has a good balance of + // control and the expected result in most cases. + // + dir_path out_base (tn.dir); + if (out_base.empty ()) + { + 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(). + // + if (v.empty () || v == "." || v == ".." || tn.type == "dir") + out_base = dir_path (v); + // + // Otherwise, if this is a simple name, see if there is a + // directory part in value. + // + else if (tn.untyped ()) + { + // We cannot assume it is a valid filesystem name so we + // will have to do the splitting manually. + // + path::size_type i (path::traits::rfind_separator (v)); + + if (i != string::npos) + out_base = dir_path (v, i != 0 ? i : 1); // Special case: "/". + } + } + + if (out_base.relative ()) + out_base = work / out_base; + + out_base.normalize (); + + // The order in which we determine the roots depends on whether + // src_base was specified explicitly. There will also be a few + // cases where we are guessing things that can turn out wrong. + // Keep track of that so that we can issue more extensive + // diagnostics for such cases. + // + bool guessing (false); + dir_path src_root; + dir_path out_root; + + dir_path& src_base (ts.src_base); // Update it in buildspec. + + if (!src_base.empty ()) + { + // Make sure it exists. While we will fail further down + // if it doesn't, the diagnostics could be confusing (e.g., + // unknown operation because we don't load bootstrap.build). + // + if (!dir_exists (src_base)) + fail << "src_base directory " << src_base << " does not exist"; + + if (src_base.relative ()) + src_base = work / src_base; + + src_base.normalize (); + + // If the src_base was explicitly specified, search for src_root. + // + src_root = find_src_root (src_base); + + // If not found, assume this is a simple project with src_root + // being the same as src_base. + // + if (src_root.empty ()) + { + src_root = src_base; + out_root = out_base; + } + else + // Calculate out_root based on src_root/src_base. + // + try + { + out_root = out_base.directory (src_base.leaf (src_root)); + } + catch (const invalid_path&) + { + fail << "out_base suffix does not match src_root" << + info << "src_root: " << src_root << + info << "out_base: " << out_base; + } + } + else + { + // If no src_base was explicitly specified, search for out_root. + // + bool src; + out_root = find_out_root (out_base, &src); + + // If not found (i.e., we have no idea where the roots are), + // then this can mean two things: an in-tree build of a + // simple project or a fresh out-of-tree build. To test for + // the latter, try to find src_root starting from work. If + // we can't, then assume it is the former case. + // + if (out_root.empty ()) + { + src_root = find_src_root (work); + + if (!src_root.empty ()) + { + src_base = work; + + if (src_root != src_base) + { + try + { + out_root = out_base.directory (src_base.leaf (src_root)); + } + catch (const invalid_path&) + { + fail << "out_base directory suffix does not match src_base" + << info << "src_base is " << src_base + << info << "src_root is " << src_root + << info << "out_base is " << out_base + << info << "consider explicitly specifying src_base " + << "for " << tn; + } + } + else + out_root = out_base; + } + else + src_root = src_base = out_root = out_base; + + guessing = true; + } + else if (src) + src_root = out_root; + } + + // Now we know out_root and, if it was explicitly specified + // or the same as out_root, src_root. The next step is to + // create the root scope and load the out_root bootstrap + // files, if any. Note that we might already have done this + // as a result of one of the preceding target processing. + // + // If we know src_root, set that variable as well. This could + // be of use to the bootstrap files (other than src-root.build, + // which, BTW, doesn't need to exist if src_root == out_root). + // + scope& rs (create_root (out_root, src_root)); + + bootstrap_out (rs); + + // See if the bootstrap process set/changed 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 (as (v)); + + if (src_root.empty ()) + src_root = p; + else if (src_root != p) + fail << "bootstrapped src_root " << p << " does not match " + << "specified " << src_root; + } + else + { + // Neither bootstrap nor the user produced src_root. + // + if (src_root.empty ()) + { + // If it also wasn't explicitly specified, see if it is + // the same as out_root. + // + if (is_src_root (out_root)) + src_root = out_root; + else + { + // If not, then assume we are running from src_base + // and calculate src_root based on out_root/out_base. + // + src_base = work; + src_root = src_base.directory (out_base.leaf (out_root)); + guessing = true; + } + } + + v = src_root; + } + + setup_root (rs); + + // At this stage we should have both roots and out_base figured + // out. If src_base is still undetermined, calculate it. + // + if (src_base.empty ()) + src_base = src_root / out_base.leaf (out_root); + + // Now that we have src_root, load the src_root bootstrap file, + // if there is one. + // + bool bootstrapped (bootstrap_src (rs)); + + // Check that out_root that we have found is the innermost root + // for this project. If it is not, then it means we are trying + // to load a disfigured sub-project and that we do not support. + // Why don't we support it? Because things are already complex + // enough here. + // + if (auto l = rs.vars["subprojects"]) + { + for (const name& n: *l) + { + if (n.pair != '\0') + continue; // Skip project names. + + if (out_base.sub (out_root / n.dir)) + fail << tn << " is in a subproject of " << out_root << + info << "explicitly specify src_base for this target"; + } + } + + // Create and bootstrap outer roots if any. Loading is done + // by load_root_pre() (that would normally be called by the + // meta-operation's load() callback below). + // + create_bootstrap_outer (rs); + + // The src bootstrap should have loaded all the modules that + // may add new meta/operations. So at this stage they should + // all be known. We store the combined action id in uint8_t; + // see for details. + // + assert (operation_table.size () <= 128); + assert (meta_operation_table.size () <= 128); + + // Since we now know all the names of meta-operations and + // operations, "lift" names that we assumed (from buildspec + // syntax) were operations but are actually meta-operations. + // Also convert empty names (which means they weren't explicitly + // specified) to the defaults and verify that all the names are + // known. + // + { + const auto& mn (ms.name); + const auto& on (os.name); + + meta_operation_id m (0); + operation_id o (0); + + if (!on.empty ()) + { + m = meta_operation_table.find (on); + + if (m != 0) + { + if (!mn.empty ()) + fail (l) << "nested meta-operation " << mn + << '(' << on << ')'; + + if (!lifted) // If this is the first target. + { + // End the previous meta-operation batch if there was one + // and start a new one. + // + if (mid != 0) + { + assert (oid == 0); + + if (mif->meta_operation_post != nullptr) + mif->meta_operation_post (); + + level5 ([&]{trace << "end meta-operation batch " + << mif->name << ", id " + << static_cast (mid);}); + + mid = 0; + } + + lifted = true; // Flag to also end it; see above. + } + } + else + { + o = operation_table.find (on); + + if (o == 0) + { + diag_record dr; + dr << fail (l) << "unknown operation " << on; + + // If we guessed src_root and didn't load anything during + // bootstrap, then this is probably a meta-operation that + // would have been added by the module if src_root was + // correct. + // + if (guessing && !bootstrapped) + dr << info << "consider explicitly specifying src_base " + << "for " << tn; + } + } + } + + if (!mn.empty ()) + { + m = meta_operation_table.find (mn); + + if (m == 0) + { + diag_record dr; + dr << fail (l) << "unknown meta-operation " << mn; + + // Same idea as for the operation case above. + // + if (guessing && !bootstrapped) + dr << info << "consider explicitly specifying src_base " + << "for " << tn; + } + } + + // The default meta-operation is perform. The default + // operation is assigned by the meta-operation below. + // + if (m == 0) + m = perform_id; + + // If this is the first target in the meta-operation batch, + // then set the batch meta-operation id. + // + if (mid == 0) + { + mid = m; + mif = rs.meta_operations[m]; + + if (mif == nullptr) + fail (l) << "target " << tn << " does not support meta-" + << "operation " << meta_operation_table[m]; + + level5 ([&]{trace << "start meta-operation batch " << mif->name + << ", id " << static_cast (mid);}); + + if (mif->meta_operation_pre != nullptr) + mif->meta_operation_pre (); + + current_mif = mif; + } + // + // Otherwise, check that all the targets in a meta-operation + // batch have the same meta-operation implementation. + // + else + { + const meta_operation_info* mi (rs.meta_operations[mid]); + + if (mi == nullptr) + fail (l) << "target " << tn << " does not support meta-" + << "operation " << meta_operation_table[mid]; + + if (mi != mif) + fail (l) << "different implementations of meta-operation " + << mif->name << " in the same meta-operation batch"; + } + + // If this is the first target in the operation batch, then set + // the batch operation id. + // + if (oid == 0) + { + auto lookup = + [&rs, &l, &tn] (operation_id o) -> const operation_info* + { + const operation_info* r (rs.operations[o]); + + if (r == nullptr) + fail (l) << "target " << tn << " does not support " + << "operation " << operation_table[o]; + return r; + }; + + if (o == 0) + o = default_id; + + oif = lookup (o); + + level5 ([&]{trace << "start operation batch " << oif->name + << ", id " << static_cast (o);}); + + // Allow the meta-operation to translate the operation. + // + if (mif->operation_pre != nullptr) + oid = mif->operation_pre (o); + else // Otherwise translate default to update. + oid = (o == default_id ? update_id : o); + + if (o != oid) + { + oif = lookup (oid); + level5 ([&]{trace << "operation translated to " << oif->name + << ", id " << static_cast (oid);}); + } + + // Handle pre/post operations. + // + if (oif->pre != nullptr && (pre_oid = oif->pre (mid)) != 0) + { + assert (pre_oid != default_id); + pre_oif = lookup (pre_oid); + } + + if (oif->post != nullptr && (post_oid = oif->post (mid)) != 0) + { + assert (post_oid != default_id); + post_oif = lookup (post_oid); + } + } + // + // Similar to meta-operations, check that all the targets in + // an operation batch have the same operation implementation. + // + else + { + auto check = + [&rs, &l, &tn] (operation_id o, const operation_info* i) + { + const operation_info* r (rs.operations[o]); + + if (r == nullptr) + fail (l) << "target " << tn << " does not support " + << "operation " << operation_table[o]; + + if (r != i) + fail (l) << "different implementations of operation " + << i->name << " in the same operation batch"; + }; + + check (oid, oif); + + if (pre_oid != 0) + check (pre_oid, pre_oif); + + if (post_oid != 0) + check (post_oid, post_oif); + } + } + + if (verb >= 5) + { + trace << "target " << tn << ':'; + trace << " out_base: " << out_base; + trace << " src_base: " << src_base; + trace << " out_root: " << out_root; + trace << " src_root: " << src_root; + } + + path bf (src_base / path ("buildfile")); + + // If we were guessing src_base, check that the buildfile + // exists and if not, issue more detailed diagnostics. + // + if (guessing && !file_exists (bf)) + fail << bf << " does not exist" + << info << "consider explicitly specifying src_base " + << "for " << tn; + + // Load the buildfile. + // + mif->load (bf, rs, out_base, src_base, l); + + // Next search and match the targets. We don't want to start + // building before we know how to for all the targets in this + // operation batch. + // + { + scope& bs (scopes.find (out_base)); + + const string* e; + const target_type* ti (bs.find_target_type (tn, e)); + + if (ti == nullptr) + fail (l) << "unknown target type " << tn.type; + + // If the directory is relative, assume it is relative to work + // (must be consistent with how we derived out_base above). + // + dir_path& d (tn.dir); + + if (d.relative ()) + d = work / d; + + d.normalize (); + + mif->search (rs, target_key {ti, &d, &tn.value, &e}, l, tgs); + } + } + + if (pre_oid != 0) + { + level5 ([&]{trace << "start pre-operation batch " << pre_oif->name + << ", id " << static_cast (pre_oid);}); + + if (mif->operation_pre != nullptr) + mif->operation_pre (pre_oid); // Cannot be translated. + + current_inner_oif = pre_oif; + current_outer_oif = oif; + current_mode = pre_oif->mode; + dependency_count = 0; + + action a (mid, pre_oid, oid); + + mif->match (a, tgs); + mif->execute (a, tgs, true); // Run quiet. + + if (mif->operation_post != nullptr) + mif->operation_post (pre_oid); + + level5 ([&]{trace << "end pre-operation batch " << pre_oif->name + << ", id " << static_cast (pre_oid);}); + } + + current_inner_oif = oif; + current_outer_oif = nullptr; + current_mode = oif->mode; + dependency_count = 0; + + action a (mid, oid, 0); + + mif->match (a, tgs); + mif->execute (a, tgs, verb == 0); + + if (post_oid != 0) + { + level5 ([&]{trace << "start post-operation batch " << post_oif->name + << ", id " << static_cast (post_oid);}); + + if (mif->operation_pre != nullptr) + mif->operation_pre (post_oid); // Cannot be translated. + + current_inner_oif = post_oif; + current_outer_oif = oif; + current_mode = post_oif->mode; + dependency_count = 0; + + action a (mid, post_oid, oid); + + mif->match (a, tgs); + mif->execute (a, tgs, true); // Run quiet. + + if (mif->operation_post != nullptr) + mif->operation_post (post_oid); + + level5 ([&]{trace << "end post-operation batch " << post_oif->name + << ", id " << static_cast (post_oid);}); + } + + if (mif->operation_post != nullptr) + mif->operation_post (oid); + + level5 ([&]{trace << "end operation batch " << oif->name + << ", id " << static_cast (oid);}); + } + + if (mif->meta_operation_post != nullptr) + mif->meta_operation_post (); + + level5 ([&]{trace << "end meta-operation batch " << mif->name + << ", id " << static_cast (mid);}); + } + } + catch (const failed&) + { + return 1; // Diagnostics has already been issued. + } + /* + catch (const std::exception& e) + { + error << e.what (); + return 1; + } + */ +} -- cgit v1.1