aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-05-29 09:55:54 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-05-29 14:12:00 +0200
commit609c81c87b2e672ebf64e10d709da869d0355495 (patch)
treed9b264f250a03844a8a6c226a5f7984f9c52d40a /libbuild2/build
parent9bea2f465cc2b47e06d65d6a29cb0f0f0c37f29c (diff)
New 'lines' dependency format in depdb-dyndep
Diffstat (limited to 'libbuild2/build')
-rw-r--r--libbuild2/build/script/builtin.cli23
-rw-r--r--libbuild2/build/script/parser.cxx157
-rw-r--r--libbuild2/build/script/parser.hxx2
3 files changed, 172 insertions, 10 deletions
diff --git a/libbuild2/build/script/builtin.cli b/libbuild2/build/script/builtin.cli
index 2fba0b0..cf1540d 100644
--- a/libbuild2/build/script/builtin.cli
+++ b/libbuild2/build/script/builtin.cli
@@ -40,6 +40,23 @@ namespace build2
// with support for generated files (and thus -I) at least in the make
// format where we use relative paths for non-existent files.
//
+ // Currently Supported dependency formats (--format) are `make`
+ // (default) and `lines`.
+ //
+ // The `make` format is the make dependency declaration in the
+ // `<target>...: [<prerequisite>...]` form. In the non-byproduct mode
+ // a relative prerequisite path is considered non-existent.
+ //
+ // The `lines` format lists targets and/or prerequisites one per line.
+ // If the --dyn-target option is specified then the target list is
+ // expected to come first separated from the prerequisites list with a
+ // blank line. If there are no prerequisites, then the blank line can
+ // be omitted. If the --dyn-target option is not specified, then all
+ // lines are treated as prerequisites and there should be no blank
+ // lines. In the non-byproduct mode a prerequisite line that starts
+ // with a leading space is considered a non-existent prerequisite.
+ // Currently only relative non-existent prerequisites are supported.
+ //
// Note on naming: whenever we (may) have two options, one for target
// and the other for prerequisite, we omit "prerequisite" as that's
// what we extract by default and most commonly. For example:
@@ -49,7 +66,8 @@ namespace build2
//
path --file; // Read from file rather than stdin.
- string --format; // Dependency format: make (default).
+ string --format; // Dependency format: `make` (default),
+ // or `lines`.
// Dynamic dependency extraction options.
//
@@ -69,7 +87,8 @@ namespace build2
dir_path --cwd; // Builtin's working directory used
// to complete relative paths of
// prerequisites (only in --byproduct
- // mode).
+ // mode, lines format for existing
+ // paths).
bool --drop-cycles; // Drop prerequisites that are also
// targets. Only use if you are sure
diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx
index 7d8a881..c6eb9cd 100644
--- a/libbuild2/build/script/parser.cxx
+++ b/libbuild2/build/script/parser.cxx
@@ -1972,7 +1972,8 @@ namespace build2
{
const string& f (ops.format ());
- if (f != "make")
+ if (f == "lines") format = dyndep_format::lines;
+ else if (f != "make")
fail (ll) << "depdb dyndep: invalid --format option value '"
<< f << "'";
}
@@ -2819,6 +2820,8 @@ namespace build2
//
if (r.first == make_type::target)
{
+ // NOTE: similar code below.
+ //
if (dyn_tgt)
{
path& f (r.second);
@@ -2826,7 +2829,8 @@ namespace build2
if (f.relative ())
{
if (!cwd_tgt)
- fail (il) << "relative target path '" << f
+ fail (il) << "relative " << what_tgt
+ << " target path '" << f
<< "' in make dependency declaration" <<
info << "consider using --target-cwd to specify "
<< "relative path base";
@@ -2844,17 +2848,17 @@ namespace build2
}
catch (const invalid_path&)
{
- fail << "invalid " << what_tgt << " path '"
- << f.string () << "'";
+ fail (il) << "invalid " << what_tgt << " target "
+ << "path '" << f.string () << "'";
}
// The target must be within this project.
//
if (!f.sub (rs.out_path ()))
{
- fail << what_tgt << " target path " << f
- << " must be inside project output directory "
- << rs.out_path ();
+ fail (il) << what_tgt << " target path " << f
+ << " must be inside project output "
+ << "directory " << rs.out_path ();
}
dyn_targets.push_back (move (f));
@@ -2863,6 +2867,8 @@ namespace build2
continue;
}
+ // NOTE: similar code below.
+ //
if (optional<bool> u = add (move (r.second), &skip, rmt))
{
restart = *u;
@@ -2892,6 +2898,143 @@ namespace build2
break; // case
}
+ case dyndep_format::lines:
+ {
+ bool tgt (dyn_tgt); // Reading targets or prerequisites.
+
+ for (string l; !restart; ++il.line) // Reuse the buffer.
+ {
+ if (eof (getline (is, l)))
+ break;
+
+ if (l.empty ())
+ {
+ if (!tgt)
+ fail (il) << "blank line in prerequisites list";
+
+ tgt = false; // Targets/prerequisites separating blank.
+ continue;
+ }
+
+ // See if this line start with space to indicate a non-
+ // existent prerequisite. This variable serves both as a
+ // flag and as a position of the beginning of the path.
+ //
+ size_t n (l.front () == ' ' ? 1 : 0);
+
+ if (tgt)
+ {
+ // NOTE: similar code above.
+ //
+ path f;
+ try
+ {
+ // Non-existent target doesn't make sense.
+ //
+ if (n)
+ throw invalid_path ("");
+
+ f = path (l);
+
+ if (f.relative ())
+ {
+ if (!cwd_tgt)
+ fail (il) << "relative " << what_tgt
+ << " target path '" << f
+ << "' in lines dependency declaration" <<
+ info << "consider using --target-cwd to specify "
+ << "relative path base";
+
+ f = *cwd_tgt / f;
+ }
+
+ // Note that unlike prerequisites, here we don't need
+ // normalize_external() since we expect the targets to
+ // be within this project.
+ //
+ f.normalize ();
+ }
+ catch (const invalid_path&)
+ {
+ fail (il) << "invalid " << what_tgt << " target path '"
+ << l << "'";
+ }
+
+ // The target must be within this project.
+ //
+ if (!f.sub (rs.out_path ()))
+ {
+ fail (il) << what_tgt << " target path " << f
+ << " must be inside project output directory "
+ << rs.out_path ();
+ }
+
+ dyn_targets.push_back (move (f));
+ }
+ else
+ {
+ path f;
+ try
+ {
+ f = path (l.c_str () + n, l.size () - n);
+
+ if (f.empty ())
+ throw invalid_path ("");
+
+ if (f.relative ())
+ {
+ if (!n)
+ {
+ if (!cwd)
+ fail (il) << "relative " << what
+ << " prerequisite path '" << f
+ << "' in lines dependency declaration" <<
+ info << "consider using --cwd to specify "
+ << "relative path base";
+
+ f = *cwd / f;
+ }
+ }
+ else if (n)
+ {
+ // @@ TODO: non-existent absolute paths.
+ //
+ throw invalid_path ("");
+ }
+
+ }
+ catch (const invalid_path&)
+ {
+ fail (il) << "invalid " << what << " prerequisite path '"
+ << l << "'";
+ }
+
+ // NOTE: similar code above.
+ //
+ if (optional<bool> u = add (move (f), &skip, rmt))
+ {
+ restart = *u;
+
+ if (restart)
+ {
+ update = true;
+ l6 ([&]{trace << "restarting";});
+ }
+ }
+ else
+ {
+ // Trigger recompilation, mark as expected to fail, and
+ // bail out.
+ //
+ update = true;
+ deferred_failure = true;
+ break;
+ }
+ }
+ }
+
+ break; // case
+ }
}
if (file)
diff --git a/libbuild2/build/script/parser.hxx b/libbuild2/build/script/parser.hxx
index 7417e9e..f975194 100644
--- a/libbuild2/build/script/parser.hxx
+++ b/libbuild2/build/script/parser.hxx
@@ -143,7 +143,7 @@ namespace build2
// depdb-dyndep --byproduct logic (which fits better into the rule
// implementation).
//
- enum class dyndep_format {make};
+ enum class dyndep_format {make, lines};
struct dyndep_byproduct
{