// file : bpkg/package.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #include #include // find_if() #include #include #include using namespace std; namespace bpkg { const version wildcard_version (0, "0", nullopt, 0, 0); // available_package_id // bool operator< (const available_package_id& x, const available_package_id& y) { int r (x.name.compare (y.name)); return r != 0 ? r < 0 : x.version < y.version; } // available_package // // Check if the package is available from the specified repository, its // prerequisite repositories, or one of their complements, recursively. // Return the first repository that contains the package or NULL if none // are. // // Note that we can end up with a repository dependency cycle since the // root repository can be the default complement for git repositories (see // rep_fetch() implementation for details). Thus we need to make sure that // the repository is not in the dependency chain yet. // using repositories = vector>>; static shared_ptr find (const shared_ptr& r, const shared_ptr& ap, repositories& chain, bool prereq) { // Prerequisites are not searched through recursively. // assert (!prereq || chain.empty ()); auto pr = [&r] (const shared_ptr& i) -> bool {return i == r;}; auto i (find_if (chain.begin (), chain.end (), pr)); if (i != chain.end ()) return nullptr; chain.emplace_back (r); unique_ptr deleter ( &chain, [] (repositories* r) {r->pop_back ();}); const auto& ps (r->prerequisites); const auto& cs (r->complements); for (const package_location& pl: ap->locations) { const lazy_shared_ptr& lr (pl.repository); // First check the repository itself. // if (lr.object_id () == r->name) return r; // Then check all the complements and prerequisites without // loading them. // if (cs.find (lr) != cs.end () || (prereq && ps.find (lr) != ps.end ())) return lr.load (); // Finally, load the complements and prerequisites and check them // recursively. // for (const lazy_shared_ptr& cr: cs) { // Should we consider prerequisites of our complements as our // prerequisites? I'd say not. // if (shared_ptr r = find (cr.load (), ap, chain, false)) return r; } if (prereq) { for (const lazy_weak_ptr& pr: ps) { if (shared_ptr r = find (pr.load (), ap, chain, false)) return r; } } } return nullptr; } shared_ptr filter (const shared_ptr& r, const shared_ptr& ap, bool prereq) { repositories chain; return find (r, ap, chain, prereq); } vector> filter (const shared_ptr& r, result&& apr, bool prereq) { vector> aps; for (shared_ptr ap: pointer_result (apr)) { if (filter (r, ap, prereq) != nullptr) aps.push_back (move (ap)); } return aps; } pair, shared_ptr> filter_one (const shared_ptr& r, result&& apr, bool prereq) { using result = pair, shared_ptr>; for (shared_ptr ap: pointer_result (apr)) { if (shared_ptr pr = filter (r, ap, prereq)) return result (move (ap), move (pr)); } return result (); } vector> filter (const vector>& rps, odb::result&& apr, bool prereq) { vector> aps; for (shared_ptr ap: pointer_result (apr)) { for (const shared_ptr r: rps) { if (filter (r, ap, prereq) != nullptr) { aps.push_back (move (ap)); break; } } } return aps; } // selected_package // string selected_package:: version_string () const { return version != wildcard_version ? version.string () : "*"; } optional package_iteration (const common_options& o, const dir_path& c, transaction& t, const dir_path& d, const string& n, const version& v, bool check_external) { tracer trace ("package_iteration"); database& db (t.database ()); tracer_guard tg (db, trace); if (check_external) { using query = query; query q ( query::package::id.name == n && compare_version_eq (query::package::id.version, v, true, false)); for (const auto& pr: db.query (q)) { const shared_ptr& r (pr.repository); if (r->location.directory_based ()) fail << "external package " << n << '/' << v << " is already available from " << r->location.canonical_name (); } } shared_ptr p (db.find (n)); if (p == nullptr || !p->src_root || compare_version_ne (v, p->version, true, false)) return nullopt; string mc (sha256 (o, d / manifest_file)); // The selected package must not be "simulated" (see pkg-build for // details). // assert (p->manifest_checksum); bool changed (mc != *p->manifest_checksum); // If the manifest didn't changed but the selected package points to an // external source directory, then we also check if the directory have // moved. // if (!changed && p->external ()) { dir_path src_root (p->effective_src_root (c)); // We need to complete and normalize the source directory as it may // generally be completed against the configuration directory (unlikely // but possible), that can be relative and/or not normalized. // src_root.complete ().normalize (); changed = src_root != dir_path (d).complete ().normalize (); } return !changed ? p->version : version (v.epoch, v.upstream, v.release, v.revision, p->version.iteration + 1); } // state // string to_string (package_state s) { switch (s) { case package_state::transient: return "transient"; case package_state::broken: return "broken"; case package_state::fetched: return "fetched"; case package_state::unpacked: return "unpacked"; case package_state::configured: return "configured"; } return string (); // Should never reach. } package_state to_package_state (const string& s) { if (s == "transient") return package_state::transient; else if (s == "broken") return package_state::broken; else if (s == "fetched") return package_state::fetched; else if (s == "unpacked") return package_state::unpacked; else if (s == "configured") return package_state::configured; else throw invalid_argument ("invalid package state '" + s + "'"); } // substate // string to_string (package_substate s) { switch (s) { case package_substate::none: return "none"; case package_substate::system: return "system"; } return string (); // Should never reach. } package_substate to_package_substate (const string& s) { if (s == "none") return package_substate::none; else if (s == "system") return package_substate::system; else throw invalid_argument ("invalid package substate '" + s + "'"); } // certificate // ostream& operator<< (ostream& os, const certificate& c) { using butl::operator<<; if (c.dummy ()) os << c.name << " (dummy)"; else os << c.name << ", \"" << c.organization << "\" <" << c.email << ">, " << c.start_date << " - " << c.end_date << ", " << c.fingerprint; return os; } }