diff options
Diffstat (limited to 'build2')
-rw-r--r-- | build2/b.cxx | 374 | ||||
-rw-r--r-- | build2/buildfile | 4 | ||||
-rw-r--r-- | build2/cli/init.cxx | 285 | ||||
-rw-r--r-- | build2/cli/init.hxx | 29 | ||||
-rw-r--r-- | build2/cli/module.hxx | 30 | ||||
-rw-r--r-- | build2/cli/rule.cxx | 338 | ||||
-rw-r--r-- | build2/cli/rule.hxx | 43 | ||||
-rw-r--r-- | build2/cli/target.cxx | 75 | ||||
-rw-r--r-- | build2/cli/target.hxx | 58 |
9 files changed, 324 insertions, 912 deletions
diff --git a/build2/b.cxx b/build2/b.cxx index f666b86..8decadf 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -54,8 +54,7 @@ #ifndef BUILD2_BOOTSTRAP # include <libbuild2/bash/init.hxx> - -# include <build2/cli/init.hxx> +# include <libbuild2/cli/init.hxx> #endif using namespace butl; @@ -149,32 +148,20 @@ namespace build2 s.begin_object (); - // Target. - // - { - // Change the stream verbosity (see print_lines() for details). - // - ostringstream os; - stream_verb (os, stream_verbosity (1, 0)); - os << t; - s.member ("target", os.str ()); - } - // Quoted target. // - { - names ns (t.as_name ()); // Note: potentially adds an extension. + s.member_name ("target"); + dump_quoted_target_name (s, t); - ostringstream os; - stream_verb (os, stream_verbosity (1, 0)); - to_stream (os, ns, quote_mode::effective, '@'); - s.member ("quoted_target", os.str ()); - } + // Display target. + // + s.member_name ("display_target"); + dump_display_target_name (s, t); - s.member ("target_type", t.key ().type->name, false /* check */); + s.member ("target_type", t.type ().name, false /* check */); if (t.is_a<dir> ()) - s.member ("target_path", t.key ().dir->string ()); + s.member ("target_path", t.dir.string ()); else if (const auto* pt = t.is_a<path_target> ()) s.member ("target_path", pt->path ().string ()); @@ -329,6 +316,7 @@ main (int argc, char* argv[]) init_diag (cmdl.verbosity, ops.silent (), cmdl.progress, + cmdl.diag_color, ops.no_line (), ops.no_column (), fdterm (stderr_fd ())); @@ -383,8 +371,8 @@ main (int argc, char* argv[]) load_builtin_module (&in::build2_in_load); #ifndef BUILD2_BOOTSTRAP - load_builtin_module (&cli::build2_cli_load); load_builtin_module (&bash::build2_bash_load); + load_builtin_module (&cli::build2_cli_load); #endif // Start up the scheduler and allocate lock shards. @@ -428,12 +416,17 @@ main (int argc, char* argv[]) pctx = nullptr; // Free first to reuse memory. } + optional<match_only_level> mo; + if (ops.load_only ()) mo = match_only_level::alias; + else if (ops.match_only ()) mo = match_only_level::all; + pctx.reset (new context (sched, mutexes, fcache, - ops.match_only (), + mo, ops.no_external_modules (), ops.dry_run (), + ops.no_diag_buffer (), !ops.serial_stop () /* keep_going */, cmdl.cmd_vars)); @@ -449,13 +442,14 @@ main (int argc, char* argv[]) // Parse the buildspec. // buildspec bspec; + path_name bspec_name ("<buildspec>"); try { istringstream is (cmdl.buildspec); is.exceptions (istringstream::failbit | istringstream::badbit); parser p (*pctx); - bspec = p.parse_buildspec (is, path_name ("<buildspec>")); + bspec = p.parse_buildspec (is, bspec_name); } catch (const io_error&) { @@ -467,18 +461,194 @@ main (int argc, char* argv[]) if (bspec.empty ()) bspec.push_back (metaopspec ()); // Default meta-operation. + // The reserve values were picked experimentally. They allow building a + // sample application that depends on Qt and Boost without causing a + // rehash. + // + // Note: omit reserving anything for the info meta-operation since it + // won't be loading the buildfiles and needs to be as fast as possible. + // + bool mo_info (bspec.size () == 1 && + bspec.front ().size () == 1 && + (bspec.front ().name == "info" || + (bspec.front ().name.empty () && + bspec.front ().front ().name == "info"))); + + if (!mo_info) + { + // Note: also adjust in bpkg if adjusting here. + // + pctx->reserve (context::reserves { + 30000 /* targets */, + 1100 /* variables */}); + } + + bool load_only (ops.load_only ()); + const path& buildfile (ops.buildfile_specified () ? ops.buildfile () : empty_path); bool dump_load (false); bool dump_match (false); - if (ops.dump_specified ()) + bool dump_match_pre (false); + bool dump_match_post (false); + for (const string& p: ops.dump ()) { - dump_load = ops.dump ().find ("load") != ops.dump ().end (); - dump_match = ops.dump ().find ("match") != ops.dump ().end (); + if (p == "load") dump_load = true; + else if (p == "match") dump_match = true; + else if (p == "match-pre") dump_match_pre = true; + else if (p == "match-post") dump_match_post = true; + else fail << "unknown phase '" << p << "' specified with --dump"; + } + + dump_format dump_fmt (dump_format::buildfile); + if (ops.dump_format_specified ()) + { + const string& f (ops.dump_format ()); + + if (f == "json-v0.1") + { +#ifdef BUILD2_BOOTSTRAP + fail << "json dump not supported in bootstrap build system"; +#endif + dump_fmt = dump_format::json; + } + else if (f != "buildfile") + { + diag_record dr (fail); + + dr << "unsupported format '" << f << "' specified with --dump-format"; + + if (f.compare (0, 4, "json") == 0) + dr << info << "supported json format version is json-v0.1"; + } } + auto dump = [&trace, &ops, dump_fmt] (context& ctx, optional<action> a) + { + const dir_paths& scopes (ops.dump_scope ()); + const vector<pair<name, optional<name>>>& targets (ops.dump_target ()); + + if (scopes.empty () && targets.empty ()) + build2::dump (ctx, a, dump_fmt); + else + { + auto comp_norm = [] (dir_path& d, const char* what) + { + try + { + if (d.relative ()) + d.complete (); + + d.normalize (); + } + catch (const invalid_path& e) + { + fail << "invalid path '" << e.path << "' specified with " << what; + } + }; + + // If exact is false then return any outer scope that contains this + // directory except for the global scope. + // + auto find_scope = [&ctx, &comp_norm] (dir_path& d, + bool exact, + const char* what) -> const scope* + { + comp_norm (d, what); + + // This is always the output directory (specifically, see the target + // case below). + // + const scope& s (ctx.scopes.find_out (d)); + + return ((exact ? s.out_path () == d : s != ctx.global_scope) + ? &s + : nullptr); + }; + + // Dump scopes. + // + for (dir_path d: scopes) + { + const scope* s (find_scope (d, true, "--dump-scope")); + + if (s == nullptr) + l5 ([&]{trace << "unknown target scope " << d + << " specified with --dump-scope";}); + + build2::dump (s, a, dump_fmt); + } + + // Dump targets. + // + for (const pair<name, optional<name>>& p: targets) + { + const target* t (nullptr); + + // Find the innermost known scope that contains this target. This + // is where we are going to resolve its type. + // + dir_path d (p.second ? p.second->dir : p.first.dir); + + if (const scope* s = find_scope (d, false, "--dump-target")) + { + // Complete relative directories in names. + // + name n (p.first), o; + + if (p.second) + { + comp_norm (n.dir, "--dump-target"); + o.dir = move (d); + } + else + n.dir = move (d); + + // Similar logic to parser::enter_target::find_target() as used by + // the dump directive. Except here we treat unknown target type as + // unknown target. + // + auto r (s->find_target_type (n, location ())); + + if (r.first != nullptr) + { + t = ctx.targets.find (*r.first, // target type + n.dir, + o.dir, + n.value, + r.second, // extension + trace); + + if (t == nullptr) + l5 ([&] + { + // @@ TODO: default_extension? + // + target::combine_name (n.value, r.second, false); + names ns {move (n)}; + if (p.second) + ns.push_back (move (o)); + + trace << "unknown target " << ns + << " specified with --dump-target"; + }); + } + else + l5 ([&]{trace << "unknown target type '" << n.type << "' in " + << *s << " specified with --dump-target";}); + + } + else + l5 ([&]{trace << "unknown target scope " << d + << " specified with --dump-target";}); + + build2::dump (t, a, dump_fmt); + } + } + }; + // If not NULL, then lifted points to the operation that has been "lifted" // to the meta-operaion (see the logic below for details). Skip is the // position of the next operation. @@ -495,7 +665,10 @@ main (int argc, char* argv[]) // Note that this constructor is cheap and so we rather call it always // instead of resorting to dynamic allocations. // - json::stream_serializer js (cout); + // Note also that we disable pretty-printing if there is also the JSON + // dump and thus we need to combine the two in the JSON Lines format. + // + json::stream_serializer js (cout, dump_fmt == dump_format::json ? 0 : 2); if (ops.structured_result_specified () && ops.structured_result () == structured_result_format::json) @@ -545,8 +718,7 @@ main (int argc, char* argv[]) context& ctx (*pctx); - const path p ("<buildspec>"); - const location l (p, 0, 0); //@@ TODO + const location l (bspec_name, 0, 0); //@@ TODO (also bpkg::pkg_configure()) meta_operation_id mid (0); // Not yet translated. const meta_operation_info* mif (nullptr); @@ -684,12 +856,19 @@ main (int argc, char* argv[]) } } - if (out_base.relative ()) - out_base = work / out_base; + try + { + if (out_base.relative ()) + out_base = work / out_base; - // This directory came from the command line so actualize it. - // - out_base.normalize (true); + // This directory came from the command line so actualize it. + // + out_base.normalize (true); + } + catch (const invalid_path& e) + { + fail << "invalid out_base directory '" << e.path << "'"; + } // The order in which we determine the roots depends on whether // src_base was specified explicitly. @@ -715,12 +894,19 @@ main (int argc, char* argv[]) if (!exists (src_base)) fail << "src_base directory " << src_base << " does not exist"; - if (src_base.relative ()) - src_base = work / src_base; + try + { + if (src_base.relative ()) + src_base = work / src_base; - // Also came from the command line, so actualize. - // - src_base.normalize (true); + // Also came from the command line, so actualize. + // + src_base.normalize (true); + } + catch (const invalid_path& e) + { + fail << "invalid src_base directory '" << e.path << "'"; + } // Make sure out_base is not a subdirectory of src_base. Who would // want to do that, you may ask. Well, you would be surprised... @@ -875,8 +1061,13 @@ main (int argc, char* argv[]) // Now that we have src_root, load the src_root bootstrap file, // if there is one. // + // As an optimization, omit discovering subprojects for the info + // meta-operation if not needed. + // bootstrap_pre (rs, altn); - bootstrap_src (rs, altn); + bootstrap_src (rs, altn, + nullopt /* amalgamation */, + !mo_info || info_subprojects (mparams) /*subprojects*/); // If this is a simple project, then implicitly load the test and // install modules. @@ -1093,23 +1284,38 @@ main (int argc, char* argv[]) if (oif->outer_id != 0) outer_oif = lookup (oif->outer_id); + if (!oparams.empty ()) + { + // Operation parameters belong to outer operation, if any. + // + auto* i (outer_oif != nullptr ? outer_oif : oif); + + if (i->operation_pre == nullptr) + fail (l) << "unexpected parameters for operation " << i->name; + } + // Handle pre/post operations. // if (auto po = oif->pre_operation) { - if ((orig_pre_oid = po (ctx, oparams, mid, l)) != 0) + if ((orig_pre_oid = po ( + ctx, + outer_oif == nullptr ? oparams : values {}, + mid, + l)) != 0) { assert (orig_pre_oid != default_id); pre_oif = lookup (orig_pre_oid); pre_oid = pre_oif->id; // De-alias. } } - else if (!oparams.empty ()) - fail (l) << "unexpected parameters for operation " << oif->name; if (auto po = oif->post_operation) { - if ((orig_post_oid = po (ctx, oparams, mid)) != 0) + if ((orig_post_oid = po ( + ctx, + outer_oif == nullptr ? oparams : values {}, + mid)) != 0) { assert (orig_post_oid != default_id); post_oif = lookup (orig_post_oid); @@ -1155,6 +1361,9 @@ main (int argc, char* argv[]) // defined there (common with non-intrusive project conversions // where everything is built from a single root buildfile). // + // Note: we use find_plausible_buildfile() and not find_buildfile() + // to look in outer directories. + // optional<path> bf ( find_buildfile (src_base, src_base, altn, buildfile)); @@ -1220,6 +1429,9 @@ main (int argc, char* argv[]) break; } + if (load_only && (mid != perform_id || oid != update_id)) + fail << "--load-only requires perform(update) action"; + // Now load the buildfiles and search the targets. // action_targets tgs; @@ -1251,6 +1463,9 @@ main (int argc, char* argv[]) if (tt == nullptr) fail (l) << "unknown target type " << tn.type; + if (load_only && !tt->is_a<alias> ()) + fail << "--load-only requires alias target"; + if (mif->search != nullptr) { // If the directory is relative, assume it is relative to work @@ -1258,10 +1473,17 @@ main (int argc, char* argv[]) // dir_path& d (tn.dir); - if (d.relative ()) - d = work / d; + try + { + if (d.relative ()) + d = work / d; - d.normalize (true); // Actualize since came from command line. + d.normalize (true); // Actualize since came from command line. + } + catch (const invalid_path& e) + { + fail << "invalid target directory '" << e.path << "'"; + } if (ts.forwarded) d = rs.out_path () / d.leaf (rs.src_path ()); // Remap. @@ -1281,8 +1503,10 @@ main (int argc, char* argv[]) } } // target - if (dump_load) - dump (ctx); + // Delay until after match in the --load-only mode (see below). + // + if (dump_load && !load_only) + dump (ctx, nullopt /* action */); // Finally, match the rules and perform the operation. // @@ -1296,6 +1520,12 @@ main (int argc, char* argv[]) ctx.current_operation (*pre_oif, oif); + if (oif->operation_pre != nullptr) + oif->operation_pre (ctx, oparams, false /* inner */, l); + + if (pre_oif->operation_pre != nullptr) + pre_oif->operation_pre (ctx, {}, true /* inner */, l); + action a (mid, pre_oid, oid); { @@ -1307,13 +1537,19 @@ main (int argc, char* argv[]) if (mif->match != nullptr) mif->match (mparams, a, tgs, diag, true /* progress */); - if (dump_match) + if (dump_match_pre) dump (ctx, a); if (mif->execute != nullptr && !ctx.match_only) mif->execute (mparams, a, tgs, diag, true /* progress */); } + if (pre_oif->operation_post != nullptr) + pre_oif->operation_post (ctx, {}, true /* inner */); + + if (oif->operation_post != nullptr) + oif->operation_post (ctx, oparams, false /* inner */); + if (mif->operation_post != nullptr) mif->operation_post (ctx, mparams, pre_oid); @@ -1325,6 +1561,15 @@ main (int argc, char* argv[]) ctx.current_operation (*oif, outer_oif); + if (outer_oif != nullptr && outer_oif->operation_pre != nullptr) + outer_oif->operation_pre (ctx, oparams, false /* inner */, l); + + if (oif->operation_pre != nullptr) + oif->operation_pre (ctx, + outer_oif == nullptr ? oparams : values {}, + true /* inner */, + l); + action a (mid, oid, oif->outer_id); { @@ -1343,6 +1588,14 @@ main (int argc, char* argv[]) mif->execute (mparams, a, tgs, diag, true /* progress */); } + if (oif->operation_post != nullptr) + oif->operation_post (ctx, + outer_oif == nullptr ? oparams : values {}, + true /* inner */); + + if (outer_oif != nullptr && outer_oif->operation_post != nullptr) + outer_oif->operation_post (ctx, oparams, false /* inner */); + if (post_oid != 0) { tgs.reset (); @@ -1355,6 +1608,12 @@ main (int argc, char* argv[]) ctx.current_operation (*post_oif, oif); + if (oif->operation_pre != nullptr) + oif->operation_pre (ctx, oparams, false /* inner */, l); + + if (post_oif->operation_pre != nullptr) + post_oif->operation_pre (ctx, {}, true /* inner */, l); + action a (mid, post_oid, oid); { @@ -1366,13 +1625,19 @@ main (int argc, char* argv[]) if (mif->match != nullptr) mif->match (mparams, a, tgs, diag, true /* progress */); - if (dump_match) + if (dump_match_post) dump (ctx, a); if (mif->execute != nullptr && !ctx.match_only) mif->execute (mparams, a, tgs, diag, true /* progress */); } + if (post_oif->operation_post != nullptr) + post_oif->operation_post (ctx, {}, true /* inner */); + + if (oif->operation_post != nullptr) + oif->operation_post (ctx, oparams, false /* inner */); + if (mif->operation_post != nullptr) mif->operation_post (ctx, mparams, post_oid); @@ -1380,6 +1645,9 @@ main (int argc, char* argv[]) << ", id " << static_cast<uint16_t> (post_oid);}); } + if (dump_load && load_only) + dump (ctx, nullopt /* action */); + if (mif->operation_post != nullptr) mif->operation_post (ctx, mparams, oid); diff --git a/build2/buildfile b/build2/buildfile index 0c21388..0111ed2 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -11,7 +11,7 @@ libs = $libbutl include ../libbuild2/ libs += ../libbuild2/lib{build2} -for m: bash bin c cc cxx in version +for m: bash bin c cc cli cxx in version { include ../libbuild2/$m/ libs += ../libbuild2/$m/lib{build2-$m} @@ -45,6 +45,8 @@ copyright = $process.run_regex( \ obj{b}: cxx.poptions += -DBUILD2_COPYRIGHT=\"$copyright\" +# NOTE: remember to update bpkg buildfile if changing anything here. +# switch $cxx.target.class { case 'linux' diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx deleted file mode 100644 index a00fd7f..0000000 --- a/build2/cli/init.cxx +++ /dev/null @@ -1,285 +0,0 @@ -// file : build2/cli/init.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include <build2/cli/init.hxx> - -#include <libbuild2/file.hxx> -#include <libbuild2/scope.hxx> -#include <libbuild2/target.hxx> -#include <libbuild2/variable.hxx> -#include <libbuild2/diagnostics.hxx> - -#include <libbuild2/config/utility.hxx> - -#include <libbuild2/cxx/target.hxx> - -#include <build2/cli/rule.hxx> -#include <build2/cli/module.hxx> -#include <build2/cli/target.hxx> - -namespace build2 -{ - namespace cli - { - // Remaining issues/semantics change: - // - // @@ Unconfigured caching. - // - // @@ Default-found cli used to result in config.cli=cli and now it's just - // omitted (and default-not-found -- in config.cli.configured=false). - // - // - Writing any default will take precedence over config.import.cli. - // In fact, this duality is a bigger problem: if we have a config - // that uses config.cli there is no way to reconfigure it to use - // config.import.cli. - // - // - We could have saved it commented. - // - // - We could do this at the module level only since we also have - // config.cli.options? - // - // - Note that in the CLI compiler itself we now rely on default cli - // being NULL/undefined. So if faving, should probably be commented - // out. BUT: it will still be defined, so will need to be defined - // NULL. Note also that long term the CLI compiler will not use the - // module relying on an ad hoc recipe instead. - // - // ! Maybe reserving NULL (instead of making it the same as NULL) for - // this "configured to default" state and saving commented is not a - // bad idea. Feels right to have some marker in config.build that - // things are in effect. And I believe if config.import.cli is - // specified, it will just be dropped. - - bool - guess_init (scope& rs, - scope& bs, - const location& loc, - bool, - bool opt, - module_init_extra& extra) - { - tracer trace ("cli::guess_init"); - l5 ([&]{trace << "for " << rs;}); - - // We only support root loading (which means there can only be one). - // - if (rs != bs) - fail (loc) << "cli.guess module must be loaded in project root"; - - // Adjust module config.build save priority (code generator). - // - config::save_module (rs, "cli", 150); - - // Enter metadata variables. - // - auto& vp (rs.var_pool ()); - - auto& v_ver (vp.insert<string> ("cli.version")); - auto& v_sum (vp.insert<string> ("cli.checksum")); - - // Import the CLI compiler target. - // - // Note that the special config.cli=false value (recognized by the - // import machinery) is treated as an explicit request to leave the - // module unconfigured. - // - bool new_cfg (false); - import_result<exe> ir ( - import_direct<exe> ( - new_cfg, - rs, - name ("cli", dir_path (), "exe", "cli"), // cli%exe{cli} - true /* phase2 */, - opt, - true /* metadata */, - loc, - "module load")); - - const exe* tgt (ir.target); - - // Extract metadata. - // - auto* ver (tgt != nullptr ? &cast<string> (tgt->vars[v_ver]) : nullptr); - auto* sum (tgt != nullptr ? &cast<string> (tgt->vars[v_sum]) : nullptr); - - // Print the report. - // - // If this is a configuration with new values, then print the report - // at verbosity level 2 and up (-v). - // - if (verb >= (new_cfg ? 2 : 3)) - { - diag_record dr (text); - dr << "cli " << project (rs) << '@' << rs << '\n'; - - if (tgt != nullptr) - dr << " cli " << ir << '\n' - << " version " << *ver << '\n' - << " checksum " << *sum; - else - dr << " cli " << "not found, leaving unconfigured"; - } - - if (tgt == nullptr) - return false; - - // The cli variable (untyped) is an imported compiler target name. - // - rs.assign ("cli") = move (ir.name); - rs.assign (v_sum) = *sum; - rs.assign (v_ver) = *ver; - - { - standard_version v (*ver); - - rs.assign<uint64_t> ("cli.version.number") = v.version; - rs.assign<uint64_t> ("cli.version.major") = v.major (); - rs.assign<uint64_t> ("cli.version.minor") = v.minor (); - rs.assign<uint64_t> ("cli.version.patch") = v.patch (); - } - - // Cache some values in the module for easier access in the rule. - // - extra.set_module (new module (data {*tgt, *sum})); - - return true; - } - - bool - config_init (scope& rs, - scope& bs, - const location& loc, - bool, - bool opt, - module_init_extra& extra) - { - tracer trace ("cli::config_init"); - l5 ([&]{trace << "for " << rs;}); - - // We only support root loading (which means there can only be one). - // - if (rs != bs) - fail (loc) << "cli.config module must be loaded in project root"; - - // Load cli.guess and share its module instance as ours. - // - if (optional<shared_ptr<build2::module>> r = load_module ( - rs, rs, "cli.guess", loc, opt, extra.hints)) - { - extra.module = *r; - } - else - { - // This can happen if someone already optionally loaded cli.guess - // and it has failed to configure. - // - if (!opt) - fail (loc) << "cli could not be configured" << - info << "re-run with -V for more information"; - - return false; - } - - // Configuration. - // - using config::append_config; - - // config.cli.options - // - // Note that we merge it into the corresponding cli.* variable. - // - append_config<strings> (rs, rs, "cli.options", nullptr); - - return true; - } - - bool - init (scope& rs, - scope& bs, - const location& loc, - bool, - bool opt, - module_init_extra& extra) - { - tracer trace ("cli::init"); - l5 ([&]{trace << "for " << rs;}); - - // We only support root loading (which means there can only be one). - // - if (rs != bs) - fail (loc) << "cli module must be loaded in project root"; - - // Make sure the cxx module has been loaded since we need its targets - // types (?xx{}). Note that we don't try to load it ourselves because of - // the non-trivial variable merging semantics. So it is better to let - // the user load cxx explicitly. @@ Not sure the reason still holds - // though it might still make sense to expect the user to load cxx. - // - if (!cast_false<bool> (rs["cxx.loaded"])) - fail (loc) << "cxx module must be loaded before cli"; - - // Load cli.config and get its module instance. - // - if (optional<shared_ptr<build2::module>> r = load_module ( - rs, rs, "cli.config", loc, opt, extra.hints)) - { - extra.module = *r; - } - else - { - // This can happen if someone already optionally loaded cli.config - // and it has failed to configure. - // - if (!opt) - fail (loc) << "cli could not be configured" << - info << "re-run with -V for more information"; - - return false; - } - - auto& m (extra.module_as<module> ()); - - // Register target types. - // - rs.insert_target_type<cli> (); - rs.insert_target_type<cli_cxx> (); - - // Register our rules. - // - // Other rules (e.g., cc::compile) may need to have the group members - // resolved/linked up. Looks like a general pattern: groups should - // resolve on *(update). - { - auto reg = [&rs, &m] (meta_operation_id mid, operation_id oid) - { - rs.insert_rule<cli_cxx> (mid, oid, "cli.compile", m); - rs.insert_rule<cxx::hxx> (mid, oid, "cli.compile", m); - rs.insert_rule<cxx::cxx> (mid, oid, "cli.compile", m); - rs.insert_rule<cxx::ixx> (mid, oid, "cli.compile", m); - }; - - reg (0 /* wildcard */, update_id); - reg (perform_id, clean_id); - } - - return true; - } - - static const module_functions mod_functions[] = - { - // NOTE: don't forget to also update the documentation in init.hxx if - // changing anything here. - - {"cli.guess", nullptr, guess_init}, - {"cli.config", nullptr, config_init}, - {"cli", nullptr, init}, - {nullptr, nullptr, nullptr} - }; - - const module_functions* - build2_cli_load () - { - return mod_functions; - } - } -} diff --git a/build2/cli/init.hxx b/build2/cli/init.hxx deleted file mode 100644 index 1c54316..0000000 --- a/build2/cli/init.hxx +++ /dev/null @@ -1,29 +0,0 @@ -// file : build2/cli/init.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CLI_INIT_HXX -#define BUILD2_CLI_INIT_HXX - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -#include <libbuild2/module.hxx> - -namespace build2 -{ - namespace cli - { - // Module `cli` does not require bootstrapping. - // - // Submodules: - // - // `cli.guess` -- set variables describing the compiler. - // `cli.config` -- load `cli.guess` and set the rest of the variables. - // `cli` -- load `cli.config` and register targets and rules. - // - extern "C" const module_functions* - build2_cli_load (); - } -} - -#endif // BUILD2_CLI_INIT_HXX diff --git a/build2/cli/module.hxx b/build2/cli/module.hxx deleted file mode 100644 index 70f6ba8..0000000 --- a/build2/cli/module.hxx +++ /dev/null @@ -1,30 +0,0 @@ -// file : build2/cli/module.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CLI_MODULE_HXX -#define BUILD2_CLI_MODULE_HXX - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -#include <libbuild2/module.hxx> - -#include <build2/cli/rule.hxx> - -namespace build2 -{ - namespace cli - { - class module: public build2::module, - public virtual data, - public compile_rule - { - public: - explicit - module (data&& d) - : data (move (d)), compile_rule (move (d)) {} - }; - } -} - -#endif // BUILD2_CLI_MODULE_HXX diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx deleted file mode 100644 index 364c90b..0000000 --- a/build2/cli/rule.cxx +++ /dev/null @@ -1,338 +0,0 @@ -// file : build2/cli/rule.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include <build2/cli/rule.hxx> - -#include <libbuild2/depdb.hxx> -#include <libbuild2/scope.hxx> -#include <libbuild2/target.hxx> -#include <libbuild2/context.hxx> -#include <libbuild2/algorithm.hxx> -#include <libbuild2/filesystem.hxx> -#include <libbuild2/diagnostics.hxx> - -#include <build2/cli/target.hxx> - -namespace build2 -{ - namespace cli - { - // Figure out if name contains stem and, optionally, calculate prefix and - // suffix. - // - static bool - match_stem (const string& name, const string& stem, - string* prefix = nullptr, string* suffix = nullptr) - { - size_t p (name.find (stem)); - - if (p != string::npos) - { - if (prefix != nullptr) - prefix->assign (name, 0, p); - - if (suffix != nullptr) - suffix->assign (name, p + stem.size (), string::npos); - - return true; - } - - return false; - } - - bool compile_rule:: - match (action a, target& t) const - { - tracer trace ("cli::compile_rule::match"); - - // Find the .cli source file. - // - auto find = [&trace, a, &t] (auto&& r) -> optional<prerequisite_member> - { - for (prerequisite_member p: r) - { - // If excluded or ad hoc, then don't factor it into our tests. - // - if (include (a, t, p) != include_type::normal) - continue; - - if (p.is_a<cli> ()) - { - // Check that the stem match. - // - if (match_stem (t.name, p.name ())) - return p; - - l4 ([&]{trace << ".cli file stem '" << p.name () << "' " - << "doesn't match target " << t;}); - } - } - - return nullopt; - }; - - if (cli_cxx* pt = t.is_a<cli_cxx> ()) - { - // The cli.cxx{} group. - // - cli_cxx& t (*pt); - - // See if we have a .cli source file. - // - if (!find (group_prerequisite_members (a, t))) - { - l4 ([&]{trace << "no .cli source file for target " << t;}); - return false; - } - - // Figure out the member list. - // - // At this stage, no further changes to cli.options are possible and - // we can determine whether the --suppress-inline option is present. - // - // Passing the group as a "reference target" is a bit iffy, - // conceptually. - // - t.h = &search<cxx::hxx> (t, t.dir, t.out, t.name); - t.c = &search<cxx::cxx> (t, t.dir, t.out, t.name); - t.i = find_option ("--suppress-inline", t, "cli.options") - ? nullptr - : &search<cxx::ixx> (t, t.dir, t.out, t.name); - - return true; - } - else - { - // One of the ?xx{} members. - // - - // Check if there is a corresponding cli.cxx{} group. - // - const cli_cxx* g (t.ctx.targets.find<cli_cxx> (t.dir, t.out, t.name)); - - // If not or if it has no prerequisites (happens when we use it to - // set cli.options) and this target has a cli{} prerequisite, then - // synthesize the dependency. - // - if (g == nullptr || !g->has_prerequisites ()) - { - if (optional<prerequisite_member> p = find ( - prerequisite_members (a, t))) - { - if (g == nullptr) - g = &t.ctx.targets.insert<cli_cxx> (t.dir, t.out, t.name, trace); - - prerequisites ps; - ps.push_back (p->as_prerequisite ()); - g->prerequisites (move (ps)); - } - } - - if (g == nullptr) - return false; - - // For ixx{}, verify it is part of the group (i.e., not disabled - // via --suppress-inline). - // - if (t.is_a<cxx::ixx> () && - find_option ("--suppress-inline", *g, "cli.options")) - return false; - - t.group = g; - return true; - } - } - - recipe compile_rule:: - apply (action a, target& xt) const - { - if (cli_cxx* pt = xt.is_a<cli_cxx> ()) - { - cli_cxx& t (*pt); - - // Derive file names for the members. - // - t.h->derive_path (); - t.c->derive_path (); - if (t.i != nullptr) - t.i->derive_path (); - - // Inject dependency on the output directory. - // - inject_fsdir (a, t); - - // Match prerequisites. - // - match_prerequisite_members (a, t); - - // For update inject dependency on the CLI compiler target. - // - if (a == perform_update_id) - inject (a, t, ctgt); - - switch (a) - { - case perform_update_id: return [this] (action a, const target& t) - { - return perform_update (a, t); - }; - case perform_clean_id: return &perform_clean_group_depdb; - default: return noop_recipe; // Configure/dist update. - } - } - else - { - const cli_cxx& g (xt.group->as<cli_cxx> ()); - match_sync (a, g); - return group_recipe; // Execute the group's recipe. - } - } - - static void - append_extension (cstrings& args, - const path_target& t, - const char* option, - const char* default_extension) - { - const string* e (t.ext ()); - assert (e != nullptr); // Should have been figured out in apply(). - - if (*e != default_extension) - { - // CLI needs the extension with the leading dot (unless it is empty) - // while we store the extension without. But if there is an extension, - // then we can get it (with the dot) from the file name. - // - args.push_back (option); - args.push_back (e->empty () - ? e->c_str () - : t.path ().extension_cstring () - 1); - } - } - - target_state compile_rule:: - perform_update (action a, const target& xt) const - { - tracer trace ("cli::compile_rule::perform_update"); - - // The rule has been matched which means the members should be resolved - // and paths assigned. We use the header file as our "target path" for - // timestamp, depdb, etc. - // - const cli_cxx& t (xt.as<cli_cxx> ()); - const path& tp (t.h->path ()); - - // Update prerequisites and determine if any relevant ones render us - // out-of-date. Note that currently we treat all the prerequisites as - // potentially affecting the result (think prologues/epilogues, CLI - // compiler target itself, etc). - // - timestamp mt (t.load_mtime (tp)); - auto pr (execute_prerequisites<cli> (a, t, mt)); - - bool update (!pr.first); - target_state ts (update ? target_state::changed : *pr.first); - - const cli& s (pr.second); - - // We use depdb to track changes to the .cli file name, options, - // compiler, etc. - // - depdb dd (tp + ".d"); - { - // First should come the rule name/version. - // - if (dd.expect ("cli.compile 1") != nullptr) - l4 ([&]{trace << "rule mismatch forcing update of " << t;}); - - // Then the compiler checksum. - // - if (dd.expect (csum) != nullptr) - l4 ([&]{trace << "compiler mismatch forcing update of " << t;}); - - // Then the options checksum. - // - sha256 cs; - append_options (cs, t, "cli.options"); - - if (dd.expect (cs.string ()) != nullptr) - l4 ([&]{trace << "options mismatch forcing update of " << t;}); - - // Finally the .cli input file. - // - if (dd.expect (s.path ()) != nullptr) - l4 ([&]{trace << "input file mismatch forcing update of " << t;}); - } - - // Update if depdb mismatch. - // - if (dd.writing () || dd.mtime > mt) - update = true; - - dd.close (); - - // If nothing changed, then we are done. - // - if (!update) - return ts; - - // Translate paths to relative (to working directory). This results in - // easier to read diagnostics. - // - path relo (relative (t.dir)); - path rels (relative (s.path ())); - - const process_path& pp (ctgt.process_path ()); - cstrings args {pp.recall_string ()}; - - // See if we need to pass --output-{prefix,suffix} - // - string prefix, suffix; - match_stem (t.name, s.name, &prefix, &suffix); - - if (!prefix.empty ()) - { - args.push_back ("--output-prefix"); - args.push_back (prefix.c_str ()); - } - - if (!suffix.empty ()) - { - args.push_back ("--output-suffix"); - args.push_back (suffix.c_str ()); - } - - // See if we need to pass any --?xx-suffix options. - // - append_extension (args, *t.h, "--hxx-suffix", "hxx"); - append_extension (args, *t.c, "--cxx-suffix", "cxx"); - if (t.i != nullptr) - append_extension (args, *t.i, "--ixx-suffix", "ixx"); - - append_options (args, t, "cli.options"); - - if (!relo.empty ()) - { - args.push_back ("-o"); - args.push_back (relo.string ().c_str ()); - } - - args.push_back (rels.string ().c_str ()); - args.push_back (nullptr); - - if (verb >= 2) - print_process (args); - else if (verb) - text << "cli " << s; - - if (!t.ctx.dry_run) - { - run (pp, args); - dd.check_mtime (tp); - } - - t.mtime (system_clock::now ()); - return target_state::changed; - } - } -} diff --git a/build2/cli/rule.hxx b/build2/cli/rule.hxx deleted file mode 100644 index 0538c57..0000000 --- a/build2/cli/rule.hxx +++ /dev/null @@ -1,43 +0,0 @@ -// file : build2/cli/rule.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CLI_RULE_HXX -#define BUILD2_CLI_RULE_HXX - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -#include <libbuild2/rule.hxx> - -namespace build2 -{ - namespace cli - { - // Cached data shared between rules and the module. - // - struct data - { - const exe& ctgt; // CLI compiler target. - const string& csum; // CLI compiler checksum. - }; - - // @@ Redo as two separate rules? - // - class compile_rule: public simple_rule, virtual data - { - public: - compile_rule (data&& d): data (move (d)) {} - - virtual bool - match (action, target&) const override; - - virtual recipe - apply (action, target&) const override; - - target_state - perform_update (action, const target&) const; - }; - } -} - -#endif // BUILD2_CLI_RULE_HXX diff --git a/build2/cli/target.cxx b/build2/cli/target.cxx deleted file mode 100644 index 37eee97..0000000 --- a/build2/cli/target.cxx +++ /dev/null @@ -1,75 +0,0 @@ -// file : build2/cli/target.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include <build2/cli/target.hxx> - -#include <libbuild2/context.hxx> - -namespace build2 -{ - namespace cli - { - // cli - // - extern const char cli_ext_def[] = "cli"; - - const target_type cli::static_type - { - "cli", - &file::static_type, - &target_factory<cli>, - nullptr, /* fixed_extension */ - &target_extension_var<cli_ext_def>, - &target_pattern_var<cli_ext_def>, - nullptr, - &file_search, - target_type::flag::none - }; - - // cli.cxx - // - group_view cli_cxx:: - group_members (action) const - { - static_assert (sizeof (cli_cxx_members) == sizeof (const target*) * 3, - "member layout incompatible with array"); - - return h != nullptr - ? group_view {reinterpret_cast<const target* const*> (&h), - (i != nullptr ? 3U : 2U)} - : group_view {nullptr, 0}; - } - - static target* - cli_cxx_factory (context& ctx, - const target_type&, dir_path d, dir_path o, string n) - { - tracer trace ("cli::cli_cxx_factory"); - - // Pre-enter (potential) members as targets. The main purpose of doing - // this is to avoid searching for existing files in src_base if the - // buildfile mentions some of them explicitly as prerequisites. - // - // Also required for the src-out remapping logic. - // - ctx.targets.insert<cxx::hxx> (d, o, n, trace); - ctx.targets.insert<cxx::cxx> (d, o, n, trace); - ctx.targets.insert<cxx::ixx> (d, o, n, trace); - - return new cli_cxx (ctx, move (d), move (o), move (n)); - } - - const target_type cli_cxx::static_type - { - "cli.cxx", - &mtime_target::static_type, - &cli_cxx_factory, - nullptr, - nullptr, - nullptr, - nullptr, - &target_search, - target_type::flag::see_through // Group with "see through" iteration. - }; - } -} diff --git a/build2/cli/target.hxx b/build2/cli/target.hxx deleted file mode 100644 index f27ee89..0000000 --- a/build2/cli/target.hxx +++ /dev/null @@ -1,58 +0,0 @@ -// file : build2/cli/target.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CLI_TARGET_HXX -#define BUILD2_CLI_TARGET_HXX - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -#include <libbuild2/target.hxx> - -#include <libbuild2/cxx/target.hxx> - -namespace build2 -{ - namespace cli - { - class cli: public file - { - public: - cli (context& c, dir_path d, dir_path o, string n) - : file (c, move (d), move (o), move (n)) - { - dynamic_type = &static_type; - } - - public: - static const target_type static_type; - }; - - // Standard layout type compatible with group_view's const target*[3]. - // - struct cli_cxx_members - { - const cxx::hxx* h = nullptr; - const cxx::cxx* c = nullptr; - const cxx::ixx* i = nullptr; - }; - - class cli_cxx: public mtime_target, public cli_cxx_members - { - public: - cli_cxx (context& c, dir_path d, dir_path o, string n) - : mtime_target (c, move (d), move (o), move (n)) - { - dynamic_type = &static_type; - } - - virtual group_view - group_members (action) const override; - - public: - static const target_type static_type; - }; - } -} - -#endif // BUILD2_CLI_TARGET_HXX |