From a3925d12af6a6ae75897d5aab25a9de0edb642fb Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 5 Sep 2016 09:24:39 +0200 Subject: Add support for build-time dependencies --- bpkg/package.xml | 1 + bpkg/pkg-build.cxx | 43 ++++++++++++++++++++---- bpkg/pkg-configure.cxx | 26 +++++++++++++++ bpkg/satisfaction | 9 +++++ bpkg/satisfaction.cxx | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ bpkg/utility | 7 ++-- bpkg/utility.cxx | 15 ++++++--- 7 files changed, 179 insertions(+), 13 deletions(-) diff --git a/bpkg/package.xml b/bpkg/package.xml index 0602880..78a49d4 100644 --- a/bpkg/package.xml +++ b/bpkg/package.xml @@ -106,6 +106,7 @@ + diff --git a/bpkg/pkg-build.cxx b/bpkg/pkg-build.cxx index a022130..d9847f2 100644 --- a/bpkg/pkg-build.cxx +++ b/bpkg/pkg-build.cxx @@ -499,6 +499,31 @@ namespace bpkg fail << "multiple dependency alternatives not yet supported"; const dependency& d (da.front ()); + const string& dn (d.name); + + if (da.buildtime) + { + // Handle special names. + // + if (dn == "build2") + { + if (d.constraint) + satisfy_build2 (options, name, d); + + continue; + } + else if (dn == "bpkg") + { + if (d.constraint) + satisfy_bpkg (options, name, d); + + continue; + } + // else + // + // @@ TODO: in the future we would need to at least make sure the + // build and target machines are the same. See also pkg-configure. + } // The first step is to always find the available package even // if, in the end, it won't be the one we select. If we cannot @@ -521,7 +546,7 @@ namespace bpkg // either way, resolving it to 1.0.0 is the conservative choice and // the user can always override it by explicitly building libhello. // - auto rp (find_available (db, d.name, ar, d.constraint)); + auto rp (find_available (db, dn, ar, d.constraint)); shared_ptr& dap (rp.first); if (dap == nullptr) @@ -550,14 +575,14 @@ namespace bpkg if (dap->system_version () == nullptr) fail << "prerequisite " << d << " of package " << name << " is " << "not available in source" << - info << "specify ?sys:" << d.name << " if it is available " - << "from the system"; + info << "specify ?sys:" << dn << " if it is available from the " + << "system"; if (!satisfies (*dap->system_version (), d.constraint)) { fail << "prerequisite " << d << " of package " << name << " is " << "not available in source" << - info << "sys:" << d.name << "/" << *dap->system_version () + info << "sys:" << dn << "/" << *dap->system_version () << " does not satisfy the constrains"; } @@ -579,11 +604,11 @@ namespace bpkg // worse, downgrade). // bool force (false); - shared_ptr dsp (db.find (d.name)); + shared_ptr dsp (db.find (dn)); if (dsp != nullptr) { if (dsp->state == package_state::broken) - fail << "unable to build broken package " << d.name << + fail << "unable to build broken package " << dn << info << "use 'pkg-purge --force' to remove"; if (satisfies (dsp->version, d.constraint)) @@ -777,6 +802,12 @@ namespace bpkg { assert (!da.conditional && da.size () == 1); // @@ TODO const dependency& d (da.front ()); + const string& dn (d.name); + + // Skip special names. + // + if (da.buildtime && (dn == "build2" || dn == "bpkg")) + continue; update (order (d.name, false)); } diff --git a/bpkg/pkg-configure.cxx b/bpkg/pkg-configure.cxx index 81b1fe3..2bb834f 100644 --- a/bpkg/pkg-configure.cxx +++ b/bpkg/pkg-configure.cxx @@ -61,6 +61,32 @@ namespace bpkg { const string& n (d.name); + if (da.buildtime) + { + // Handle special names. + // + if (n == "build2") + { + if (d.constraint) + satisfy_build2 (o, m.name, d); + + satisfied = true; + break; + } + else if (n == "bpkg") + { + if (d.constraint) + satisfy_bpkg (o, m.name, d); + + satisfied = true; + break; + } + // else + // + // @@ TODO: in the future we would need to at least make sure the + // build and target machines are the same. See also pkg-build. + } + if (shared_ptr dp = db.find (n)) { if (dp->state != package_state::configured) diff --git a/bpkg/satisfaction b/bpkg/satisfaction index a140b5f..ae78a98 100644 --- a/bpkg/satisfaction +++ b/bpkg/satisfaction @@ -9,6 +9,7 @@ #include #include +#include namespace bpkg { @@ -36,6 +37,14 @@ namespace bpkg { return l ? (!r || satisfies (*l, *r)) : !r; } + + // Special build-time dependencies. + // + void + satisfy_build2 (const common_options&, const string& pkg, const dependency&); + + void + satisfy_bpkg (const common_options&, const string& pkg, const dependency&); } #endif // BPKG_SATISFACTION diff --git a/bpkg/satisfaction.cxx b/bpkg/satisfaction.cxx index bddf7e7..2e2b269 100644 --- a/bpkg/satisfaction.cxx +++ b/bpkg/satisfaction.cxx @@ -4,8 +4,13 @@ #include +#include +#include + +#include #include #include +#include using namespace std; using namespace butl; @@ -93,4 +98,90 @@ namespace bpkg return s; } + + static version build2_version; + + void + satisfy_build2 (const common_options& co, + const string& pkg, + const dependency& d) + { + assert (d.name == "build2"); + + // Extract, parse, and cache build2 version string. + // + if (build2_version.empty ()) + { + const char* args[] = {name_b (co), "--version", nullptr}; + + try + { + process_path pp (process::path_search (args[0], exec_dir)); + + if (verb >= 3) + print_process (args); + + process pr (pp, args, 0, -1); // Redirect STDOUT to pipe. + + string l; + try + { + ifdstream is (pr.in_ofd, fdstream_mode::skip); + getline (is, l); + is.close (); + + if (pr.wait () && l.compare (0, 7, "build2 ") == 0) + { + try + { + build2_version = version (string (l, 7)); + } + catch (const invalid_argument&) {} // Fall through. + } + + // Fall through. + } + catch (const ifdstream::failure&) + { + pr.wait (); + // Fall through. + } + + if (build2_version.empty ()) + fail << "unable to determine build2 version of " << args[0]; + } + catch (const process_error& e) + { + error << "unable to execute " << args[0] << ": " << e.what (); + + if (e.child ()) + exit (1); + + throw failed (); + } + } + + if (!satisfies (build2_version, d.constraint)) + fail << "unable to satisfy constraint (" << d << ") for package " + << pkg << + info << "available build2 version is " << build2_version; + } + + static version bpkg_version; + + void + satisfy_bpkg (const common_options&, const string& pkg, const dependency& d) + { + assert (d.name == "bpkg"); + + // Parse and cache bpkg version string. + // + if (bpkg_version.empty ()) + bpkg_version = version (BPKG_VERSION_STR); + + if (!satisfies (bpkg_version, d.constraint)) + fail << "unable to satisfy constraint (" << d << ") for package " + << pkg << + info << "available bpkg version is " << bpkg_version; + } } diff --git a/bpkg/utility b/bpkg/utility index a4bc6d3..b7dbf36 100644 --- a/bpkg/utility +++ b/bpkg/utility @@ -76,8 +76,8 @@ namespace bpkg // Process. // - // The process command line is printed for verbosity >= 2 (essential - // command lines). + // By default the process command line is printed for verbosity >= 2 + // (essential command lines). // // If fallback is specified, then this directory is searched for the // executable as a last resort. @@ -102,6 +102,9 @@ namespace bpkg // class common_options; + const char* + name_b (const common_options&); + void run_b (const common_options&, const dir_path& configuration, diff --git a/bpkg/utility.cxx b/bpkg/utility.cxx index 002d316..5d37972 100644 --- a/bpkg/utility.cxx +++ b/bpkg/utility.cxx @@ -7,6 +7,7 @@ #include // cout, cin #include +#include #include #include @@ -195,6 +196,14 @@ namespace bpkg } } + const char* + name_b (const common_options& co) + { + return co.build_specified () + ? co.build ().string ().c_str () + : "b" BPKG_EXE_SUFFIX; + } + void run_b (const common_options& co, const dir_path& c, @@ -203,11 +212,7 @@ namespace bpkg const strings& pvars, const strings& cvars) { - const char* b (co.build_specified () - ? co.build ().string ().c_str () - : "b" BPKG_EXE_SUFFIX); - - cstrings args {b}; + cstrings args {name_b (co)}; // Map verbosity level. If we are running quiet or at level 1, // then run build2 quiet. Otherwise, run it at the same level -- cgit v1.1