aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-05-30 09:36:35 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-05-30 11:23:09 +0200
commit2d4b7eb982d2f7140d8093d9b1f0c3498d84f936 (patch)
tree0e3b3dda431012094140515edfcee70675097091 /libbuild2
parentb4c8dc71b6f2c9d8bd63591b3e9a1c6bc329c240 (diff)
Add support for fsdir{} dynamic prerequisites in the dyndep lines format
This can be used to handle situations where the dynamic targets are placed into subdirectories.
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/adhoc-rule-buildscript.cxx93
-rw-r--r--libbuild2/build/script/builtin.cli4
-rw-r--r--libbuild2/build/script/parser.cxx59
3 files changed, 135 insertions, 21 deletions
diff --git a/libbuild2/adhoc-rule-buildscript.cxx b/libbuild2/adhoc-rule-buildscript.cxx
index 4277573..29b11ee 100644
--- a/libbuild2/adhoc-rule-buildscript.cxx
+++ b/libbuild2/adhoc-rule-buildscript.cxx
@@ -551,14 +551,16 @@ namespace build2
}
}
- // Read the list of dynamic targets from depdb, if exists (used in a few
- // depdb-dyndep --dyn-target handling places below).
+ // Read the list of dynamic targets and, optionally, fsdir{} prerequisites
+ // from depdb, if exists (used in a few depdb-dyndep --dyn-target handling
+ // places below).
//
- auto read_dyn_targets = [] (path ddp) -> dynamic_targets
+ auto read_dyn_targets = [] (path ddp, bool fsdir)
+ -> pair<dynamic_targets, dir_paths>
{
depdb dd (move (ddp), true /* read_only */);
- dynamic_targets r;
+ pair<dynamic_targets, dir_paths> r;
while (dd.reading ()) // Breakout loop.
{
string* l;
@@ -571,8 +573,7 @@ namespace build2
break;
// We can omit this for as long as we don't break our blank line
- // anchors semantics (e.g., by adding another blank somewhere, say
- // after the custom depdb builtins).
+ // anchors semantics.
//
#if 0
if (*l != rule_id_)
@@ -581,14 +582,33 @@ namespace build2
#endif
// Note that we cannot read out expected lines since there can be
- // custom depdb builtins. We also need to skip the prerequisites
- // list. So we read until the blank line that always terminates the
- // prerequisites list.
+ // custom depdb builtins. So we use the blank lines as anchors to
+ // skip to the parts we need.
//
+ // Skip until the first blank that separated custom depdb entries from
+ // the prerequisites list.
{
- bool r;
- while ((r = read ()) && !l->empty ()) ;
- if (!r)
+ bool g;
+ while ((g = read ()) && !l->empty ()) ;
+ if (!g)
+ break;
+ }
+
+ // Next read the prerequisites, detecting fsdir{} entries if asked.
+ //
+ {
+ bool g;
+ while ((g = read ()) && !l->empty ())
+ {
+ if (fsdir)
+ {
+ path p (*l);
+ if (p.to_directory ())
+ r.second.push_back (path_cast<dir_path> (move (p)));
+ }
+ }
+
+ if (!g)
break;
}
@@ -608,7 +628,8 @@ namespace build2
p + 1 == l->size ()) // Empty path.
break;
- r.emplace_back (string (*l, 0, p), path (*l, p + 1, string::npos));
+ r.first.emplace_back (string (*l, 0, p),
+ path (*l, p + 1, string::npos));
}
break;
@@ -661,7 +682,10 @@ namespace build2
};
}
- for (dynamic_target& dt: read_dyn_targets (target_path () + ".d"))
+ pair<dynamic_targets, dir_paths> p (
+ read_dyn_targets (target_path () + ".d", true));
+
+ for (dynamic_target& dt: p.first)
{
path& f (dt.path);
@@ -687,6 +711,20 @@ namespace build2
dyndep::inject_adhoc_group_member (a, bs, t, move (f), *tt);
}
}
+
+ // Enter fsdir{} prerequisites.
+ //
+ // See the add lambda in exec_depdb_dyndep() for background.
+ //
+ for (dir_path& d: p.second)
+ {
+ const fsdir& dt (search<fsdir> (t,
+ move (d),
+ dir_path (),
+ string (), nullptr, nullptr));
+ match_sync (a, dt);
+ pts.push_back (prerequisite_target (&dt, true /* adhoc */));
+ }
}
return g == nullptr ? perform_clean_file : perform_clean_group;
@@ -838,7 +876,7 @@ namespace build2
// will need to update. Oh, well, being dynamic ain't free.
//
if (script.depdb_dyndep_dyn_target)
- old_dyn_targets = read_dyn_targets (tp + ".d");
+ old_dyn_targets = read_dyn_targets (tp + ".d", false).first;
}
depdb dd (tp + ".d");
@@ -940,6 +978,14 @@ namespace build2
{
build::script::parser p (ctx);
p.execute_depdb_preamble (a, bs, t, env, script, run, dd);
+
+ // Write a blank line after the custom depdb entries and before
+ // prerequisites, which we use as an anchor (see read_dyn_targets
+ // above). We only do it for the new --dyn-target mode in order not to
+ // invalidate the existing depdb instances.
+ //
+ if (script.depdb_dyndep_dyn_target)
+ dd.expect ("");
}
// Determine if we need to do an update based on the above checks.
@@ -1480,6 +1526,11 @@ namespace build2
{
f = path (l);
+ // fsdir{} prerequisites only make sense with dynamic targets.
+ //
+ if (f.to_directory ())
+ throw invalid_path ("");
+
if (f.relative ())
{
if (!byp.cwd)
@@ -1551,8 +1602,6 @@ namespace build2
//
const group* g (t.is_a<group> ());
- const file& ft ((g == nullptr ? t : *g->members.front ()).as<file> ());
-
// Note that even if we've updated all our prerequisites in apply(), we
// still need to execute them here to keep the dependency counts straight.
//
@@ -1581,7 +1630,8 @@ namespace build2
if (!ctx.dry_run || verb != 0)
{
if (g == nullptr)
- execute_update_file (*md.bs, a, ft, env, run, md.deferred_failure);
+ execute_update_file (
+ *md.bs, a, t.as<file> (), env, run, md.deferred_failure);
else
execute_update_group (*md.bs, a, *g, env, run, md.deferred_failure);
}
@@ -1591,10 +1641,15 @@ namespace build2
timestamp now (system_clock::now ());
if (!ctx.dry_run)
+ {
+ // Note: in case of deferred failure we may not have any members.
+ //
+ const file& ft ((g == nullptr ? t : *g->members.front ()).as<file> ());
depdb::check_mtime (start, md.dd, ft.path (), now);
+ }
(g == nullptr
- ? static_cast<const mtime_target&> (ft)
+ ? static_cast<const mtime_target&> (t)
: static_cast<const mtime_target&> (*g)).mtime (now);
return target_state::changed;
diff --git a/libbuild2/build/script/builtin.cli b/libbuild2/build/script/builtin.cli
index 6d6369b..5aea034 100644
--- a/libbuild2/build/script/builtin.cli
+++ b/libbuild2/build/script/builtin.cli
@@ -56,6 +56,10 @@ namespace build2
// 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.
+ // Finally, in this mode, if the prerequisite is syntactically a
+ // directory (that is, it ends with a trailing directory separator),
+ // then it is added as fsdir{}. This can be used to handle situations
+ // where the dynamic targets are placed into subdirectories.
//
// Note on naming: whenever we (may) have two options, one for target
// and the other for prerequisite, we omit "prerequisite" as that's
diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx
index 9965799..7e2feb9 100644
--- a/libbuild2/build/script/parser.cxx
+++ b/libbuild2/build/script/parser.cxx
@@ -2434,6 +2434,61 @@ namespace build2
bool cache (skip == nullptr);
+ // Handle fsdir{} prerequisite separately.
+ //
+ // Note: inspired by inject_fsdir().
+ //
+ if (fp.to_directory ())
+ {
+ if (!cache)
+ {
+ // Note: already absolute since cannot be non-existent.
+ //
+ fp.normalize ();
+ }
+
+ const fsdir* dt (&search<fsdir> (t,
+ path_cast<dir_path> (fp),
+ dir_path (),
+ string (), nullptr, nullptr));
+
+ // Subset of code for file below.
+ //
+ if (!cache)
+ {
+ for (size_t i (0); i != pts_n; ++i)
+ {
+ const prerequisite_target& p (pts[i]);
+
+ if (const target* pt =
+ (p.target != nullptr ? p.target :
+ p.adhoc () ? reinterpret_cast<target*> (p.data) :
+ nullptr))
+ {
+ if (dt == pt)
+ return false;
+ }
+ }
+
+ if (*skip != 0)
+ {
+ --(*skip);
+ return false;
+ }
+ }
+
+ match_sync (a, *dt);
+ pts.push_back (
+ prerequisite_target (
+ nullptr, true /* adhoc */, reinterpret_cast<uintptr_t> (dt)));
+
+ if (!cache)
+ dd.expect (fp.representation ());
+
+ skip_count++;
+ return false;
+ }
+
// We can only defer the failure if we will be running the recipe
// body.
//
@@ -3013,7 +3068,8 @@ namespace build2
{
f = path (l.c_str () + n, l.size () - n);
- if (f.empty ())
+ if (f.empty () ||
+ (n && f.to_directory ())) // Non-existent fsdir{}.
throw invalid_path ("");
if (f.relative ())
@@ -3036,7 +3092,6 @@ namespace build2
//
throw invalid_path ("");
}
-
}
catch (const invalid_path&)
{