From 4c89e8a9d54774d71aff661c918e11170c3d2693 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 21 Mar 2021 16:00:33 +0000 Subject: [PATCH] =?UTF-8?q?Packages:=20add=20=E2=80=98provides=E2=80=99=20?= =?UTF-8?q?listing=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support a list of provided files on a package, to make it easier to identify which package to install based on a required file path. --- simgear/package/Catalog.cxx | 33 +++++++++++++++++++- simgear/package/Catalog.hxx | 4 ++- simgear/package/CatalogTest.cxx | 38 +++++++++++++++++++++--- simgear/package/Package.cxx | 37 +++++++++++++++-------- simgear/package/Package.hxx | 9 ++++++ simgear/package/PackageCommon.hxx | 5 +++- simgear/package/Root.cxx | 36 ++++++++++++++++++++++ simgear/package/Root.hxx | 17 ++++++++++- simgear/package/catalogTest1/catalog.xml | 20 +++++++++++++ 9 files changed, 179 insertions(+), 20 deletions(-) diff --git a/simgear/package/Catalog.cxx b/simgear/package/Catalog.cxx index fb09685f..e6b27a7f 100644 --- a/simgear/package/Catalog.cxx +++ b/simgear/package/Catalog.cxx @@ -298,6 +298,9 @@ bool Catalog::removeDirectory() PackageList Catalog::packages(Type ty) const { + if (ty == AnyPackageType) + return m_packages; + PackageList r; std::copy_if(m_packages.begin(), m_packages.end(), std::back_inserter(r), @@ -338,7 +341,9 @@ Catalog::installedPackages(Type ty) const std::copy_if(m_packages.begin(), m_packages.end(), std::back_inserter(r), [ty](const PackageRef& p) { - return p->isInstalled() && p->type() == ty; + if (ty != AnyPackageType && (p->type() != ty)) + return false; + return p->isInstalled(); }); return r; } @@ -726,6 +731,32 @@ int Catalog::markPackagesForInstallation(const string_list &packageIds) { CatalogRef Catalog::migratedFrom() const { return m_migratedFrom; } +PackageList Catalog::packagesProviding(const Type inferredType, const std::string& directory, const std::string& subpath) const +{ + PackageList p; + copy_if(m_packages.begin(), m_packages.end(), std::back_inserter(p), + [inferredType, &directory, &subpath](const PackageRef& pkg) { + // if we detected a package type, it needs to match, so the resulting + // path matches as well + if (inferredType != AnyPackageType) { + if (inferredType != pkg->type()) { + return false; + } + } + + if (directory != pkg->dirName()) + return false; + + if (subpath.empty()) { + return true; // we're done, success + } + + return pkg->doesProvidePath(subpath); + }); + return p; +} + + } // of namespace pkg } // of namespace simgear diff --git a/simgear/package/Catalog.hxx b/simgear/package/Catalog.hxx index 5ca664e4..8939d2f2 100644 --- a/simgear/package/Catalog.hxx +++ b/simgear/package/Catalog.hxx @@ -194,7 +194,9 @@ public: void changeStatus(Delegate::StatusCode newStatus); void processAlternate(SGPropertyNode_ptr alt); - + + PackageList packagesProviding(const Type inferredType, const std::string& path, const std::string& subpath) const; + Root* m_root; SGPropertyNode_ptr m_props; SGPath m_installRoot; diff --git a/simgear/package/CatalogTest.cxx b/simgear/package/CatalogTest.cxx index 09246d70..d0be1561 100644 --- a/simgear/package/CatalogTest.cxx +++ b/simgear/package/CatalogTest.cxx @@ -177,7 +177,9 @@ int parseTest() SG_CHECK_EQUAL(cat->description(), "First test catalog"); // check the packages too - SG_CHECK_EQUAL(cat->packages().size(), 6); + SG_CHECK_EQUAL(cat->packages().size(), 4); + SG_CHECK_EQUAL(cat->packages(simgear::pkg::LibraryPackage).size(), 2); + SG_CHECK_EQUAL(cat->packages(simgear::pkg::AIModelPackage).size(), 1); pkg::PackageRef p1 = cat->packages().front(); SG_CHECK_EQUAL(p1->catalog(), cat.ptr()); @@ -187,6 +189,7 @@ int parseTest() SG_CHECK_EQUAL(p1->name(), "Alpha package"); SG_CHECK_EQUAL(p1->revision(), 8); SG_CHECK_EQUAL(p1->fileSizeBytes(), 593); + SG_CHECK_EQUAL(p1->type(), simgear::pkg::AircraftPackage); pkg::PackageRef p2 = cat->getPackageById("c172p"); @@ -370,7 +373,7 @@ void testAddCatalog(HTTP::Client* cl) p.append("org.flightgear.test.catalog1"); p.append("catalog.xml"); SG_VERIFY(p.exists()); - SG_CHECK_EQUAL(root->allPackages().size(), 6); + SG_CHECK_EQUAL(root->allPackages().size(), 4); SG_CHECK_EQUAL(root->catalogs().size(), 1); pkg::PackageRef p1 = root->getPackageById("alpha"); @@ -1219,8 +1222,8 @@ void testMigrateInstalled(HTTP::Client *cl) { waitForUpdateComplete(cl, root); string_list existing; - for (const auto &pack : oldCatalog->installedPackages()) { - existing.push_back(pack->id()); + for (const auto& pack : oldCatalog->installedPackages(simgear::pkg::AnyPackageType)) { + existing.push_back(pack->id()); } SG_CHECK_EQUAL(4, existing.size()); @@ -1309,6 +1312,31 @@ void testDontMigrateRemoved(HTTP::Client *cl) { } } +void testProvides(HTTP::Client* cl) +{ + SGPath rootPath(simgear::Dir::current().path()); + rootPath.append("pkg_check_provides"); + simgear::Dir pd(rootPath); + pd.removeChildren(); + global_catalogVersion = 0; + + pkg::RootRef root(new pkg::Root(rootPath, "8.1.2")); + // specify a test dir + root->setHTTPClient(cl); + + pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml"); + waitForUpdateComplete(cl, root); + + // query + auto packages = root->packagesProviding("Aircraft/b744", false); + SG_CHECK_EQUAL(packages.size(), 1); + SG_CHECK_EQUAL(packages.front()->qualifiedId(), "org.flightgear.test.catalog1.b747-400"); + + packages = root->packagesProviding("movies/Foo/intro.mov", false); + SG_CHECK_EQUAL(packages.size(), 1); + SG_CHECK_EQUAL(packages.front()->qualifiedId(), "org.flightgear.test.catalog1.movies"); +} + int main(int argc, char* argv[]) { sglog().setLogLevels( SG_ALL, SG_WARN ); @@ -1358,6 +1386,8 @@ int main(int argc, char* argv[]) testDontMigrateRemoved(&cl); + testProvides(&cl); + cerr << "Successfully passed all tests!" << endl; return EXIT_SUCCESS; } diff --git a/simgear/package/Package.cxx b/simgear/package/Package.cxx index cb9e34fe..df9c7d10 100644 --- a/simgear/package/Package.cxx +++ b/simgear/package/Package.cxx @@ -34,9 +34,10 @@ namespace { const string_list static_typeNames = { "aircraft", "ai-model", - "add-on"}; + "add-on", + "library"}; -} +} // namespace namespace simgear { @@ -79,6 +80,10 @@ void Package::initWithProps(const SGPropertyNode* aProps) } else { m_type = AircraftPackage; } + + for (auto p : m_props->getChildren("provides")) { + m_provides.push_back(p->getStringValue()); + } } void Package::updateFromProps(const SGPropertyNode* aProps) @@ -221,7 +226,10 @@ bool Package::isInstalled() const std::string Package::directoryForType(Type type) { switch (type) { + case AnyPackageType: + throw sg_range_exception("Package::directoryForType: passed 'AnyPackage'"); case AircraftPackage: + case LibraryPackage: default: return "Aircraft"; case AIModelPackage: @@ -353,7 +361,7 @@ unsigned int Package::revision() const std::string Package::name() const { - return m_props->getStringValue("name"); + return getLocalisedProp("name", 0); } size_t Package::fileSizeBytes() const @@ -482,14 +490,8 @@ std::string Package::nameForVariant(const std::string& vid) const return name(); } - for (auto var : m_props->getChildren("variant")) { - if (vid == var->getStringValue("id")) { - return var->getStringValue("name"); - } - } - - - throw sg_exception("Unknow variant +" + vid + " in package " + id()); + const int index = indexOfVariant(vid); + return nameForVariant(index); } unsigned int Package::indexOfVariant(const std::string& vid) const @@ -515,7 +517,7 @@ unsigned int Package::indexOfVariant(const std::string& vid) const std::string Package::nameForVariant(const unsigned int vIndex) const { - return propsForVariant(vIndex, "name")->getStringValue("name"); + return getLocalisedProp("name", vIndex); } SGPropertyNode_ptr Package::propsForVariant(const unsigned int vIndex, const char* propName) const @@ -635,6 +637,17 @@ Type Package::type() const return m_type; } +string_list Package::providesPaths() const +{ + return m_provides; +} + +bool Package::doesProvidePath(const std::string& p) const +{ + auto it = std::find(m_provides.begin(), m_provides.end(), p); + return it != m_provides.end(); +} + } // of namespace pkg } // of namespace simgear diff --git a/simgear/package/Package.hxx b/simgear/package/Package.hxx index abb850bd..3c73434e 100644 --- a/simgear/package/Package.hxx +++ b/simgear/package/Package.hxx @@ -209,6 +209,14 @@ public: static std::string directoryForType(Type type); + /** + @brief key files provided by this package. Optional list of externally interesting files + within this package, relative to the package root. + */ + string_list providesPaths() const; + + bool doesProvidePath(const std::string& p) const; + private: SGPath pathOnDisk() const; @@ -240,6 +248,7 @@ private: string_set m_tags; Catalog* m_catalog = nullptr; // non-owning ref, explicitly string_list m_variants; + string_list m_provides; mutable function_list _install_cb; }; diff --git a/simgear/package/PackageCommon.hxx b/simgear/package/PackageCommon.hxx index 1902e6ab..144ed2bb 100644 --- a/simgear/package/PackageCommon.hxx +++ b/simgear/package/PackageCommon.hxx @@ -28,10 +28,13 @@ namespace pkg { enum Type { AircraftPackage = 0, AIModelPackage, - AddOnPackage + AddOnPackage, + LibraryPackage, ///< common files for use by other package(s) (eg, the DavePack) // if you extend this enum, extend the static_typeNames string array // in Package.cxx file as well. + + AnyPackageType = 9999 }; // forward decls diff --git a/simgear/package/Root.cxx b/simgear/package/Root.cxx index affac887..b458f869 100755 --- a/simgear/package/Root.cxx +++ b/simgear/package/Root.cxx @@ -906,6 +906,42 @@ string_list Root::explicitlyRemovedCatalogs() const { return d->manuallyRemovedCatalogs; } + +PackageList Root::packagesProviding(const std::string& path, bool onlyInstalled) const +{ + string modPath = path; + auto inferredType = AnyPackageType; + if (strutils::starts_with(path, "Aircraft/")) { + inferredType = AircraftPackage; + modPath = path.substr(9); + } else if (strutils::starts_with(path, "AI/Aircraft/")) { + inferredType = AIModelPackage; + modPath = path.substr(12); + } + + string subPath; + const auto firstSeperatorPos = modPath.find('/'); + if (firstSeperatorPos != std::string::npos) { + subPath = modPath.substr(firstSeperatorPos + 1); + modPath.resize(firstSeperatorPos); + } + + PackageList r; + for (auto cat : d->catalogs) { + const auto p = cat.second->packagesProviding(inferredType, modPath, subPath); + r.insert(r.end(), p.begin(), p.end()); + } // catalog iteratrion + + if (onlyInstalled) { + auto it = std::remove_if(r.begin(), r.end(), [](const PackageRef& p) { + return p->isInstalled(); + }); + r.erase(it, r.end()); + } + + return r; +} + } // of namespace pkg } // of namespace simgear diff --git a/simgear/package/Root.hxx b/simgear/package/Root.hxx index 05adbddf..6ad8907b 100644 --- a/simgear/package/Root.hxx +++ b/simgear/package/Root.hxx @@ -159,7 +159,22 @@ public: */ string_list explicitlyRemovedCatalogs() const; - private: + /** + * @brief Given a relative path to a file, return the packages which provide it. + * If the path starts with a type-based prefix (eg 'Aircraft' or 'AI/Aircraft'), the + * corresponding package type will be considered. The next item must correspond to + * the package directory name. + * + * If the path contains components more specific than this, they will be checked + * against matching packkages 'provides' list. If the path does not contain such + * components, a match of the type+directory name will be considered sufficient. + * + * @param path + * @return PackageList + */ + PackageList packagesProviding(const std::string& path, bool onlyInstalled) const; + +private: friend class Install; friend class Catalog; friend class Package; diff --git a/simgear/package/catalogTest1/catalog.xml b/simgear/package/catalogTest1/catalog.xml index 76070b80..e06b1a69 100644 --- a/simgear/package/catalogTest1/catalog.xml +++ b/simgear/package/catalogTest1/catalog.xml @@ -230,6 +230,9 @@ http://localhost:2000/catalogTest1/common-sounds.zip 360 acf9eb89cf396eb42f8823d9cdf17584 + library + engine1.wav + engine3.wav @@ -244,5 +247,22 @@ movies_6789 232 e5f89c3f1ed1bdda16174c868f3c7b30 + library + Foo/intro.mov + + + b737-ng-ai + Boeing 737 NG AI Model + b737NG + AI Model for the 737-NG + 111 + 860 + ai-model + 737NG/Models/BritishAirways-738.xml + a94ca5704f305b90767f40617d194ed6 + http://localhost:2000/mirrorA/b737.tar.gz + http://localhost:2000/mirrorB/b737.tar.gz + http://localhost:2000/mirrorC/b737.tar.gz +