From 0f619ddcbee3b477edd2ab46f5f82d1ae693d553 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 29 May 2023 09:55:54 +0200 Subject: New 'lines' dependency format in depdb-dyndep --- libbuild2/build/script/builtin.cli | 23 +++++- libbuild2/build/script/parser.cxx | 157 +++++++++++++++++++++++++++++++++++-- libbuild2/build/script/parser.hxx | 2 +- 3 files changed, 172 insertions(+), 10 deletions(-) (limited to 'libbuild2/build') 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 + // `...: [...]` 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 d37fef5..c18a2bb 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 << "'"; } @@ -2818,6 +2819,8 @@ namespace build2 // if (r.first == make_type::target) { + // NOTE: similar code below. + // if (dyn_tgt) { path& f (r.second); @@ -2825,7 +2828,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"; @@ -2843,17 +2847,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)); @@ -2862,6 +2866,8 @@ namespace build2 continue; } + // NOTE: similar code below. + // if (optional u = add (move (r.second), &skip, rmt)) { restart = *u; @@ -2891,6 +2897,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 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 { -- cgit v1.1