aboutsummaryrefslogtreecommitdiff
path: root/build2
diff options
context:
space:
mode:
Diffstat (limited to 'build2')
-rw-r--r--build2/b.cxx374
-rw-r--r--build2/buildfile4
-rw-r--r--build2/cli/init.cxx285
-rw-r--r--build2/cli/init.hxx29
-rw-r--r--build2/cli/module.hxx30
-rw-r--r--build2/cli/rule.cxx338
-rw-r--r--build2/cli/rule.hxx43
-rw-r--r--build2/cli/target.cxx75
-rw-r--r--build2/cli/target.hxx58
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