https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/setup.git;h=bb8a17ba89743eeaa17574504395d4a9968edfc8 commit bb8a17ba89743eeaa17574504395d4a9968edfc8 Merge: b7bcb7e b1902cb Author: Jon Turney <jon.turney@dronecode.org.uk> Date: Sun Jan 28 15:12:03 2018 +0000 Merge branch 'topic/libsolv' Diff: --- IniDBBuilderPackage.cc | 346 ++++++---------- IniDBBuilderPackage.h | 32 +- Makefile.am | 9 +- PackageSpecification.cc | 18 + PackageSpecification.h | 9 +- PickPackageLine.cc | 1 - PickView.cc | 4 - README | 28 ++ bootstrap.sh | 2 +- choose.cc | 171 ++++++-- choose.h | 10 +- configure.ac | 2 + confirm.cc | 148 +++++++ confirm.h | 34 ++ cygpackage.cc | 143 ------- cygpackage.h | 74 ---- desktop.cc | 1 - download.cc | 76 ++--- filemanip.cc | 18 +- filemanip.h | 1 - ini.cc | 8 +- inilex.ll | 3 + iniparse.yy | 9 + install.cc | 99 ++--- libsolv.cc | 1018 +++++++++++++++++++++++++++++++++++++++++++++++ libsolv.h | 276 +++++++++++++ main.cc | 4 + package_db.cc | 301 ++++++++++++-- package_db.h | 34 ++- package_depends.cc | 19 +- package_depends.h | 2 +- package_meta.cc | 130 +++++-- package_meta.h | 21 +- package_version.cc | 320 --------------- package_version.h | 164 +-------- prereq.cc | 245 +++--------- prereq.h | 27 +- res.rc | 42 ++- resource.h | 6 +- 39 files changed, 2430 insertions(+), 1425 deletions(-) diff --git a/IniDBBuilderPackage.cc b/IniDBBuilderPackage.cc index a964ee0..4169634 100644 --- a/IniDBBuilderPackage.cc +++ b/IniDBBuilderPackage.cc @@ -22,8 +22,6 @@ #include "IniParseFeedback.h" #include "package_db.h" #include "package_meta.h" -#include "package_version.h" -#include "cygpackage.h" #include "ini.h" // for strtoul #include <string.h> @@ -34,7 +32,7 @@ using namespace std; IniDBBuilderPackage::IniDBBuilderPackage (IniParseFeedback const &aFeedback) : -cp (0), cbpv (), cspv (), currentSpec (0), currentNodeList (0), trust (0), _feedback (aFeedback){} +currentSpec (0), _feedback (aFeedback){} IniDBBuilderPackage::~IniDBBuilderPackage() { @@ -67,33 +65,34 @@ IniDBBuilderPackage::buildVersion (const std::string& aVersion) } void -IniDBBuilderPackage::buildPackage (const std::string& name) +IniDBBuilderPackage::buildPackage (const std::string& _name) { -#if DEBUG - if (cp) - { - Log (LOG_BABBLE) << "Finished with package " << cp->name << endLog; - if (cbpv) - { - Log (LOG_BABBLE) << "Version " << cbpv.Canonical_version() << endLog; - Log (LOG_BABBLE) << "Depends:"; - dumpPackageDepends (cbpv.depends(), Log (LOG_BABBLE)); - Log (LOG_BABBLE) << endLog; - } - } -#endif - packagedb db; - cp = db.findBinary (PackageSpecification(name)); - if (!cp) - { - cp = new packagemeta (name); - db.packages.insert (packagedb::packagecollection::value_type(cp->name,cp)); - } - cbpv = cygpackage::createInstance (name, package_binary); - cspv = packageversion (); + process(); + + /* Reset for next package */ + name = _name; + message_id = ""; + message_string = ""; + categories.clear(); + replace_versions.clear(); + + cbpv.reponame = release; + cbpv.version = ""; + cbpv.vendor = release; + cbpv.sdesc = ""; + cbpv.ldesc = ""; + cbpv.stability = TRUST_CURR; + cbpv.type = package_binary; + cbpv.spkg = PackageSpecification(); + cbpv.spkg_id = packageversion(); + cbpv.requires = NULL; + cbpv.obsoletes = NULL; + cbpv.archive = packagesource(); + currentSpec = NULL; currentNodeList = NULL; - trust = TRUST_CURR; + dependsNodeList = PackageDepends(); + obsoletesNodeList = PackageDepends(); #if DEBUG Log (LOG_BABBLE) << "Created package " << name << endLog; #endif @@ -102,20 +101,19 @@ IniDBBuilderPackage::buildPackage (const std::string& name) void IniDBBuilderPackage::buildPackageVersion (const std::string& version) { - cbpv.setCanonicalVersion (version); - add_correct_version(); + cbpv.version = version; } void IniDBBuilderPackage::buildPackageSDesc (const std::string& theDesc) { - cbpv.set_sdesc(theDesc); + cbpv.sdesc = theDesc; } void IniDBBuilderPackage::buildPackageLDesc (const std::string& theDesc) { - cbpv.set_ldesc(theDesc); + cbpv.ldesc = theDesc; } void @@ -124,21 +122,23 @@ IniDBBuilderPackage::buildPackageInstall (const std::string& path, char *hash, hashType type) { - process_src (*cbpv.source(), path); - setSourceSize (*cbpv.source(), size); + // set archive path, size, mirror, hash + cbpv.archive.set_canonical(path.c_str()); + cbpv.archive.size = atoi(size.c_str()); + cbpv.archive.sites.push_back(site(parse_mirror)); switch (type) { case hashType::sha512: - if (hash && !cbpv.source()->sha512_isSet) + if (hash && !cbpv.archive.sha512_isSet) { - memcpy (cbpv.source()->sha512sum, hash, sizeof(cbpv.source()->sha512sum)); - cbpv.source()->sha512_isSet = true; + memcpy (cbpv.archive.sha512sum, hash, sizeof(cbpv.archive.sha512sum)); + cbpv.archive.sha512_isSet = true; } break; case hashType::md5: - if (hash && !cbpv.source()->md5.isSet()) - cbpv.source()->md5.set((unsigned char *)hash); + if (hash && !cbpv.archive.md5.isSet()) + cbpv.archive.md5.set((unsigned char *)hash); break; case hashType::none: @@ -152,91 +152,70 @@ IniDBBuilderPackage::buildPackageSource (const std::string& path, char *hash, hashType type) { - packagedb db; - /* get an appropriate metadata */ - csp = db.findSource (PackageSpecification (cbpv.Name())); - if (!csp) - { - /* Copy the existing meta data to a new source package */ - csp = new packagemeta (*cp); - /* delete versions information */ - csp->versions.clear(); - csp->desired = packageversion(); - csp->installed = packageversion(); - csp->curr = packageversion(); - csp->exp = packageversion(); - db.sourcePackages.insert (packagedb::packagecollection::value_type(csp->name,csp)); - } - /* create a source packageversion */ - cspv = cygpackage::createInstance (cbpv.Name(), package_source); - cspv.setCanonicalVersion (cbpv.Canonical_version()); - set<packageversion>::iterator i=find (csp->versions.begin(), - csp->versions.end(), cspv); - if (i == csp->versions.end()) - { - csp->add_version (cspv); - } - else - cspv = *i; - - if (!cspv.source()->Canonical()) - cspv.source()->set_canonical (path.c_str()); - cspv.source()->sites.push_back(site(parse_mirror)); + /* When there is a source: line, we invent a package to contain the source, + and make it the source package for this package. */ - /* creates the relationship between binary and source packageversions */ - cbpv.setSourcePackageSpecification (PackageSpecification (cspv.Name())); - PackageSpecification &spec = cbpv.sourcePackageSpecification(); - spec.setOperator (PackageSpecification::Equals); - spec.setVersion (cbpv.Canonical_version()); + /* create a source package version */ + SolverPool::addPackageData cspv = cbpv; + cspv.type = package_source; + cspv.requires = NULL; + cspv.obsoletes = NULL; - setSourceSize (*cspv.source(), size); + /* set archive path, size, mirror, hash */ + cspv.archive = packagesource(); + cspv.archive.set_canonical(path.c_str()); + cspv.archive.size = atoi(size.c_str()); + cspv.archive.sites.push_back(site(parse_mirror)); switch (type) { case hashType::sha512: - if (hash && !cspv.source()->sha512_isSet) + if (hash && !cspv.archive.sha512_isSet) { - memcpy (cspv.source()->sha512sum, hash, sizeof(cspv.source()->sha512sum)); - cspv.source()->sha512_isSet = true; + memcpy (cspv.archive.sha512sum, hash, sizeof(cspv.archive.sha512sum)); + cspv.archive.sha512_isSet = true; } break; case hashType::md5: - if (hash && !cspv.source()->md5.isSet()) - cspv.source()->md5.set((unsigned char *)hash); + if (hash && !cspv.archive.md5.isSet()) + cspv.archive.md5.set((unsigned char *)hash); break; case hashType::none: break; } + + packagedb db; + packageversion spkg_id = db.addSource (name + "-src", cspv); + + /* create relationship between binary and source packageversions */ + cbpv.spkg = PackageSpecification(name + "-src"); + cbpv.spkg_id = spkg_id; } void -IniDBBuilderPackage::buildPackageTrust (int newtrust) +IniDBBuilderPackage::buildPackageTrust (trusts newtrust) { - trust = newtrust; - if (newtrust != TRUST_UNKNOWN) - { - cbpv = cygpackage::createInstance (cp->name, package_binary); - cspv = packageversion (); - } + process(); + cbpv.stability = newtrust; } void IniDBBuilderPackage::buildPackageCategory (const std::string& name) { - cp->add_category (name); + categories.insert(name); } void IniDBBuilderPackage::buildBeginDepends () { #if DEBUG - Log (LOG_BABBLE) << "Beginning of a depends statement for " << cp->name - << endLog; + Log (LOG_BABBLE) << "Beginning of a depends statement " << endLog; #endif currentSpec = NULL; - cbpv.depends()->clear(); - currentNodeList = cbpv.depends(); + dependsNodeList = PackageDepends(); + currentNodeList = &dependsNodeList; + cbpv.requires = &dependsNodeList; } void @@ -246,55 +225,49 @@ IniDBBuilderPackage::buildBeginBuildDepends () Log (LOG_BABBLE) << "Beginning of a Build-Depends statement" << endLog; #endif currentSpec = NULL; - currentNodeList = NULL; /* there is currently nowhere to store Build-Depends information */ + currentNodeList = NULL; + /* there is currently nowhere to store Build-Depends information */ } void -IniDBBuilderPackage::buildSourceName (const std::string& name) +IniDBBuilderPackage::buildBeginObsoletes () { - if (cbpv) - { - cbpv.setSourcePackageSpecification (PackageSpecification (name)); #if DEBUG - Log (LOG_BABBLE) << "\"" << cbpv.sourcePackageSpecification() << - "\" is the source package for " << cp->name << "." << endLog; + Log (LOG_BABBLE) << "Beginning of an obsoletes statement" << endLog; #endif - } - else - _feedback.warning ((std::string ("Attempt to set source for package") - + std::string(cp->name) - + "before creation of a version.").c_str()); + currentSpec = NULL; + obsoletesNodeList = PackageDepends(); + currentNodeList = &obsoletesNodeList; + cbpv.obsoletes = &obsoletesNodeList; } void -IniDBBuilderPackage::buildSourceNameVersion (const std::string& version) +IniDBBuilderPackage::buildSourceName (const std::string& _name) { - if (cbpv) - { - cbpv.sourcePackageSpecification().setOperator (PackageSpecification::Equals); - cbpv.sourcePackageSpecification().setVersion (version); + // When there is a Source: line, that names a real source package + packagedb db; + cbpv.spkg = PackageSpecification(_name); + cbpv.spkg_id = db.findSourceVersion (PackageSpecification(_name, cbpv.version)); #if DEBUG - Log (LOG_BABBLE) << "The source version needed for " << cp->name << - " is " << version << "." << endLog; + Log (LOG_BABBLE) << "\"" << _name << "\" is the source package for " << name << "." << endLog; #endif - } - else - _feedback.warning ((std::string ("Attempt to set source version for package") - + std::string(cp->name) - + "before creation of a version.").c_str()); +} + +void +IniDBBuilderPackage::buildSourceNameVersion (const std::string& version) +{ + // XXX: should be stored as sourceevr } void IniDBBuilderPackage::buildPackageListNode (const std::string & name) { - if (currentNodeList) - { #if DEBUG - Log (LOG_BABBLE) << "New node '" << name << "' for package list" << endLog; + Log (LOG_BABBLE) << "New node '" << name << "' for package list" << endLog; #endif - currentSpec = new PackageSpecification (name); - currentNodeList->push_back (currentSpec); - } + currentSpec = new PackageSpecification (name); + if (currentNodeList) + currentNodeList->push_back (currentSpec); } void @@ -308,13 +281,8 @@ IniDBBuilderPackage::buildPackageListOperator (PackageSpecification::_operators endLog; #endif } - else - _feedback.warning ((std::string ("Attempt to set an operator for package ") - + std::string(cp->name) - + " with no current specification.").c_str()); } - void IniDBBuilderPackage::buildPackageListOperatorVersion (const std::string& aVersion) { @@ -326,110 +294,50 @@ IniDBBuilderPackage::buildPackageListOperatorVersion (const std::string& aVersio endLog; #endif } - else - _feedback.warning ((std::string ("Attempt to set an operator version for package ") - + std::string(cp->name) - + " with no current specification.").c_str()); } -/* privates */ - void -IniDBBuilderPackage::add_correct_version() +IniDBBuilderPackage::buildMessage (const std::string& _message_id, const std::string& _message_string) { - if (currentNodeList) - *cbpv.depends() = *currentNodeList; - - int merged = 0; - for (set<packageversion>::iterator n = cp->versions.begin(); - !merged && n != cp->versions.end(); ++n) - if (*n == cbpv ) - { - packageversion ver = *n; - /* ASSUMPTIONS: - categories and requires are consistent for the same version across - all mirrors - */ - /* - XXX: if the versions are equal but the size/md5sum are different, - we should alert the user, as they may not be getting what they expect... - */ - /* Copy the binary mirror across if this site claims to have an install */ - if (cbpv.source()->sites.size() ) - ver.source()->sites.push_back(site (cbpv.source()->sites.begin()->key)); - /* Copy the descriptions across */ - if (cbpv.SDesc ().size() && !n->SDesc ().size()) - ver.set_sdesc (cbpv.SDesc ()); - if (cbpv.LDesc ().size() && !n->LDesc ().size()) - ver.set_ldesc (cbpv.LDesc ()); - if (cbpv.depends()->size() && !ver.depends ()->size()) - *ver.depends() = *cbpv.depends(); - /* TODO: other package lists */ - /* Prevent dangling references */ - currentNodeList = NULL; - currentSpec = NULL; - cbpv = *n; - merged = 1; -#if DEBUG - Log (LOG_BABBLE) << cp->name << " merged with an existing version " << cbpv.Canonical_version() << endLog; -#endif - } - - if (!merged) - { - cp->add_version (cbpv); -#if DEBUG - Log (LOG_BABBLE) << cp->name << " version " << cbpv.Canonical_version() << " added" << endLog; -#endif - } - - /* - Should this version be the one selected for this package at a given - stability/trust setting? After merging potentially multiple package - databases, we should pick the one with the highest version number. - */ - packageversion *v = NULL; - switch (trust) - { - case TRUST_CURR: - v = &(cp->curr); - break; - case TRUST_TEST: - v = &(cp->exp); - break; - } - - if (v) - { - int comparison = packageversion::compareVersions(cbpv, *v); - - if ((bool)(*v)) - Log (LOG_BABBLE) << "package " << cp->name << " comparing versions " << cbpv.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog; - - if (comparison > 0) - { - *v = cbpv; - } - } + message_id = _message_id; + message_string = _message_string; } void -IniDBBuilderPackage::process_src (packagesource &src, const std::string& path) +IniDBBuilderPackage::buildPackageReplaceVersionsList (const std::string& version) { - if (!src.Canonical()) - src.set_canonical (path.c_str()); - src.sites.push_back(site(parse_mirror)); + replace_versions.insert(version); } +/* privates */ void -IniDBBuilderPackage::setSourceSize (packagesource &src, const std::string& size) +IniDBBuilderPackage::process () { - if (!src.size) - src.size = atoi(size.c_str()); -} + if (!name.size()) + return; -void -IniDBBuilderPackage::buildMessage (const std::string& message_id, const std::string& message) -{ - cp->set_message (message_id, message); +#if DEBUG + Log (LOG_BABBLE) << "Finished with package " << name << endLog; + Log (LOG_BABBLE) << "Version " << cbpv.version << endLog; +#endif + + /* Transfer the accumulated package information to packagedb */ + packagedb db; + packagemeta *pkg = db.addBinary (name, cbpv); + + // For no good historical reason, some data lives in packagemeta rather than + // the packageversion + for (auto i = categories.begin(); i != categories.end(); i++) + { + pkg->add_category(*i); + } + pkg->set_message(message_id, message_string); + pkg->set_version_blacklist(replace_versions); + + // Reset for next version + cbpv.version = ""; + cbpv.type = package_binary; + cbpv.spkg = PackageSpecification(); + cbpv.spkg_id = packageversion(); + cbpv.archive = packagesource(); } diff --git a/IniDBBuilderPackage.h b/IniDBBuilderPackage.h index 323b186..da2d97d 100644 --- a/IniDBBuilderPackage.h +++ b/IniDBBuilderPackage.h @@ -17,11 +17,15 @@ #define SETUP_INIDBBUILDERPACKAGE_H #include <vector> -#include "package_version.h" +#include <set> + +#include "package_message.h" +#include "PackageTrust.h" +#include "String++.h" +#include "libsolv.h" class IniParseFeedback; class packagesource; -class packagemeta; enum class hashType { none, md5, sha512 }; @@ -42,21 +46,24 @@ public: void buildPackageSource (const std::string&, const std::string&, char *, hashType); - void buildPackageTrust (int); + void buildPackageTrust (trusts); void buildPackageCategory (const std::string& ); void buildBeginDepends (); void buildBeginBuildDepends (); + void buildBeginObsoletes (); void buildMessage (const std::string&, const std::string&); void buildSourceName (const std::string& ); void buildSourceNameVersion (const std::string& ); void buildPackageListNode (const std::string& ); void buildPackageListOperator (PackageSpecification::_operators const &); void buildPackageListOperatorVersion (const std::string& ); + void buildPackageReplaceVersionsList (const std::string& ); void set_arch (const std::string& a) { arch = a; } void set_release (const std::string& rel) { release = rel; } + // setup.ini header data unsigned int timestamp; std::string arch; std::string release; @@ -64,17 +71,20 @@ public: std::string parse_mirror; private: - void add_correct_version(); - void process_src (packagesource &src, const std::string& ); - void setSourceSize (packagesource &src, const std::string& ); + void process (); - packagemeta *cp; - packageversion cbpv; - packagemeta *csp; - packageversion cspv; + // package data + std::string name; + std::set <std::string, casecompare_lt_op> categories; + std::string message_id; + std::string message_string; PackageSpecification *currentSpec; PackageDepends *currentNodeList; - int trust; + PackageDepends dependsNodeList; + PackageDepends obsoletesNodeList; + SolverPool::addPackageData cbpv; + std::set <std::string> replace_versions; + IniParseFeedback const &_feedback; }; diff --git a/Makefile.am b/Makefile.am index a238d88..37341b9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -98,7 +98,7 @@ inilint_SOURCES = \ String++.h @SETUP@_LDADD = \ - libgetopt++/libgetopt++.la -lgcrypt -lgpg-error -llzma -lbz2 -lz \ + libgetopt++/libgetopt++.la -lgcrypt -lgpg-error -llzma -lbz2 -lz -lsolv -lregex \ -lshlwapi -lcomctl32 -lole32 -lws2_32 -lpsapi -luuid -lntdll -lwininet -lmingw32 @SETUP@_LDFLAGS = -mwindows -Wc,-static -static-libtool-libs @SETUP@_SOURCES = \ @@ -119,6 +119,8 @@ inilint_SOURCES = \ compress_gz.h \ compress_xz.cc \ compress_xz.h \ + confirm.cc \ + confirm.h \ ConnectionSetting.cc \ ConnectionSetting.h \ ControlAdjuster.cc \ @@ -126,8 +128,6 @@ inilint_SOURCES = \ crypto.cc \ crypto.h \ cyg-pubkey.h \ - cygpackage.cc \ - cygpackage.h \ desktop.cc \ desktop.h \ dialog.cc \ @@ -171,6 +171,8 @@ inilint_SOURCES = \ IOStreamProvider.h \ KeysSetting.cc \ KeysSetting.h \ + libsolv.cc \ + libsolv.h \ localdir.cc \ localdir.h \ LogFile.cc \ @@ -206,7 +208,6 @@ inilint_SOURCES = \ package_meta.h \ package_source.cc \ package_source.h \ - package_version.cc \ package_version.h \ PackageSpecification.cc \ PackageSpecification.h \ diff --git a/PackageSpecification.cc b/PackageSpecification.cc index 247f3da..b58ad80 100644 --- a/PackageSpecification.cc +++ b/PackageSpecification.cc @@ -22,12 +22,30 @@ PackageSpecification::PackageSpecification (const std::string& packageName) { } +PackageSpecification::PackageSpecification (const std::string& packageName, + const std::string& packageVersion) + : _packageName (packageName) , _operator (Equals), _version (packageVersion) +{ +} + const std::string& PackageSpecification::packageName () const { return _packageName; } +const PackageSpecification::_operators +PackageSpecification::op() const +{ + return _operator; +} + +const std::string& +PackageSpecification::version() const +{ + return _version; +} + void PackageSpecification::setOperator (_operators anOperator) { diff --git a/PackageSpecification.h b/PackageSpecification.h index 4c3ed6d..b881494 100644 --- a/PackageSpecification.h +++ b/PackageSpecification.h @@ -18,7 +18,9 @@ #include <iosfwd> #include "String++.h" -class packageversion; + +class SolvableVersion; +typedef SolvableVersion packageversion; /* Describe a package - i.e. we need version 5 of apt */ @@ -27,6 +29,8 @@ class PackageSpecification public: PackageSpecification () : _packageName (), _operator(Equals) {} PackageSpecification (const std::string& packageName); + PackageSpecification (const std::string& packageName, + const std::string &packageVersion); ~PackageSpecification () {} enum _operators @@ -39,6 +43,9 @@ public: }; const std::string& packageName() const; + const _operators op() const; + const std::string& version() const; + void setOperator (_operators); void setVersion (const std::string& ); diff --git a/PickPackageLine.cc b/PickPackageLine.cc index 31103ce..2caeafe 100644 --- a/PickPackageLine.cc +++ b/PickPackageLine.cc @@ -16,7 +16,6 @@ #include "PickPackageLine.h" #include "PickView.h" #include "package_db.h" -#include "package_version.h" void PickPackageLine::paint (HDC hdc, HRGN unused, int x, int y, int col_num, int show_cat) diff --git a/PickView.cc b/PickView.cc index 2e1beda..c583eea 100644 --- a/PickView.cc +++ b/PickView.cc @@ -21,7 +21,6 @@ #include "PickPackageLine.h" #include "PickCategoryLine.h" #include "package_db.h" -#include "package_version.h" #include "dialog.h" #include "resource.h" /* For 'source' */ @@ -944,9 +943,6 @@ PickView::defaultTrust (trusts trust) { this->deftrust = trust; - packagedb db; - db.defaultTrust(trust); - // force the picker to redraw RECT r = GetClientRect (); InvalidateRect (this->GetHWND(), &r, TRUE); diff --git a/README b/README index 120049c..9be9c50 100644 --- a/README +++ b/README @@ -3,6 +3,10 @@ for the Cygwin net releases. HOW TO BUILD: ------------- + +Cygwin +------ + Setup should build out-of-the-box on any Cygwin environment that has all the required packages and their dependencies installed: @@ -10,6 +14,7 @@ required packages and their dependencies installed: - mingw64-${arch}-headers - mingw64-${arch}-gcc-g++ - mingw64-${arch}-libgcrypt + - mingw64-${arch}-libsolv - mingw64-${arch}-bzip2 - mingw64-${arch}-xz - mingw64-${arch}-zlib @@ -26,6 +31,29 @@ to make changes to the build system. - flex - bison +Fedora +------ + +Setup should also build out-of-the-box in a Fedora environment that has all the +required packages and their dependencies installed: + + - make + - libtool + - mingw${arch}-gcc-c++ + - mingw${arch}-zlib-static + - mingw${arch}-libgcrypt-static + - mingw${arch}-libgnurx-static (*) + - mingw${arch}-libsolv-static + - mingw${arch}-bzip2-static + - mingw${arch}-xz-libs-static + - mingw${arch}-winpthreads-static + - upx (optional) + +The ${arch} needs to be replaced with either "32" or "64" +depending on the target architecture to build for. + +(*) Requires 'dnf copr enable jturney/mingw-libsolv' + Build commands: 0) If building from git, obtain this project's code: diff --git a/bootstrap.sh b/bootstrap.sh index f21206d..1d388da 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -21,7 +21,7 @@ bootstrap() { cd "$srcdir" # Make sure we are running in the right directory -if [ ! -f cygpackage.cc ]; then +if [ ! -f main.cc ]; then echo "You must run this script from the directory containing it" exit 1 fi diff --git a/choose.cc b/choose.cc index a676a64..9cf3a50 100644 --- a/choose.cc +++ b/choose.cc @@ -48,7 +48,6 @@ #include "package_db.h" #include "package_meta.h" -#include "package_version.h" #include "threebar.h" #include "Generic.h" @@ -64,7 +63,6 @@ static BoolOption UpgradeAlsoOption (false, 'g', "upgrade-also", "Also upgrade i static BoolOption CleanOrphansOption (false, 'o', "delete-orphans", "Remove orphaned packages"); static BoolOption ForceCurrentOption (false, 'f', "force-current", "Select the current version for all packages"); static BoolOption PruneInstallOption (false, 'Y', "prune-install", "Prune the installation to only the requested packages"); -static BoolOption MirrorOption (false, 'm', "mirror-mode", "Skip package availability check when installing from local directory (requires local directory to be clean mirror!)"); using namespace std; @@ -79,7 +77,8 @@ static ControlAdjuster::ControlInfo ChooserControlsInfo[] = { {IDC_CHOOSE_SEARCH_LABEL, CP_LEFT, CP_TOP}, {IDC_CHOOSE_SEARCH_EDIT, CP_LEFT, CP_TOP}, {IDC_CHOOSE_KEEP, CP_RIGHT, CP_TOP}, - {IDC_CHOOSE_CURR, CP_RIGHT, CP_TOP}, + {IDC_CHOOSE_BEST, CP_RIGHT, CP_TOP}, + {IDC_CHOOSE_SYNC, CP_RIGHT, CP_TOP}, {IDC_CHOOSE_EXP, CP_RIGHT, CP_TOP}, {IDC_CHOOSE_VIEW, CP_LEFT, CP_TOP}, {IDC_LISTVIEW_POS, CP_RIGHT, CP_TOP}, @@ -91,7 +90,7 @@ static ControlAdjuster::ControlInfo ChooserControlsInfo[] = { ChooserPage::ChooserPage () : cmd_show_set (false), saved_geom (false), saw_geom_change (false), - timer_id (DEFAULT_TIMER_ID) + timer_id (DEFAULT_TIMER_ID), activated (false) { sizeProcessor.AddControlInfo (ChooserControlsInfo); @@ -153,13 +152,34 @@ ChooserPage::createListview () chooser->setViewMode (!is_new_install || UpgradeAlsoOption || hasManualSelections ? PickView::views::PackagePending : PickView::views::Category); SendMessage (GetDlgItem (IDC_CHOOSE_VIEW), CB_SETCURSEL, (WPARAM)chooser->getViewMode(), 0); - - /* FIXME: do we need to init the desired fields ? */ - static int ta[] = { IDC_CHOOSE_KEEP, IDC_CHOOSE_CURR, IDC_CHOOSE_EXP, 0 }; - rbset (GetHWND (), ta, IDC_CHOOSE_CURR); ClearBusy (); } +void +ChooserPage::initialUpdateState() +{ + // set the initial update state + if (ForceCurrentOption) + { + update_mode_id = IDC_CHOOSE_SYNC; + changeTrust(update_mode_id, false, true); + } + else if (hasManualSelections && !UpgradeAlsoOption) + { + // if packages are added or removed on the command-line and --upgrade-also + // isn't used, we keep the current versions of everything else + update_mode_id = IDC_CHOOSE_KEEP; + } + else + { + update_mode_id = IDC_CHOOSE_BEST; + changeTrust (update_mode_id, false, true); + } + + static int ta[] = { IDC_CHOOSE_KEEP, IDC_CHOOSE_BEST, IDC_CHOOSE_SYNC, 0 }; + rbset (GetHWND (), ta, update_mode_id); +} + /* TODO: review ::overrides for possible consolidation */ void ChooserPage::getParentRect (HWND parent, HWND child, RECT * r) @@ -248,14 +268,30 @@ ChooserPage::OnInit () SendMessage(viewlist, CB_ADDSTRING, 0, (LPARAM)PickView::mode_caption((PickView::views)view)); } - SetBusy (); - if (source == IDC_SOURCE_DOWNLOAD || source == IDC_SOURCE_LOCALDIR) - packagemeta::ScanDownloadedFiles (MirrorOption); + if (source == IDC_SOURCE_DOWNLOAD) + setPrompt("Select packages to download "); + else + setPrompt("Select packages to install "); - packagedb db; - db.setExistence (); - db.fillMissingCategory (); + createListview (); + + AddTooltip (IDC_CHOOSE_KEEP, IDS_TRUSTKEEP_TOOLTIP); + AddTooltip (IDC_CHOOSE_BEST, IDS_TRUSTCURR_TOOLTIP); + AddTooltip (IDC_CHOOSE_SYNC, IDS_TRUSTSYNC_TOOLTIP); + AddTooltip (IDC_CHOOSE_EXP, IDS_TRUSTEXP_TOOLTIP); + AddTooltip (IDC_CHOOSE_VIEW, IDS_VIEWBUTTON_TOOLTIP); + AddTooltip (IDC_CHOOSE_HIDE, IDS_HIDEOBS_TOOLTIP); + AddTooltip (IDC_CHOOSE_SEARCH_EDIT, IDS_SEARCH_TOOLTIP); + /* Set focus to search edittext control. */ + PostMessage (GetHWND (), WM_NEXTDLGCTL, + (WPARAM) GetDlgItem (IDC_CHOOSE_SEARCH_EDIT), TRUE); +} + +void +ChooserPage::applyCommandLinePackageSelection() +{ + packagedb db; for (packagedb::packagecollection::iterator i = db.packages.begin (); i != db.packages.end (); ++i) { @@ -264,8 +300,7 @@ ChooserPage::OnInit () bool deleted = pkg.isManuallyDeleted(); bool basemisc = (pkg.categories.find ("Base") != pkg.categories.end () || pkg.categories.find ("Orphaned") != pkg.categories.end ()); - bool upgrade = wanted || (!pkg.installed && basemisc) - || UpgradeAlsoOption || !hasManualSelections; + bool upgrade = wanted || (!pkg.installed && basemisc); bool install = wanted && !deleted && !pkg.installed; bool reinstall = (wanted || basemisc) && deleted; bool uninstall = (!(wanted || basemisc) && (deleted || PruneInstallOption)) @@ -276,38 +311,36 @@ ChooserPage::OnInit () pkg.set_action (packagemeta::Reinstall_action, pkg.curr); else if (uninstall) pkg.set_action (packagemeta::Uninstall_action, packageversion ()); - else if (PruneInstallOption || ForceCurrentOption) + else if (PruneInstallOption) pkg.set_action (packagemeta::Default_action, pkg.curr); else if (upgrade) pkg.set_action (packagemeta::Default_action, pkg.trustp(true, TRUST_UNKNOWN)); else pkg.set_action (packagemeta::Default_action, pkg.installed); } - - ClearBusy (); - - if (source == IDC_SOURCE_DOWNLOAD) - setPrompt("Select packages to download "); - else - setPrompt("Select packages to install "); - createListview (); - - AddTooltip (IDC_CHOOSE_KEEP, IDS_TRUSTKEEP_TOOLTIP); - AddTooltip (IDC_CHOOSE_CURR, IDS_TRUSTCURR_TOOLTIP); - AddTooltip (IDC_CHOOSE_EXP, IDS_TRUSTEXP_TOOLTIP); - AddTooltip (IDC_CHOOSE_VIEW, IDS_VIEWBUTTON_TOOLTIP); - AddTooltip (IDC_CHOOSE_HIDE, IDS_HIDEOBS_TOOLTIP); - AddTooltip (IDC_CHOOSE_SEARCH_EDIT, IDS_SEARCH_TOOLTIP); - - /* Set focus to search edittext control. */ - PostMessage (GetHWND (), WM_NEXTDLGCTL, - (WPARAM) GetDlgItem (IDC_CHOOSE_SEARCH_EDIT), TRUE); } void ChooserPage::OnActivate() { - chooser->refresh();; + SetBusy(); + + packagedb db; + db.prep(); + + if (!activated) + { + // Do things which should only happen once, but rely on packagedb being + // ready to use, so OnInit() is too early + applyCommandLinePackageSelection(); + initialUpdateState(); + + activated = true; + } + + ClearBusy(); + + chooser->refresh(); PlaceDialog (true); } @@ -360,6 +393,7 @@ ChooserPage::OnBack () void ChooserPage::keepClicked() { + update_mode_id = IDC_CHOOSE_KEEP; packagedb db; for (packagedb::packagecollection::iterator i = db.packages.begin (); i != db.packages.end (); ++i) @@ -372,12 +406,57 @@ ChooserPage::keepClicked() } void -ChooserPage::changeTrust(trusts aTrust) +ChooserPage::changeTrust(int button, bool test, bool initial) { SetBusy (); - chooser->defaultTrust (aTrust); + + update_mode_id = button; + + SolverSolution::updateMode mode; + switch (button) + { + default: + case IDC_CHOOSE_KEEP: + mode = SolverSolution::keep; + break; + + case IDC_CHOOSE_BEST: + mode = SolverSolution::updateBest; + break; + + case IDC_CHOOSE_SYNC: + mode = SolverSolution::updateForce; + break; + } + + packagedb db; + SolverTasks q; + + // usually we want to apply the solver to an empty task list to get the list + // of packages to upgrade (if any) + if (initial) + { + // but initially we want a task list with any package changes caused by + // command line options + // (also note the installed version to avoid generating spurious taskKeep + // or taskSkip tasks) + for (packagedb::packagecollection::iterator p = db.packages.begin (); + p != db.packages.end (); ++p) + { + packagemeta *pkg = p->second; + pkg->default_version = pkg->installed; + } + q.setTasks(); + } + db.defaultTrust(q, mode, test); + + // configure PickView so 'test' or 'curr' version is chosen when an + // uninstalled package is first clicked on. + chooser->defaultTrust (test ? TRUST_TEST : TRUST_CURR); + chooser->refresh(); - PrereqChecker::setTrust (aTrust); + + PrereqChecker::setTestPackages(test); ClearBusy (); } @@ -444,14 +523,18 @@ ChooserPage::OnMessageCmd (int id, HWND hwndctl, UINT code) keepClicked(); break; - case IDC_CHOOSE_CURR: + case IDC_CHOOSE_BEST: if (IsButtonChecked (id)) - changeTrust (TRUST_CURR); + changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false); break; - case IDC_CHOOSE_EXP: + case IDC_CHOOSE_SYNC: if (IsButtonChecked (id)) - changeTrust (TRUST_TEST); + changeTrust (id, IsButtonChecked(IDC_CHOOSE_EXP), false); + break; + + case IDC_CHOOSE_EXP: + changeTrust(update_mode_id, IsButtonChecked (id), false); break; case IDC_CHOOSE_HIDE: diff --git a/choose.h b/choose.h index 46f0f35..32a1650 100644 --- a/choose.h +++ b/choose.h @@ -54,11 +54,13 @@ private: RECT getDefaultListViewSize(); void getParentRect (HWND parent, HWND child, RECT * r); void keepClicked(); - void changeTrust(trusts aTrust); + void changeTrust(int button, bool test, bool initial); void logOnePackageResult(packagemeta const *aPkg); void logResults(); void setPrompt(char const *aPrompt); void PlaceDialog (bool); + void applyCommandLinePackageSelection(); + void initialUpdateState(); PickView *chooser; static HWND ins_dialog; @@ -73,10 +75,8 @@ private: WINDOWPLACEMENT wp; UINT wpi[sizeof (WINDOWPLACEMENT) / sizeof (UINT)]; }; - - - - + int update_mode_id; + bool activated; }; #endif /* SETUP_CHOOSE_H */ diff --git a/configure.ac b/configure.ac index 2c610ed..dcab4ee 100644 --- a/configure.ac +++ b/configure.ac @@ -57,6 +57,8 @@ AC_CHECK_HEADER(zlib.h, , missing_deps="$missing_deps zlib") AC_CHECK_HEADER(bzlib.h, , missing_deps="$missing_deps libbz2") AC_CHECK_HEADER(lzma.h, , missing_deps="$missing_deps liblzma") AC_CHECK_HEADER(gcrypt.h, , missing_deps="$missing_deps libgcrypt") +AC_CHECK_HEADER(solv/pool.h, , missing_deps="$missing_deps libsolv") +AC_CHECK_HEADER(regex.h, , missing_deps="$missing_deps libregex") if test -n "$missing_deps"; then AC_MSG_ERROR([missing prerequisites: $missing_deps]) diff --git a/confirm.cc b/confirm.cc new file mode 100644 index 0000000..7e949d8 --- /dev/null +++ b/confirm.cc @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2018 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + */ + +#include "confirm.h" +#include "threebar.h" +#include "resource.h" +#include "state.h" +#include "ControlAdjuster.h" +#include "package_db.h" +#include "package_meta.h" + +extern ThreeBarProgressPage Progress; + +// Sizing information. +static ControlAdjuster::ControlInfo ConfirmControlsInfo[] = { + {IDC_CONFIRM_EDIT, CP_STRETCH, CP_STRETCH}, + {0, CP_LEFT, CP_TOP} +}; + +// --------------------------------------------------------------------------- +// implements class ConfirmPage +// --------------------------------------------------------------------------- + +ConfirmPage::ConfirmPage () +{ + sizeProcessor.AddControlInfo (ConfirmControlsInfo); +} + +bool +ConfirmPage::Create () +{ + return PropertyPage::Create (IDD_CONFIRM); +} + +void +ConfirmPage::OnInit () +{ + // set the edit-area to a larger font + SetDlgItemFont(IDC_CONFIRM_EDIT, "MS Shell Dlg", 10); +} + +void +ConfirmPage::OnActivate() +{ + // generate a report on actions we're going to take + std::string s = ""; + + packagedb db; + const SolverTransactionList & trans = db.solution.transactions (); + + // first list things we will erase + if (source != IDC_SOURCE_DOWNLOAD) + { + for (SolverTransactionList::const_iterator i = trans.begin (); + i != trans.end (); i++) + { + if (i->type == SolverTransaction::transErase) + { + packageversion pv = i->version; + packagemeta *pkg = db.findBinary (PackageSpecification (pv.Name ())); + + s += "Uninstall "; + s += i->version.Name(); + s += " "; + s += i->version.Canonical_version(); + if (pkg->desired) + s += " (automatically added)"; + s += "\r\n"; + } + } + } + + // then list things downloaded or installed + for (SolverTransactionList::const_iterator i = trans.begin (); + i != trans.end (); i++) + { + packageversion pv = i->version; + packagemeta *pkg = db.findBinary (PackageSpecification (pv.Name ())); + + if (i->type == SolverTransaction::transInstall) + { + if (source != IDC_SOURCE_DOWNLOAD) + s += "Install "; + else + s += "Download "; + s += i->version.Name(); + s += " "; + s += i->version.Canonical_version(); + if (!pkg->desired) + s += " (automatically added)"; + s += "\r\n"; + } + } + + // be explicit about doing nothing + if (s.empty()) + s += "No changes"; + + SetDlgItemText (GetHWND (), IDC_CONFIRM_EDIT, s.c_str ()); + + // move focus to 'next' button, so enter doesn't get eaten by edit control + HWND nextButton = ::GetDlgItem(::GetParent(GetHWND()), 0x3024 /* ID_WIZNEXT */); + PostMessage (GetHWND (), WM_NEXTDLGCTL, (WPARAM)nextButton, TRUE); +} + +long +ConfirmPage::OnNext () +{ + return whatNext(); +} + +long +ConfirmPage::whatNext () +{ + if (source == IDC_SOURCE_LOCALDIR) + { + // Next, install + Progress.SetActivateTask (WM_APP_START_INSTALL); + } + else + { + // Next, start download from internet + Progress.SetActivateTask (WM_APP_START_DOWNLOAD); + } + return IDD_INSTATUS; +} + +long +ConfirmPage::OnBack () +{ + return IDD_CHOOSE; +} + +long +ConfirmPage::OnUnattended () +{ + return whatNext(); +} diff --git a/confirm.h b/confirm.h new file mode 100644 index 0000000..fe11ee0 --- /dev/null +++ b/confirm.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + */ + +#ifndef SETUP_CONFIRM_H +#define SETUP_CONFIRM_H + +#include "proppage.h" + +class ConfirmPage:public PropertyPage +{ +public: + ConfirmPage (); + virtual ~ConfirmPage () { }; + bool Create (); + virtual void OnInit (); + virtual void OnActivate (); + virtual long OnNext (); + virtual long OnBack (); + virtual long OnUnattended (); +private: + long whatNext (); +}; + +#endif /* SETUP_CONFIRM_H */ diff --git a/cygpackage.cc b/cygpackage.cc deleted file mode 100644 index 32b9403..0000000 --- a/cygpackage.cc +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2001, Robert Collins. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * A copy of the GNU General Public License can be found at - * http://www.gnu.org/ - * - * Written by Robert Collins <rbtcollins@hotmail.com> - * - */ - -/* this is the parent class for all package operations. - */ - -#include "cygpackage.h" -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#include "io_stream.h" -#include "compress.h" - -#include "package_version.h" -#include "cygpackage.h" -#include "LogSingleton.h" - -/* this constructor creates an invalid package - further details MUST be provided */ -cygpackage::cygpackage (): -name (), -vendor (), -packagev (), -canonical (), -sdesc (), -ldesc (), -type (package_binary) -{ - /* FIXME: query the install database for the currently installed - * version details - */ -} - -packageversion -cygpackage::createInstance (const std::string& pkgname, - const package_type_t type) -{ - cygpackage *temp = new cygpackage; - temp->name = pkgname; - temp->type = type; - return packageversion(temp); -} - -packageversion -cygpackage::createInstance (const std::string& pkgname, - const std::string& version, - package_type_t const newtype) -{ - cygpackage *temp = new cygpackage; - temp->name = pkgname; - temp->type = newtype; - temp->setCanonicalVersion (version); - return packageversion(temp); -} - -/* tell the version */ -void -cygpackage::setCanonicalVersion (const std::string& version) -{ - canonical = version; - - const char *start = canonical.c_str(); - const char *curr = strchr(start, '-'); - - if (curr) - { - const char *next; - while ((next = strchr (curr + 1, '-'))) - curr = next; - - /* package version appears after the last '-' in the version string */ - packagev = curr + 1; - /* vendor version is everything up to that last '-' */ - vendor.assign(canonical.c_str(), (size_t)(curr - start)); - } - else - { - // FIXME: What's up with the "0"? It's probably a mistake, and should be - // "". It used to be written as 0, and was subject to a bizarre implicit - // conversion by the unwise String(int) constructor. - packagev = "0"; - vendor = version; - } -} - -cygpackage::~cygpackage () -{ -} - -const std::string -cygpackage::Name () -{ - return name; -} - -const std::string -cygpackage::Vendor_version () -{ - return vendor; -} - -const std::string -cygpackage::Package_version () -{ - return packagev; -} - -std::string const -cygpackage::Canonical_version () -{ - return canonical; -} - -void -cygpackage::set_sdesc (const std::string& desc) -{ - sdesc = desc; -} - -void -cygpackage::set_ldesc (const std::string& desc) -{ - ldesc = desc; -} - -#if 0 -package_stability_t cygpackage::Stability () -{ - return stability; -} -#endif diff --git a/cygpackage.h b/cygpackage.h deleted file mode 100644 index c6d0657..0000000 --- a/cygpackage.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2001, Robert Collins. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * A copy of the GNU General Public License can be found at - * http://www.gnu.org/ - * - * Written by Robert Collins <rbtcollins@hotmail.com> - * - */ - -#ifndef SETUP_CYGPACKAGE_H -#define SETUP_CYGPACKAGE_H - -/* This is a cygwin specific package class, that should be able to - * arbitrate acceess to cygwin binary packages amd cygwin source packages - */ - -#include "package_version.h" - -class io_stream; - -class cygpackage:public _packageversion -{ -public: - virtual const std::string Name (); - virtual const std::string Vendor_version (); - virtual const std::string Package_version (); - virtual const std::string Canonical_version (); - virtual package_type_t Type () - { - return type; - }; - virtual void set_sdesc (const std::string& ); - virtual void set_ldesc (const std::string& ); - virtual const std::string SDesc () - { - return sdesc; - }; - virtual const std::string LDesc () - { - return ldesc; - }; - - /* pass the name of the package when constructing */ - void setCanonicalVersion (const std::string& ); - - virtual ~ cygpackage (); - - /* pass the name of the package when constructing */ - static packageversion createInstance (const std::string& pkgname, - const package_type_t type); - - static packageversion createInstance (const std::string& pkgname, - const std::string& version, - package_type_t const); - -private: - cygpackage (); - std::string name; - std::string vendor; - std::string packagev; - std::string canonical; - std::string sdesc, ldesc; - -// package_stability_t stability; - package_type_t type; -}; - -#endif /* SETUP_CYGPACKAGE_H */ diff --git a/desktop.cc b/desktop.cc index 24908f8..927c02f 100644 --- a/desktop.cc +++ b/desktop.cc @@ -35,7 +35,6 @@ #include "mklink2.h" #include "package_db.h" #include "package_meta.h" -#include "package_version.h" #include "filemanip.h" #include "io_stream.h" #include "getopt++/BoolOption.h" diff --git a/download.cc b/download.cc index 129b226..0cb3352 100644 --- a/download.cc +++ b/download.cc @@ -39,21 +39,16 @@ #include "package_db.h" #include "package_meta.h" -#include "package_version.h" #include "package_source.h" #include "threebar.h" #include "Exception.h" -#include "getopt++/BoolOption.h" - using namespace std; extern ThreeBarProgressPage Progress; -BoolOption IncludeSource (false, 'I', "include-source", "Automatically install source for every package installed"); - // Return true if selected checks pass, false if they don't and the // user chooses to delete the file; otherwise throw an exception. static bool @@ -283,59 +278,42 @@ do_download_thread (HINSTANCE h, HWND owner) Progress.SetText3 (""); packagedb db; - /* calculate the amount needed */ - for (packagedb::packagecollection::iterator i = db.packages.begin (); - i != db.packages.end (); ++i) + const SolverTransactionList &t = db.solution.transactions(); + + /* calculate the total size of the download */ + for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i) { - packagemeta & pkg = *(i->second); - if (pkg.desired && (pkg.picked () || pkg.srcpicked ())) - { - packageversion version = pkg.desired; - packageversion sourceversion = version.sourcePackage(); - try - { - if (pkg.picked()) - { - if (!check_for_cached (*version.source(), owner)) - total_download_bytes += version.source()->size; - } - if (pkg.srcpicked () || IncludeSource) - { - if (!check_for_cached (*sourceversion.source(), owner)) - total_download_bytes += sourceversion.source()->size; - } - } - catch (Exception * e) - { - // We know what to do with these.. - if (e->errNo() == APPERR_CORRUPT_PACKAGE) - fatal (owner, IDS_CORRUPT_PACKAGE, pkg.name.c_str()); - // Unexpected exception. - throw e; - } - } + if (i->type != SolverTransaction::transInstall) + continue; + packageversion version = i->version; + + try + { + if (!check_for_cached (*version.source(), owner)) + total_download_bytes += version.source()->size; + } + catch (Exception * e) + { + // We know what to do with these.. + if (e->errNo() == APPERR_CORRUPT_PACKAGE) + fatal (owner, IDS_CORRUPT_PACKAGE, version.Name().c_str()); + // Unexpected exception. + throw e; + } } /* and do the download. FIXME: This here we assign a new name for the cached version * and check that above. */ - for (packagedb::packagecollection::iterator i = db.packages.begin (); - i != db.packages.end (); ++i) + for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i) { - packagemeta & pkg = *(i->second); - if (pkg.desired && (pkg.picked () || pkg.srcpicked ())) + if (i->type != SolverTransaction::transInstall) + continue; + packageversion version = i->version; + { int e = 0; - packageversion version = pkg.desired; - packageversion sourceversion = version.sourcePackage(); - if (pkg.picked()) - { - e += download_one (*version.source(), owner); - } - if (sourceversion && (pkg.srcpicked() || IncludeSource)) - { - e += download_one (*sourceversion.source (), owner); - } + e += download_one (*version.source(), owner); errors += e; if (e) download_failures.push_back (version); diff --git a/filemanip.cc b/filemanip.cc index d1d27be..265a2a7 100644 --- a/filemanip.cc +++ b/filemanip.cc @@ -43,22 +43,6 @@ get_file_size (const std::string& name) return rv; } -std::string -base (const std::string& aString) -{ - if (!aString.size()) - return ""; - const char *s = aString.c_str(); - std::string rv = s; - while (*s) - { - if ((*s == '/' || *s == ':' || *s == '\\') && s[1]) - rv = s + 1; - s++; - } - return rv; -} - /* returns the number of characters of path that * precede the extension */ @@ -89,7 +73,7 @@ parse_filename (const string &fn, fileparse & f) f.tail = fn.substr (n, string::npos); - p = new_cstr_char_array (base (fn.substr (0, n))); + p = new_cstr_char_array (fn.substr (0, n)); char const *ext; /* TODO: make const and non-const trail variant. */ if ((ext = trail (p, "-src"))) diff --git a/filemanip.h b/filemanip.h index 5594519..451211f 100644 --- a/filemanip.h +++ b/filemanip.h @@ -30,7 +30,6 @@ struct fileparse }; int parse_filename (const std::string& fn, fileparse & f); -std::string base (const std::string& ); size_t get_file_size (const std::string& ); std::string backslash (const std::string& s); const char * trail (const char *, const char *); diff --git a/ini.cc b/ini.cc index 87443a8..d807ed6 100644 --- a/ini.cc +++ b/ini.cc @@ -347,16 +347,16 @@ do_remote_ini (HWND owner) static bool do_ini_thread (HINSTANCE h, HWND owner) { + packagedb db; + db.init(); + bool ini_error = true; + if (source == IDC_SOURCE_LOCALDIR) ini_error = do_local_ini (owner); else ini_error = do_remote_ini (owner); - packagedb db; - db.upgrade(); - db.removeEmptyCategories(); - if (ini_error) return false; diff --git a/inilex.ll b/inilex.ll index 13422b1..95888cf 100644 --- a/inilex.ll +++ b/inilex.ll @@ -118,10 +118,13 @@ B64 [a-zA-Z0-9_-] "message:" return MESSAGE; "Source:" return SOURCEPACKAGE; "Build-Depends:" return BUILDDEPENDS; +"replace-versions:" return REPLACE_VERSIONS; "category:"|"Section:" return CATEGORY; "requires:" return REQUIRES; [dD]"epends:" return DEPENDS; +[dD]"epends2:" return DEPENDS; +[oO]"bsoletes:" return OBSOLETES; ^{STR}":" ignore_line (); diff --git a/iniparse.yy b/iniparse.yy index 18ebe2a..e5c514b 100644 --- a/iniparse.yy +++ b/iniparse.yy @@ -37,6 +37,7 @@ extern int yylineno; %token STRING %token SETUP_TIMESTAMP SETUP_VERSION PACKAGEVERSION INSTALL SOURCE SDESC LDESC +%token REPLACE_VERSIONS %token CATEGORY DEPENDS REQUIRES %token T_PREV T_CURR T_TEST T_OTHER %token MD5 SHA512 @@ -45,6 +46,7 @@ extern int yylineno; %token COMMA NL AT %token OPENBRACE CLOSEBRACE EQUAL GT LT GTEQUAL LTEQUAL %token BUILDDEPENDS +%token OBSOLETES %token MESSAGE %token ARCH RELEASE @@ -103,6 +105,9 @@ singleitem /* non-empty */ | DEPENDS { iniBuilder->buildBeginDepends(); } versionedpackagelist NL | REQUIRES { iniBuilder->buildBeginDepends(); } versionedpackagelistsp NL | BUILDDEPENDS { iniBuilder->buildBeginBuildDepends(); } versionedpackagelist NL + | OBSOLETES { iniBuilder->buildBeginObsoletes(); } versionedpackagelist NL + | REPLACE_VERSIONS versionlist NL + | MESSAGE STRING STRING NL { iniBuilder->buildMessage ($2, $3); } | error NL { yyerror (std::string("unrecognized line ") + stringify(yylineno) @@ -153,4 +158,8 @@ operator /* non-empty */ | GTEQUAL { iniBuilder->buildPackageListOperator (PackageSpecification::MoreThanEquals); } ; +versionlist: /* empty */ + | versionlist STRING { iniBuilder->buildPackageReplaceVersionsList ($2); } + ; + %% diff --git a/install.cc b/install.cc index 366aeb2..3dd491e 100644 --- a/install.cc +++ b/install.cc @@ -68,7 +68,6 @@ static long long int total_bytes = 0; static long long int total_bytes_sofar = 0; static int package_bytes = 0; -extern BoolOption IncludeSource; static BoolOption NoReplaceOnReboot (false, 'r', "no-replaceonreboot", "Disable replacing in-use files on next " "reboot."); @@ -815,26 +814,21 @@ do_install_thread (HINSTANCE h, HWND owner) /* Writes Cygwin/setup/rootdir registry value */ create_install_root (); - vector <packagemeta *> install_q, uninstall_q, sourceinstall_q; + vector <packageversion> install_q, uninstall_q, sourceinstall_q; packagedb db; + const SolverTransactionList &t = db.solution.transactions(); /* Calculate the amount of data to md5sum */ Progress.SetText1("Calculating..."); long long int md5sum_total_bytes = 0; - for (packagedb::packagecollection::iterator i = db.packages.begin (); - i != db.packages.end (); ++i) + for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i) { - packagemeta & pkg = *(i->second); + packageversion version = i->version; - if (pkg.picked()) + if (i->type == SolverTransaction::transInstall) { - md5sum_total_bytes += pkg.desired.source()->size; - } - - if (pkg.srcpicked() || IncludeSource) - { - md5sum_total_bytes += pkg.desired.sourcePackage ().source()->size; + md5sum_total_bytes += version.source()->size; } } @@ -844,58 +838,37 @@ do_install_thread (HINSTANCE h, HWND owner) net install, the hashes will have already been verified at download time, and all calls to check_hash() below should instantly return. */ long long int md5sum_total_bytes_sofar = 0; - for (packagedb::packagecollection::iterator i = db.packages.begin (); - i != db.packages.end (); ++i) + for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i) { - packagemeta & pkg = *(i->second); + packageversion version = i->version; - if (pkg.picked()) + if (i->type == SolverTransaction::transInstall) { try { - (*pkg.desired.source ()).check_hash (); + (*version.source ()).check_hash (); } catch (Exception *e) { - if (yesno (owner, IDS_SKIP_PACKAGE, e->what()) == IDYES) - pkg.pick (false); + yesno (owner, IDS_SKIP_PACKAGE, e->what()); } - if (pkg.picked()) { - md5sum_total_bytes_sofar += pkg.desired.source()->size; - total_bytes += pkg.desired.source()->size; - install_q.push_back (&pkg); + md5sum_total_bytes_sofar += version.source()->size; + total_bytes += version.source()->size; + + // source packages are kept in a separate queue as they are installed + // differently: root is /usr/src, install isn't recorded, etc. + if (version.Type() == package_source) + sourceinstall_q.push_back (version); + else + install_q.push_back (version); } } - if (pkg.srcpicked() || IncludeSource) + /* Uninstall, upgrade or reinstall */ + if (i->type == SolverTransaction::transErase) { - bool skiprequested = false ; - try - { - (*pkg.desired.sourcePackage ().source ()).check_hash (); - } - catch (Exception *e) - { - if (yesno (owner, IDS_SKIP_PACKAGE, e->what()) == IDYES) - { - skiprequested = true ; //(err occurred,) skip pkg desired - pkg.srcpick (false); - } - } - if (pkg.srcpicked() || (IncludeSource && !skiprequested)) - { - md5sum_total_bytes_sofar += pkg.desired.sourcePackage ().source()->size; - total_bytes += pkg.desired.sourcePackage ().source()->size; - sourceinstall_q.push_back (&pkg); - } - } - - /* Upgrade or reinstall */ - if ((pkg.installed && pkg.desired != pkg.installed) - || pkg.picked ()) - { - uninstall_q.push_back (&pkg); + uninstall_q.push_back (version); } if (md5sum_total_bytes > 0) @@ -904,27 +877,31 @@ do_install_thread (HINSTANCE h, HWND owner) /* start with uninstalls - remove files that new packages may replace */ Progress.SetBar2(0); - for (vector <packagemeta *>::iterator i = uninstall_q.begin (); + for (vector <packageversion>::iterator i = uninstall_q.begin (); i != uninstall_q.end (); ++i) { - myInstaller.preremoveOne (**i); + packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name())); + myInstaller.preremoveOne (*pkgm); Progress.SetBar2(std::distance(uninstall_q.begin(), i) + 1, uninstall_q.size()); } Progress.SetBar2(0); - for (vector <packagemeta *>::iterator i = uninstall_q.begin (); + for (vector <packageversion>::iterator i = uninstall_q.begin (); i != uninstall_q.end (); ++i) { - myInstaller.uninstallOne (**i); + packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name())); + myInstaller.uninstallOne (*pkgm); Progress.SetBar2(std::distance(uninstall_q.begin(), i) + 1, uninstall_q.size()); } - for (vector <packagemeta *>::iterator i = install_q.begin (); + for (vector <packageversion>::iterator i = install_q.begin (); i != install_q.end (); ++i) { - packagemeta & pkg = **i; + packageversion & pkg = *i; + packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name())); + try { - myInstaller.installOne (pkg, pkg.desired, *pkg.desired.source(), + myInstaller.installOne (*pkgm, pkg, *pkg.source(), "cygfile://", "/", owner); } catch (exception *e) @@ -939,12 +916,12 @@ do_install_thread (HINSTANCE h, HWND owner) } } - for (vector <packagemeta *>::iterator i = sourceinstall_q.begin (); + for (vector <packageversion>::iterator i = sourceinstall_q.begin (); i != sourceinstall_q.end (); ++i) { - packagemeta & pkg = **i; - myInstaller.installOne (pkg, pkg.desired.sourcePackage(), - *pkg.desired.sourcePackage().source(), + packagemeta *pkgm = db.findSource (PackageSpecification(i->Name())); + packageversion & pkg = *i; + myInstaller.installOne (*pkgm, pkg, *pkg.source(), "cygfile://", "/usr/src/", owner); } diff --git a/libsolv.cc b/libsolv.cc new file mode 100644 index 0000000..135d9af --- /dev/null +++ b/libsolv.cc @@ -0,0 +1,1018 @@ +/* + * Copyright (c) 2017 Jon Turney + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + */ + +#include "libsolv.h" +#include "package_db.h" +#include "package_meta.h" + +#include "solv/solver.h" +#include "solv/solverdebug.h" +#include "solv/evr.h" + +#include "LogSingleton.h" +#include <iomanip> + +// --------------------------------------------------------------------------- +// Utility functions for mapping between Operators and Relation Ids +// --------------------------------------------------------------------------- + +static Id +Operator2RelId(PackageSpecification::_operators op) +{ + switch (op) + { + case PackageSpecification::Equals: + return REL_EQ; + case PackageSpecification::LessThan: + return REL_LT; + case PackageSpecification::MoreThan: + return REL_GT; + case PackageSpecification::LessThanEquals: + return REL_LT | REL_EQ; + case PackageSpecification::MoreThanEquals: + return REL_GT | REL_EQ; + } + + return 0; +} + +static PackageSpecification::_operators +RelId2Operator(Id id) +{ + switch (id) + { + case REL_EQ: + return PackageSpecification::Equals; + case REL_LT: + return PackageSpecification::LessThan; + case REL_GT: + return PackageSpecification::MoreThan; + case REL_LT | REL_EQ: + return PackageSpecification::LessThanEquals; + case REL_GT | REL_EQ: + return PackageSpecification::MoreThanEquals; + } + + return PackageSpecification::Equals; +} + +// --------------------------------------------------------------------------- +// implements class SolvableVersion +// +// a wrapper around a libsolv Solvable +// --------------------------------------------------------------------------- + +Id +SolvableVersion::name_id () const +{ + Solvable *solvable = pool_id2solvable(pool, id); + return solvable->name; +} + +const std::string +SolvableVersion::Name () const +{ + if (!id) + return ""; + return pool_id2str(pool, name_id()); +} + +const std::string +SolvableVersion::Canonical_version() const +{ + if (!id) + return ""; + Solvable *solvable = pool_id2solvable(pool, id); + return std::string(pool_id2str(pool, solvable->evr)); +} + +package_type_t +SolvableVersion::Type () const +{ + if (!id) + return package_binary; + Solvable *solvable = pool_id2solvable(pool, id); + if (solvable->arch == ARCH_SRC) + return package_source; + else + return package_binary; +} + +const PackageDepends +SolvableVersion::depends() const +{ + return deplist(SOLVABLE_REQUIRES); +} + +const PackageDepends +SolvableVersion::obsoletes() const +{ + return deplist(SOLVABLE_OBSOLETES); +} + +// helper function which returns the deplist for a given key, as a PackageDepends +const PackageDepends +SolvableVersion::deplist(Id keyname) const +{ + static PackageDepends empty_package; + if (!id) + return empty_package; + Solvable *solvable = pool_id2solvable(pool, id); + + Queue q; + queue_init(&q); + + if (repo_lookup_idarray(solvable->repo, id, keyname, &q)) + { + // convert + PackageDepends dep; + + for (int i = 0; i < q.count; i++) + { +#ifdef DEBUG + Log (LOG_PLAIN) << "dep " << std::hex << q.elements[i] << ": " << pool_dep2str(pool, q.elements[i]) << endLog; +#endif + + const char *name = pool_id2str(pool, q.elements[i]); + PackageSpecification *spec = new PackageSpecification (name); + + if (ISRELDEP(id)) + { + Reldep *rd = GETRELDEP(pool, id); + spec->setOperator(RelId2Operator(rd->flags)); + spec->setVersion(pool_id2str(pool, rd->evr)); + } + + dep.push_back (spec); + } + + queue_empty(&q); + + return dep; + } + + // otherwise, return an empty depends list + return empty_package; +} + +const std::string +SolvableVersion::SDesc () const +{ + if (!id) + return ""; + Solvable *solvable = pool_id2solvable(pool, id); + const char *sdesc = repo_lookup_str(solvable->repo, id, SOLVABLE_SUMMARY); + + if (!sdesc) + return ""; + + return sdesc; +} + +const std::string +SolvableVersion::sourcePackageName () const +{ + if (!id) + return ""; + + // extract source package name + Solvable *solvable = pool_id2solvable(pool, id); + Id spkg = repo_lookup_id(solvable->repo, id, SOLVABLE_SOURCENAME); + + // has no such attribute + if (!spkg) + return ""; + + return std::string(pool_id2str(pool, spkg)); +} + +SolvableVersion +SolvableVersion::sourcePackage () const +{ + if (!id) + return SolvableVersion(); + + // extract source package id + Solvable *solvable = pool_id2solvable(pool, id); + Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1); + Id spkg_id = repo_lookup_id(solvable->repo, id, spkg_attr); + + // has no such attribute + if (!spkg_id) + return SolvableVersion(); + + return SolvableVersion(spkg_id, pool); +} + +void +SolvableVersion::fixup_spkg_id (SolvableVersion spkg_id) const +{ + if (!id) + return; + Solvable *solvable = pool_id2solvable(pool, id); + Repodata *data = repo_last_repodata(solvable->repo); + Id handle = id; + Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1); + repodata_set_id(data, handle, spkg_attr, spkg_id.id); +} + +packagesource * +SolvableVersion::source() const +{ + static packagesource empty_source = packagesource(); + if (!id) { + return &empty_source; + } + + Solvable *solvable = pool_id2solvable(pool, id); + Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1); + return (packagesource *)repo_lookup_num(solvable->repo, id, psrc_attr, (unsigned long long)&empty_source); +} + +bool +SolvableVersion::accessible () const +{ + // The 'accessible' check used to test if an archive was available locally or + // from a mirror. + // + // This seems utterly pointless. as binary packages which aren't 'accessible' + // never get to appear in the package list. + // + // For source packages, it's equivalent to the bool conversion operator.) + // + if (id != 0) + return TRUE; + else + return FALSE; +} + +package_stability_t +SolvableVersion::Stability () const +{ + if (!id) + return TRUST_UNKNOWN; + Solvable *solvable = pool_id2solvable(pool, id); + Id stability_attr = pool_str2id(pool, "solvable:stability", 1); + return (package_stability_t)repo_lookup_num(solvable->repo, id, stability_attr, TRUST_UNKNOWN); +} + +bool +SolvableVersion::operator <(SolvableVersion const &rhs) const +{ + return (compareVersions(*this, rhs) < 0); +} + +bool +SolvableVersion::operator ==(SolvableVersion const &rhs) const +{ + return (compareVersions(*this, rhs) == 0); +} + +bool +SolvableVersion::operator !=(SolvableVersion const &rhs) const +{ + return (compareVersions(*this, rhs) != 0); +} + +int +SolvableVersion::compareVersions(const SolvableVersion &a, + const SolvableVersion &b) +{ + if (a.id == b.id) + return 0; + + // if a and b are different, at least one of them has a pool + Pool *pool = a.pool ? a.pool : b.pool; + + Solvable *sa = a.id ? pool_id2solvable(a.pool, a.id) : NULL; + Solvable *sb = b.id ? pool_id2solvable(b.pool, b.id) : NULL; + + // empty versions compare as if their version is the empty string + Id evra = sa ? sa->evr : pool_str2id(pool, "", 1); + Id evrb = sb ? sb->evr : pool_str2id(pool, "", 1); + + return pool_evrcmp(pool, evra, evrb, EVRCMP_COMPARE); +} + +// --------------------------------------------------------------------------- +// implements class SolverPool +// +// a simplified wrapper for libsolv +// --------------------------------------------------------------------------- + +static +void debug_callback(Pool *pool, void *data, int type, const char *str) +{ + if (type & (SOLV_FATAL|SOLV_ERROR)) + LogPlainPrintf("libsolv: %s", str); + else + LogBabblePrintf("libsolv: %s", str); +} + +SolverPool::SolverPool() +{ + init(); +} + +void +SolverPool::init() +{ + /* create a pool */ + pool = pool_create(); + + pool_setdebugcallback(pool, debug_callback, NULL); + + int level = 1; +#if DEBUG + level = 3; +#endif + pool_setdebuglevel(pool, level); + + /* create the repo to hold installed packages */ + SolvRepo *installed = getRepo("_installed"); + pool_set_installed(pool, installed->repo); +} + +void +SolverPool::clear() +{ + repos.clear(); + pool_free(pool); + pool = NULL; + + init(); +} + +SolvRepo * +SolverPool::getRepo(const std::string &name, bool test) +{ + RepoList::iterator i = repos.find(name); + if (i != repos.end()) + return i->second; + + /* create repo if not found */ + SolvRepo *r = new(SolvRepo); + r->repo = repo_create(pool, name.c_str()); + + /* create attribute store, with no local pool */ + r->data = repo_add_repodata(r->repo, 0); + + /* remember if this is a test stability repo */ + r->test = test; + + /* set default priority */ + r->repo->priority = SolvRepo::priorityNormal; + + repos[name] = r; + + return r; +} + +/* + Helper function to convert a PackageDepends list to libsolv dependencies. +*/ +Id +SolverPool::makedeps(Repo *repo, PackageDepends *requires) +{ + Id deps = 0; + + for (PackageDepends::iterator i = requires->begin(); + i != requires->end(); + i++) + { + Id name = pool_str2id(pool, (*i)->packageName().c_str(), 1); + + if ((*i)->version().size() == 0) + { + // no relation, so dependency is just on package name + deps = repo_addid_dep(repo, deps, name, 0); + } + else + { + // otherwise, dependency is on package name with a version condition + Id evr = pool_str2id(pool, (*i)->version().c_str(), 1); + int rel = pool_rel2id(pool, name, evr, Operator2RelId((*i)->op()), 1); + + deps = repo_addid_dep(repo, deps, rel, 0); + } + } + + return deps; +} + +SolvableVersion +SolverPool::addPackage(const std::string& pkgname, const addPackageData &pkgdata) +{ + std::string repoName = pkgdata.reponame; + bool test = false; + + /* It's simplest to place test packages into a separate repo, and + then arrange for that repo to have low priority, if we don't want + to install those packages by default */ + + if (pkgdata.stability == TRUST_TEST && repoName != "_installed") + { + repoName = pkgdata.reponame + "_test_"; + test = true; + } + + SolvRepo *r = getRepo(repoName, test); + Repo *repo = r->repo; + + /* create a solvable */ + Id s = repo_add_solvable(repo); + Solvable *solvable = pool_id2solvable(pool, s); + + /* initialize solvable for this packageo/version/etc. */ + solvable->name = pool_str2id(pool, pkgname.c_str(), 1); + solvable->arch = (pkgdata.type == package_binary) ? ARCH_ANY : ARCH_SRC; + solvable->evr = pool_str2id(repo->pool, pkgdata.version.c_str(), 1); + solvable->vendor = pool_str2id(repo->pool, pkgdata.vendor.c_str(), 1); + solvable->provides = repo_addid_dep(repo, solvable->provides, pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0); + if (pkgdata.requires) + solvable->requires = makedeps(repo, pkgdata.requires); + if (pkgdata.obsoletes) + solvable->obsoletes = makedeps(repo, pkgdata.obsoletes); + + /* a solvable can also store arbitrary attributes not needed for dependency + resolution, if we need them */ + + Repodata *data = r->data; + Id handle = s; +#if DEBUG + Log (LOG_PLAIN) << "solvable " << s << " name " << pkgname << endLog; +#endif + + /* store short description attribute */ + repodata_set_str(data, handle, SOLVABLE_SUMMARY, pkgdata.sdesc.c_str()); + /* store long description attribute */ + repodata_set_str(data, handle, SOLVABLE_DESCRIPTION, pkgdata.ldesc.c_str()); + + /* store source-package attribute */ + const std::string sname = pkgdata.spkg.packageName(); + if (!sname.empty()) + repodata_set_id(data, handle, SOLVABLE_SOURCENAME, pool_str2id(pool, sname.c_str(), 1)); + else + repodata_set_void(data, handle, SOLVABLE_SOURCENAME); + /* solvable:sourceevr may also be available from spkg but assumed to be same + as evr for the moment */ + + /* store source-package id */ + /* (If the source-package hasn't been seen yet, we don't know what it's Id + will be. That gets fixed up later.) */ + if (pkgdata.spkg_id) + { + Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1); + repodata_set_id(data, handle, spkg_attr, pkgdata.spkg_id.id); + } + + /* we could store packagesource information as attributes ... + + e.g. + size SOLVABLE_DOWNLOADSIZE + pathname SOLVABLE_MEDIAFILE + site SOLVABLE_MEDIABASE + checksum SOLVABLE_CHECKSUM + + ... but for the moment, we just store a pointer to a packagesource object + */ + Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1); + packagesource *psrc = new packagesource(pkgdata.archive); + repodata_set_num(data, handle, psrc_attr, (intptr_t)psrc); + + /* store stability level attribute */ + Id stability_attr = pool_str2id(pool, "solvable:stability", 1); + repodata_set_num(data, handle, stability_attr, pkgdata.stability); + +#if 0 + repodata_internalize(data); + + /* debug: verify the attributes we've just set get retrieved correctly */ + SolvableVersion sv = SolvableVersion(s, pool); + const std::string check_sdesc = sv.SDesc(); + if (pkgdata.sdesc.compare(check_sdesc) != 0) { + Log (LOG_PLAIN) << pkgname << " has sdesc mismatch: '" << pkgdata.sdesc << "' and '" + << check_sdesc << "'" << endLog; + } + if (!sname.empty()) { + SolvableVersion check_spkg = sv.sourcePackage(); + Solvable *check_spkg_solvable = pool_id2solvable(pool, check_spkg.id); + std::string check_sname = pool_id2str(pool, check_spkg_solvable->name); + if (sname.compare(check_sname) != 0) { + Log (LOG_PLAIN) << pkgname << " has spkg mismatch: '" << pkgdata.spkg.packageName() + << "' and '" << check_sname << "'" << endLog; + } + } + packagesource *check_archive = sv.source(); + if (check_archive != psrc) + Log (LOG_PLAIN) << pkgname << " has archive mismatch: " << psrc + << " and " << check_archive << endLog; + package_stability_t check_stability = sv.Stability(); + if (check_stability != pkgdata.stability) { + Log (LOG_PLAIN) << pkgname << " has stability mismatch: " << pkgdata.stability + << " and " << check_stability << endLog; + } +#endif + + return SolvableVersion(s, pool); +} + +void +SolverPool::internalize() +{ + /* Make attribute data available to queries */ + for (RepoList::iterator i = repos.begin(); + i != repos.end(); + i++) + { + repodata_internalize(i->second->data); + } +} + +void +SolverTasks::setTasks() +{ + // go through all packages, adding changed ones to the solver task list + packagedb db; + tasks.clear(); + + for (packagedb::packagecollection::iterator p = db.packages.begin (); + p != db.packages.end (); ++p) + { + packagemeta *pkg = p->second; + + // decode UI state to action + // keep and skip need attention only when they differ from the + // solver's solution + if (pkg->installed != pkg->desired) + { + if (pkg->desired) + add(pkg->desired, taskInstall); // install/upgrade + else + add(pkg->installed, taskUninstall); // uninstall + } + else if (pkg->installed) + { + if (pkg->picked()) + add(pkg->installed, taskReinstall); // reinstall + else if (pkg->installed != pkg->default_version) + add(pkg->installed, taskKeep); // keep + else + { + // if installed (with no action selected), but blacklisted, force + // a distupgrade of this package + if (pkg->isBlacklisted(pkg->installed)) + { + add(pkg->installed, taskForceDistUpgrade); + } + } + } + else if (pkg->default_version) + add(pkg->default_version, taskSkip); // skip + + // only install action makes sense for source packages + if (pkg->srcpicked()) + { + if (pkg->desired) + add(pkg->desired.sourcePackage(), taskInstall); + else + add(pkg->installed.sourcePackage(), taskInstall); + } + } +} + +void +SolverPool::use_test_packages(bool use_test_packages) +{ + // Give repos containing test packages higher priority than normal + // if wanted, otherwise lower priority. + SolvRepo::priority_t p = use_test_packages ? SolvRepo::priorityNormal : SolvRepo::priorityLow; + for (RepoList::iterator i = repos.begin(); + i != repos.end(); + i++) + { + if (i->second->test) + i->second->setPriority(p); + } +} + +// --------------------------------------------------------------------------- +// implements class SolverSolution +// +// A wrapper around the libsolv solver +// --------------------------------------------------------------------------- + +SolverSolution::SolverSolution(SolverPool &_pool) : pool(_pool), solv(NULL) +{ + queue_init(&job); +} + +SolverSolution::~SolverSolution() +{ + clear(); +} + +void +SolverSolution::clear() +{ + if (solv) + { + solver_free(solv); + solv = NULL; + } + queue_free(&job); +} + +void +SolverSolution::trans2db() const +{ + packagedb db; + // First reset all packages to the "no change" state + for (packagedb::packagecollection::iterator i = db.packages.begin(); + i != db.packages.end(); i++) + { + packagemeta *pkg = i->second; + pkg->desired = pkg->default_version = pkg->installed; + pkg->pick(false); + } + // Now make changes according to trans. transErase requires some + // care; it could either be a "plain" uninstall, or it could be + // paired with a transInstall for an upgrade/downgrade or reinstall. + for (SolverTransactionList::const_iterator i = trans.begin(); + i != trans.end(); i++) + { + const packageversion & pv = i->version; + packagemeta *pkg = db.findBinary(PackageSpecification(pv.Name())); + if (!pkg) + // Can't happen - throw an exception? + { + Log (LOG_PLAIN) << "Can't happen. No packagemeta for " + << pv.Name() << endLog; + return; + } + switch (i->type) + { + case SolverTransaction::transInstall: + if (pv.Type() == package_binary) + { + pkg->desired = pkg->default_version = pv; + pkg->pick(true); + } + else // source package + pkg->srcpick(true); + break; + case SolverTransaction::transErase: + // Only relevant if pkg is still in its "no change" state + if (pkg->desired == pkg->installed && !pkg->picked()) + pkg->desired = pkg->default_version = packageversion(); + break; + default: + break; + } + } +} + +void +SolverSolution::db2trans() +{ + trans.clear(); + packagedb db; + + for (packagedb::packagecollection::iterator p = db.packages.begin (); + p != db.packages.end (); ++p) + { + packagemeta *pkg = p->second; + if (pkg->desired && pkg->picked()) // install/upgrade/reinstall + { + trans.push_back(SolverTransaction(pkg->desired, SolverTransaction::transInstall)); + if (pkg->installed) + trans.push_back(SolverTransaction(pkg->installed, SolverTransaction::transErase)); + } + else if (!pkg->desired && pkg->installed) // uninstall + trans.push_back(SolverTransaction(pkg->installed, SolverTransaction::transErase)); + + if (pkg->srcpicked()) + { + if (pkg->desired) + trans.push_back(SolverTransaction(pkg->desired.sourcePackage(), SolverTransaction::transInstall)); + else + trans.push_back(SolverTransaction(pkg->installed.sourcePackage(), SolverTransaction::transInstall)); + } + } +} + +static +std::ostream &operator<<(std::ostream &stream, + SolverTransaction::transType type) +{ + switch (type) + { + case SolverTransaction::transInstall: + stream << "install"; + break; + case SolverTransaction::transErase: + stream << "erase"; + break; + default: + stream << "unknown"; + } + return stream; +} + +void +SolverSolution::tasksToJobs(SolverTasks &tasks, updateMode update, Queue &job) +{ + // solver accepts a queue containing pairs of (cmd, id) tasks + // cmd is job and selection flags ORed together + for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin(); + i != tasks.tasks.end(); + i++) + { + const SolvableVersion &sv = (*i).first; + + switch ((*i).second) + { + case SolverTasks::taskInstall: + queue_push2(&job, SOLVER_INSTALL | SOLVER_SOLVABLE, sv.id); + break; + case SolverTasks::taskUninstall: + queue_push2(&job, SOLVER_ERASE | SOLVER_SOLVABLE, sv.id); + break; + case SolverTasks::taskReinstall: + // we don't know how to ask solver for this, so we just add the erase + // and install later + break; + case SolverTasks::taskKeep: + queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE, sv.id); + break; + case SolverTasks::taskSkip: + queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE_NAME, sv.name_id()); + break; + case SolverTasks::taskForceDistUpgrade: + queue_push2(&job, SOLVER_DISTUPGRADE | SOLVER_SOLVABLE, sv.id); + break; + default: + Log (LOG_PLAIN) << "unknown task " << (*i).second << endLog; + } + } + + // Ask solver to update packages + switch (update) + { + case keep: + break; + + case updateBest: + // Update to best version + queue_push2(&job, SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, 0); + break; + + case updateForce: + // Bring installed, non-orphaned packages in sync with the ones in the + // repository + queue_push2(&job, SOLVER_DISTUPGRADE | SOLVER_SOLVABLE_ALL, 0); + break; + } + + // Ask solver to check dependencies of installed packages. + queue_push2(&job, SOLVER_VERIFY | SOLVER_SOLVABLE_ALL, 0); +} + +bool +SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_packages) +{ + Log (LOG_PLAIN) << "solving: " << tasks.tasks.size() << " tasks," << + " update: " << (update ? "yes" : "no") << "," << + " use test packages: " << (use_test_packages ? "yes" : "no") << endLog; + + pool.use_test_packages(use_test_packages); + + queue_free(&job); + tasksToJobs(tasks, update, job); + + if (!solv) + solv = solver_create(pool.pool); + + return solve(); +} + +bool +SolverSolution::solve() +{ + solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1); + solver_set_flag(solv, SOLVER_FLAG_ALLOW_DOWNGRADE, 0); + solver_set_flag(solv, SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE, 1); + solver_set_flag(solv, SOLVER_FLAG_DUP_ALLOW_DOWNGRADE, 1); + solver_solve(solv, &job); + + int pcnt = solver_problem_count(solv); + solver_printdecisions(solv); + + solutionToTransactionList(); + + return (pcnt == 0); +} + +void +SolverSolution::solutionToTransactionList() +{ + // get transactions for solution + Transaction *t = solver_create_transaction(solv); + transaction_order(t, 0); + transaction_print(t); + + // massage into SolverTransactions form + trans.clear(); + for (int i = 0; i < t->steps.count; i++) + { + Id id = t->steps.elements[i]; + SolverTransaction::transType tt = type(t, i); + if (tt != SolverTransaction::transIgnore) + trans.push_back(SolverTransaction(SolvableVersion(id, pool.pool), tt)); + } + + transaction_free(t); + + dumpTransactionList(); +} + +void +SolverSolution::augmentTasks(SolverTasks &tasks) +{ + // add install and remove tasks for anything marked as reinstall + for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin(); + i != tasks.tasks.end(); + i++) + { + const SolvableVersion &sv = (*i).first; + + if (((*i).second) == SolverTasks::taskReinstall) + { + trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool), + SolverTransaction::transErase)); + trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool), + SolverTransaction::transInstall)); + } + } +} + +void +SolverSolution::addSource(bool include_source) +{ + // if include_source mode is on, also install source for everything we are + // installing + if (include_source) + { + // (this uses indicies into the vector, as iterators might become + // invalidated by doing push_back) + size_t n = trans.size(); + for (size_t i = 0; i < n; i++) + { + if (trans[i].type == SolverTransaction::transInstall) + { + SolvableVersion src_version = trans[i].version.sourcePackage(); + if (src_version) + trans.push_back(SolverTransaction(src_version, + SolverTransaction::transInstall)); + } + } + } +} + +void +SolverSolution::dumpTransactionList() const +{ + if (trans.size()) + { + Log (LOG_PLAIN) << "Augmented Transaction List:" << endLog; + for (SolverTransactionList::const_iterator i = trans.begin (); + i != trans.end (); + ++i) + { + Log (LOG_PLAIN) << std::setw(4) << std::distance(trans.begin(), i) + << std::setw(8) << i->type + << std::setw(48) << i->version.Name() + << std::setw(20) << i->version.Canonical_version() << endLog; + } + } + else + Log (LOG_PLAIN) << "Augmented Transaction List: is empty" << endLog; +} + +void SolverSolution::applyDefaultProblemSolutions() +{ + // adjust the task list with the default solutions + int pcnt = solver_problem_count(solv); + for (Id problem = 1; problem <= pcnt; problem++) + { + int scnt = solver_solution_count(solv, problem); + solver_take_solution(solv, problem, scnt, &job); + } + + // re-solve + if (!solve()) + Log (LOG_PLAIN) << "default solutions did not solve all problems!" << endLog; +} + +const SolverTransactionList & +SolverSolution::transactions() const +{ + return trans; +} + +// Construct a string reporting the problems and solutions +std::string +SolverSolution::report() const +{ + packagedb db; + std::string r = ""; + int pcnt = solver_problem_count(solv); + for (Id problem = 1; problem <= pcnt; problem++) + { + r += "Problem " + std::to_string(problem) + "/" + std::to_string(pcnt); + r += "\n"; + + Id probr = solver_findproblemrule(solv, problem); + Id dep, source, target; + SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep); + if (source == db.basepkg.id) + r += "package " + std::string(pool_dep2str(pool.pool, dep)) + " is a Base package and is therefore required"; + else + r += solver_problemruleinfo2str(solv, type, source, target, dep); + r += "\n"; + + int scnt = solver_solution_count(solv, problem); + for (Id solution = 1; solution <= scnt; solution++) + { + r += "Solution " + std::to_string(solution) + "/" + std::to_string(scnt); + if (solution == scnt) r += " (default)"; + r += "\n"; + + Id p, rp, element; + element = 0; + while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0) + { + r += " - "; + if (p == db.basepkg.id) + r += "allow deinstallation of Base packages"; + else + r += solver_solutionelement2str(solv, p, rp); + r += "\n"; + } + } + } + + // since package arch isn't set usefully at the moment, remove all ".any" from + // package names in the problem report string. + std::string any = ".any"; + size_t pos; + while ((pos = r.find(any)) != std::string::npos) + { + r.replace(pos, any.length(), ""); + } + + return r; +} + +// helper function to map transaction type +SolverTransaction::transType +SolverSolution::type(Transaction *trans, int pos) +{ + Id tt = transaction_type(trans, trans->steps.elements[pos], + SOLVER_TRANSACTION_SHOW_ACTIVE); + + // if active side of transaction is nothing, ask again for passive side of + // transaction + if (tt == SOLVER_TRANSACTION_IGNORE) + tt = transaction_type(trans, trans->steps.elements[pos], 0); + + switch(tt) + { + case SOLVER_TRANSACTION_INSTALL: + case SOLVER_TRANSACTION_REINSTALL: + case SOLVER_TRANSACTION_UPGRADE: + case SOLVER_TRANSACTION_DOWNGRADE: + return SolverTransaction::transInstall; + case SOLVER_TRANSACTION_ERASE: + case SOLVER_TRANSACTION_REINSTALLED: + case SOLVER_TRANSACTION_UPGRADED: + case SOLVER_TRANSACTION_DOWNGRADED: + return SolverTransaction::transErase; + default: + Log (LOG_PLAIN) << "unknown transaction type " << std::hex << tt << endLog; + case SOLVER_TRANSACTION_CHANGE: + case SOLVER_TRANSACTION_CHANGED: + case SOLVER_TRANSACTION_IGNORE: + return SolverTransaction::transIgnore; + } +}; diff --git a/libsolv.h b/libsolv.h new file mode 100644 index 0000000..6a6e0b3 --- /dev/null +++ b/libsolv.h @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2017 Jon Turney + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * A copy of the GNU General Public License can be found at + * http://www.gnu.org/ + * + */ + +#ifndef LIBSOLV_H +#define LIBSOLV_H + +#include "solv/pool.h" +#include "solv/repo.h" +#include "solv/solver.h" +#include "PackageSpecification.h" +#include "PackageTrust.h" +#include "package_source.h" +#include "package_depends.h" +#include <map> +#include <vector> + +typedef trusts package_stability_t; + +typedef enum +{ + package_binary, + package_source +} +package_type_t; + +// --------------------------------------------------------------------------- +// interface to class SolverVersion +// +// a wrapper around a libsolv Solvable +// --------------------------------------------------------------------------- + +class SolverPool; +class SolverSolution; + +class SolvableVersion +{ + public: + SolvableVersion() : id(0), pool(0) {}; + SolvableVersion(Id _id, Pool *_pool) : id(_id), pool(_pool) {}; + + // converted to a bool, this is true if this isn't the result of the default + // constructor (an 'empty' version, in some sense) + explicit operator bool () const { return (id != 0); } + + const std::string Name () const; + const std::string SDesc () const; + // In setup-speak, 'Canonical' version means 'e:v-r', the non-decomposed version + const std::string Canonical_version () const; + // Return the dependency list + const PackageDepends depends() const; + // Return the obsoletes list + const PackageDepends obsoletes() const; + bool accessible () const; + package_type_t Type () const; + package_stability_t Stability () const; + // the associated source package name, if this is a binary package + const std::string sourcePackageName () const; + // the associated source package, if this is a binary package + SolvableVersion sourcePackage () const; + // where this package archive can be obtained from + packagesource *source() const; + + // fixup spkg_id + void fixup_spkg_id(SolvableVersion spkg_id) const; + + // utility function to compare package versions + static int compareVersions(const SolvableVersion &a, const SolvableVersion &b); + + // comparison operators + + // these are somewhat necessary as otherwise we are compared as bool values + bool operator == (SolvableVersion const &) const; + bool operator != (SolvableVersion const &) const; + + // these are only well defined for versions of the same package + bool operator < (SolvableVersion const &) const; + bool operator <= (SolvableVersion const &) const; + bool operator > (SolvableVersion const &) const; + bool operator >= (SolvableVersion const &) const; + + private: + Id id; + Pool *pool; + + friend SolverPool; + friend SolverSolution; + + const PackageDepends deplist(Id keyname) const; + Id name_id () const; +}; + +// --------------------------------------------------------------------------- +// Helper class SolvRepo +// +// --------------------------------------------------------------------------- + +class SolvRepo +{ +public: + typedef enum + { + priorityLow = 0, + priorityNormal, + priorityHigh, + } priority_t; + Repo *repo; + Repodata *data; + bool test; + void setPriority(priority_t p) { repo->priority = p; } +}; + +// --------------------------------------------------------------------------- +// interface to class SolverPool +// +// a simplified wrapper for libsolv +// --------------------------------------------------------------------------- + +class SolverPool +{ +public: + SolverPool(); + void clear(); + SolvRepo *getRepo(const std::string &name, bool test = false); + + // Utility class for passing arguments to addPackage() + class addPackageData + { + public: + std::string reponame; + std::string version; + std::string vendor; + std::string sdesc; + std::string ldesc; + package_stability_t stability; + package_type_t type; + packagesource archive; + PackageSpecification spkg; + SolvableVersion spkg_id; + PackageDepends *requires; + PackageDepends *obsoletes; + }; + + SolvableVersion addPackage(const std::string& pkgname, + const addPackageData &pkgdata); + + void internalize(void); + void use_test_packages(bool use_test_packages); + + +private: + void init(); + Id makedeps(Repo *repo, PackageDepends *requires); + Pool *pool; + + typedef std::map<std::string, SolvRepo *> RepoList; + RepoList repos; + + friend SolverSolution; +}; + + +// --------------------------------------------------------------------------- +// interface to class SolverTaskQueue +// +// This is used to contain a set of package install/uninstall tasks selected via +// the UI to be passed to solver +// --------------------------------------------------------------------------- + +class SolverTasks +{ + public: + enum task + { + taskInstall, + taskUninstall, + taskReinstall, + taskKeep, + taskSkip, + taskForceDistUpgrade, + }; + void add(const SolvableVersion &v, task t) + { + tasks.push_back(taskList::value_type(v, t)); + }; + /* Create solver tasks corresponding to state of database */ + void setTasks(); + + private: + typedef std::vector<std::pair<const SolvableVersion, task>> taskList; + taskList tasks; + + friend SolverSolution; +}; + +// --------------------------------------------------------------------------- +// SolverTransactionList +// +// a list of transactions output by the solver +// --------------------------------------------------------------------------- + +class SolverTransaction +{ + public: + typedef enum + { + transIgnore, + transInstall, + transErase, + } transType; + + SolverTransaction(SolvableVersion version_, transType type_) : + version(version_), type(type_) {}; + + SolvableVersion version; + transType type; +}; + +typedef std::vector<SolverTransaction> SolverTransactionList; + +// --------------------------------------------------------------------------- +// interface to class SolverSolution +// +// A wrapper around the libsolv solver +// --------------------------------------------------------------------------- + +class SolverSolution +{ + public: + SolverSolution(SolverPool &_pool); + ~SolverSolution(); + void clear(); + + /* Reset package database to correspond to trans */ + void trans2db() const; + + /* Reset transaction list to correspond to package database */ + void db2trans(); + + enum updateMode + { + keep, // don't update + updateBest, // update to best version + updateForce, // distupdate: downgrade if necessary to best version in repo + }; + bool update(SolverTasks &tasks, updateMode update, bool use_test_packages); + void augmentTasks(SolverTasks &tasks); + void addSource(bool include_source); + void applyDefaultProblemSolutions(); + std::string report() const; + + const SolverTransactionList &transactions() const; + void dumpTransactionList() const; + + private: + static SolverTransaction::transType type(Transaction *trans, int pos); + bool solve(); + void tasksToJobs(SolverTasks &tasks, updateMode update, Queue &job); + void solutionToTransactionList(); + + SolverPool &pool; + Solver *solv; + Queue job; + SolverTransactionList trans; +}; + +#endif // LIBSOLV_H diff --git a/main.cc b/main.cc index 028f8de..2e57f94 100644 --- a/main.cc +++ b/main.cc @@ -55,6 +55,7 @@ #include "site.h" #include "choose.h" #include "prereq.h" +#include "confirm.h" #include "threebar.h" #include "desktop.h" #include "postinstallresults.h" @@ -135,6 +136,7 @@ main_display () SitePage Site; ChooserPage Chooser; PrereqPage Prereq; + ConfirmPage Confirm; DesktopSetupPage Desktop; PropSheet MainWindow; @@ -175,6 +177,7 @@ main_display () Site.Create (); Chooser.Create (); Prereq.Create (); + Confirm.Create (); Progress.Create (); PostInstallResults.Create (); Desktop.Create (); @@ -189,6 +192,7 @@ main_display () MainWindow.AddPage (&Site); MainWindow.AddPage (&Chooser); MainWindow.AddPage (&Prereq); + MainWindow.AddPage (&Confirm); MainWindow.AddPage (&Progress); MainWindow.AddPage (&PostInstallResults); MainWindow.AddPage (&Desktop); diff --git a/package_db.cc b/package_db.cc index cac98d7..730cb78 100644 --- a/package_db.cc +++ b/package_db.cc @@ -31,24 +31,50 @@ #include "compress.h" #include "filemanip.h" - #include "package_version.h" -#include "cygpackage.h" #include "package_db.h" #include "package_meta.h" #include "Exception.h" #include "Generic.h" #include "LogSingleton.h" #include "resource.h" +#include "libsolv.h" +#include "csu_util/version_compare.h" +#include "getopt++/BoolOption.h" + +static BoolOption MirrorOption (false, 'm', "mirror-mode", "Skip package availability check when installing from local directory (requires local directory to be clean mirror!)"); using namespace std; packagedb::packagedb () { - io_stream *db = 0; +} + +void +packagedb::init () +{ + installeddbread = 0; + installeddbver = 0; + prepped = false; + + packages.clear(); + sourcePackages.clear(); + categories.clear(); + solver.clear(); + solution.clear(); + basepkg = packageversion(); + dependencyOrderedPackages.clear(); +} + +void +packagedb::read () +{ if (!installeddbread) { - /* no parameters. Read in the local installation database. */ + solver.internalize(); + + /* Read in the local installation database. */ + io_stream *db = 0; db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0); installeddbread = 1; if (!db) @@ -98,23 +124,61 @@ packagedb::packagedb () if (!parseable) continue; - packagemeta *pkg = findBinary (PackageSpecification(pkgname)); - if (!pkg) - { - pkg = new packagemeta (pkgname); - packages.insert (packagedb::packagecollection::value_type(pkgname, pkg)); - } - - packageversion binary = - cygpackage::createInstance (pkgname, f.ver, - package_binary); - - pkg->add_version (binary); - pkg->set_installed (binary); - pkg->desired = pkg->installed; + SolverPool::addPackageData data; + data.reponame = "_installed"; + data.version = f.ver; + data.type = package_binary; + + // very limited information is available from installed.db, so + // we put our best guesses here... + data.vendor = "cygwin"; + data.requires = NULL; + data.obsoletes = NULL; + data.sdesc = ""; + data.ldesc = ""; + data.stability = TRUST_UNKNOWN; + data.spkg = PackageSpecification(std::string(pkgname) + "-src", f.ver); + + // supplement this with sdesc, source, and stability + // information from setup.ini, if possible... + packageversion pv = findBinaryVersion(PackageSpecification(pkgname, f.ver)); + PackageDepends dep; + PackageDepends obs; + if (pv) + { + data.sdesc = pv.SDesc(); + data.archive = *pv.source(); + data.stability = pv.Stability(); + data.spkg_id = pv.sourcePackage(); + data.spkg = pv.sourcePackageName(); + dep = pv.depends(); + data.requires = &dep; + obs = pv.obsoletes(); + data.obsoletes = &obs; + } + else + // This version is no longer available. It could + // be old, or it could be a previous test release + // that has been replaced by a new test release. + // Try to get some info from the packagemeta. + { + packagemeta *pkgm = findBinary(PackageSpecification(pkgname)); + if (pkgm) + { + data.sdesc = pkgm->curr.SDesc(); + if (pkgm->curr + && version_compare (f.ver, pkgm->curr.Canonical_version()) > 0) + data.stability = TRUST_TEST; + } + } + + packagemeta *pkg = packagedb::addBinary (pkgname, data); + + pkg->set_installed_version (f.ver); if (dbver == 3) pkg->user_picked = (user_picked & 1); + } delete db; db = 0; @@ -127,6 +191,80 @@ packagedb::packagedb () installeddbver = dbver; } } + solver.internalize(); +} + +/* Create the fictitious basepkg */ +void +packagedb::makeBase() +{ + SolverPool::addPackageData data; + data.reponame = "_installed"; + data.version = "0.0-0"; + data.type = package_binary; + data.vendor = "cygwin"; + data.sdesc = "Ficitious package that requires all Base packages"; + data.ldesc = "Ficitious package that requires all Base packages"; + data.obsoletes = NULL; + data.stability = TRUST_CURR; + // data.spkg = PackageSpecification(); + // data.spkg_id = packageversion(); + + PackageDepends dep; + for (std::vector <packagemeta *>::const_iterator i = categories["Base"].begin(); + i != categories["Base"].end(); i++) + { + packagemeta *pkg = *i; + PackageSpecification *spec = new PackageSpecification(pkg->name); + dep.push_back(spec); + } + data.requires = &dep; + + basepkg = solver.addPackage("base", data); + /* We don't register this in packagemeta */ +} + +/* Add a package version to the packagedb */ +packagemeta * +packagedb::addBinary (const std::string &pkgname, + const SolverPool::addPackageData &pkgdata) +{ + /* If pkgname isn't already in packagedb, add a packagemeta */ + packagemeta *pkg = findBinary (PackageSpecification(pkgname)); + if (!pkg) + { + pkg = new packagemeta (pkgname); + packages.insert (packagedb::packagecollection::value_type(pkgname, pkg)); + } + + /* Create the SolvableVersion */ + SolvableVersion sv = solver.addPackage(pkgname, pkgdata); + + /* Register it in packagemeta */ + pkg->add_version (sv, pkgdata); + + return pkg; +} + +packageversion +packagedb::addSource (const std::string &pkgname, + const SolverPool::addPackageData &pkgdata) +{ + /* If pkgname isn't already in packagedb, add a packagemeta */ + packagemeta *pkg = findSource (PackageSpecification(pkgname)); + if (!pkg) + { + pkg = new packagemeta (pkgname); + sourcePackages.insert (packagedb::packagecollection::value_type(pkgname, pkg)); + } + + /* Create the SolvableVersion */ + SolvableVersion sv = solver.addPackage(pkgname, pkgdata); + + /* Register it in packagemeta */ + pkg->add_version (sv, pkgdata); + + return sv; } int @@ -201,6 +339,21 @@ packagedb::findBinary (PackageSpecification const &spec) const return NULL; } +packageversion +packagedb::findBinaryVersion (PackageSpecification const &spec) const +{ + packagedb::packagecollection::iterator n = packages.find(spec.packageName()); + if (n != packages.end()) + { + packagemeta & pkgm = *(n->second); + for (set<packageversion>::iterator i=pkgm.versions.begin(); + i != pkgm.versions.end(); ++i) + if (spec.satisfies (*i)) + return *i; + } + return packageversion(); +} + packagemeta * packagedb::findSource (PackageSpecification const &spec) const { @@ -216,15 +369,34 @@ packagedb::findSource (PackageSpecification const &spec) const return NULL; } +packageversion +packagedb::findSourceVersion (PackageSpecification const &spec) const +{ + packagedb::packagecollection::iterator n = sourcePackages.find(spec.packageName()); + if (n != sourcePackages.end()) + { + packagemeta & pkgm = *(n->second); + for (set<packageversion>::iterator i = pkgm.versions.begin(); + i != pkgm.versions.end(); ++i) + if (spec.satisfies (*i)) + return *i; + } + return packageversion(); +} + /* static members */ int packagedb::installeddbread = 0; int packagedb::installeddbver = 0; +bool packagedb::prepped = false; packagedb::packagecollection packagedb::packages; packagedb::categoriesType packagedb::categories; packagedb::packagecollection packagedb::sourcePackages; PackageDBActions packagedb::task = PackageDB_Install; +packageversion packagedb::basepkg; std::vector <packagemeta *> packagedb::dependencyOrderedPackages; +SolverPool packagedb::solver; +SolverSolution packagedb::solution(packagedb::solver); #include <stack> @@ -312,8 +484,9 @@ ConnectedLoopFinder::visit(packagemeta *nodeToVisit) nodesInStronglyConnectedComponent.push(nodeToVisit); /* walk through each node */ - PackageDepends::const_iterator dp = nodeToVisit->installed.depends()->begin(); - while (dp != nodeToVisit->installed.depends()->end()) + const PackageDepends deps = nodeToVisit->installed.depends(); + PackageDepends::const_iterator dp = deps.begin(); + while (dp != deps.end()) { /* check for an installed match */ if (checkForInstalled (*dp)) @@ -428,22 +601,12 @@ packagedb::fillMissingCategory () } void -packagedb::defaultTrust (trusts trust) +packagedb::defaultTrust (SolverTasks &q, SolverSolution::updateMode mode, bool test) { - for (packagedb::packagecollection::iterator i = packages.begin (); i != packages.end (); ++i) - { - packagemeta & pkg = *(i->second); - if (pkg.installed - || pkg.categories.find ("Base") != pkg.categories.end () - || pkg.categories.find ("Orphaned") != pkg.categories.end ()) - { - pkg.desired = pkg.trustp (true, trust); - if (pkg.desired) - pkg.pick (pkg.desired.accessible() && pkg.desired != pkg.installed); - } - else - pkg.desired = packageversion (); - } + solution.update(q, mode, test); + + // reflect that task list into packagedb + solution.trans2db(); } void @@ -501,8 +664,9 @@ packagedb::guessUserPicked() continue; /* walk through each node */ - std::vector <PackageSpecification *>::const_iterator dp = pkgm.installed.depends()->begin(); - while (dp != pkgm.installed.depends()->end()) + const PackageDepends deps = pkgm.installed.depends(); + std::vector <PackageSpecification *>::const_iterator dp = deps.begin(); + while (dp != deps.end()) { /* check for an installed match */ if (checkForInstalled(*dp)) @@ -521,3 +685,66 @@ packagedb::guessUserPicked() } } } + +void +packagedb::fixup_source_package_ids() +{ + for (packagecollection::iterator i = packages.begin (); + i != packages.end (); ++i) + { + packagemeta &pkgm = *(i->second); + + for (set<packageversion>::iterator i = pkgm.versions.begin(); + i != pkgm.versions.end(); ++i) + { + /* If spkg_id is already known for this package, there's nothing to + fix. */ + if (i->sourcePackage()) + continue; + + /* Some packages really have no source, indicated by no [sS]ource: + line in setup.ini, which becomes an empty source package name */ + const std::string spkg = i->sourcePackageName(); + if (spkg.empty()) + continue; + + /* Otherwise, we need to find the source package and fix up the source + package id*/ + packageversion spkg_id = findSourceVersion(PackageSpecification(spkg, + i->Canonical_version())); + + if (spkg_id) + { + i->fixup_spkg_id(spkg_id); + } + else + { + Log (LOG_BABBLE) << "No source package for '" << i->Name() << "' " << i->Canonical_version() << ", source package name '" << spkg << "'" << endLog; + } + } + } +} + +void +packagedb::prep() +{ + /* make packagedb ready for use for chooser */ + if (prepped) + return; + + makeBase(); + read(); + upgrade(); + fixup_source_package_ids(); + removeEmptyCategories(); + + /* XXX: this needs to be broken out somewhere where it can do progress + reporting, as it can take a long time... */ + if (source == IDC_SOURCE_DOWNLOAD || source ==IDC_SOURCE_LOCALDIR) + packagemeta::ScanDownloadedFiles (MirrorOption); + + setExistence (); + fillMissingCategory (); + + prepped = true; +} diff --git a/package_db.h b/package_db.h index 59737c0..e500e4b 100644 --- a/package_db.h +++ b/package_db.h @@ -57,23 +57,29 @@ typedef std::vector <packagemeta *>::iterator PackageDBConnectedIterator; */ +#include "libsolv.h" #include <PackageTrust.h> class packagedb { public: packagedb (); + void init(); /* 0 on success */ int flush (); - void upgrade (); + void prep(); + packagemeta * findBinary (PackageSpecification const &) const; + packageversion findBinaryVersion (PackageSpecification const &) const; packagemeta * findSource (PackageSpecification const &) const; + packageversion findSourceVersion (PackageSpecification const &spec) const; + packagemeta * addBinary (const std::string &pkgname, const SolverPool::addPackageData &pkgdata); + packageversion addSource (const std::string &pkgname, const SolverPool::addPackageData &pkgdata); + PackageDBConnectedIterator connectedBegin(); PackageDBConnectedIterator connectedEnd(); - void fillMissingCategory(); - void defaultTrust (trusts trust); - void setExistence(); - void removeEmptyCategories(); + + void defaultTrust (SolverTasks &q, SolverSolution::updateMode mode, bool test); typedef std::map <std::string, packagemeta *> packagecollection; /* all seen binary packages */ @@ -84,12 +90,28 @@ public: typedef std::map <std::string, std::vector <packagemeta *>, casecompare_lt_op > categoriesType; static categoriesType categories; static PackageDBActions task; + /* a ficitious package that requires all packages in the Base category */ + static packageversion basepkg; + + static SolverPool solver; + static SolverSolution solution; + private: + void makeBase(); + void read(); + void upgrade (); + void fixup_source_package_ids(); + void removeEmptyCategories(); + void fillMissingCategory(); + void setExistence(); + void guessUserPicked(void); + static int installeddbread; /* do we have to reread this */ static int installeddbver; + static bool prepped; + friend class ConnectedLoopFinder; static std::vector <packagemeta *> dependencyOrderedPackages; - void guessUserPicked(void); }; #endif /* SETUP_PACKAGE_DB_H */ diff --git a/package_depends.cc b/package_depends.cc index e288c0b..a03f5a0 100644 --- a/package_depends.cc +++ b/package_depends.cc @@ -15,19 +15,16 @@ #include <LogSingleton.h> void -dumpPackageDepends (PackageDepends const *currentList, +dumpPackageDepends (PackageDepends const ¤tList, std::ostream &logger) { - if (currentList) + Log (LOG_BABBLE) << "( "; + PackageDepends::const_iterator i = currentList.begin(); + while (true) { - Log (LOG_BABBLE) << "( "; - PackageDepends::const_iterator i = currentList->begin(); - while (true) - { - if (i == currentList->end()) break; - Log (LOG_BABBLE) << **i << " "; - ++i; - } - Log (LOG_BABBLE) << ")"; + if (i == currentList.end()) break; + Log (LOG_BABBLE) << **i << " "; + ++i; } + Log (LOG_BABBLE) << ")"; } diff --git a/package_depends.h b/package_depends.h index af3fa01..36f7f6f 100644 --- a/package_depends.h +++ b/package_depends.h @@ -19,6 +19,6 @@ typedef std::vector <PackageSpecification *> PackageDepends; -void dumpPackageDepends (PackageDepends const *currentList, std::ostream &); +void dumpPackageDepends (PackageDepends const ¤tList, std::ostream &); #endif // PACKAGE_DEPENDS_H diff --git a/package_meta.cc b/package_meta.cc index af494f4..c488e35 100644 --- a/package_meta.cc +++ b/package_meta.cc @@ -35,11 +35,7 @@ using namespace std; /* this goes at the same time */ #include "win32.h" - #include "script.h" - -#include "package_version.h" -#include "cygpackage.h" #include "package_db.h" #include <algorithm> @@ -128,19 +124,98 @@ packagemeta::~packagemeta() } void -packagemeta::add_version (packageversion & thepkg) +packagemeta::add_version (packageversion & thepkg, const SolverPool::addPackageData &pkgdata) +{ + /* + If a packageversion for the same version number is already present,allow + this version to replace it. + + There is a problem where multiple repos provide a package. It's never been + clear which repo should win. With this implementation, the last one added + will win. + + We rely on this by adding packages from installed.db last. + */ + + set <packageversion>::iterator i = versions.find(thepkg); + if (i != versions.end()) + { + versions.erase(i); + } + + /* Add the version */ + std::pair<std::set <packageversion>::iterator, bool> result = versions.insert (thepkg); + + if (!result.second) + Log (LOG_PLAIN) << "Failed to add version " << thepkg.Canonical_version() << " in package " << name << endLog; +#ifdef DEBUG + else + Log (LOG_PLAIN) << "Added version " << thepkg.Canonical_version() << " in package " << name << endLog; +#endif + + /* Record the highest version at a given stability level */ + /* (This has to be written somewhat carefully as attributes aren't + internalized yet so we can't look at them) */ + packageversion *v = NULL; + switch (pkgdata.stability) + { + case TRUST_CURR: + v = &(this->curr); + break; + case TRUST_TEST: + v = &(this->exp); + break; + default: + break; + } + + if (v) + { + /* Any version is always greater than no version */ + int comparison = 1; + if (*v) + comparison = SolvableVersion::compareVersions(thepkg, *v); + +#ifdef DEBUG + if ((bool)(*v)) + Log (LOG_BABBLE) << "package " << thepkg.Name() << " comparing versions " << thepkg.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog; +#endif + + if (comparison >= 0) + { + *v = thepkg; + } + } +} + +bool +packagemeta::isBlacklisted(const packageversion &version) const { - /* todo: check return value */ - versions.insert (thepkg); + for (std::set<std::string>::iterator i = version_blacklist.begin(); + i != version_blacklist.end(); + i++) + { + if (i->compare(version.Canonical_version()) == 0) + return true; + } + + return false; } -/* assumption: package thepkg is already in the metadata list. */ void -packagemeta::set_installed (packageversion & thepkg) +packagemeta::set_installed_version (const std::string &version) { - set<packageversion>::const_iterator temp = versions.find (thepkg); - if (temp != versions.end()) - installed = thepkg; + set<packageversion>::iterator i; + for (i = versions.begin(); i != versions.end(); i++) + { + if (version.compare(i->Canonical_version()) == 0) + { + installed = *i; + + /* and mark as Keep */ + desired = installed; + } + } } void @@ -174,12 +249,6 @@ packagemeta::getReadableCategoryList () const ).visitor.result; } -static bool -hasSDesc(packageversion const &pkg) -{ - return pkg.SDesc().size(); -} - static void parseNames (std::set<string> &parsed, std::string &option) { @@ -304,11 +373,15 @@ bool packagemeta::isManuallyDeleted() const const std::string packagemeta::SDesc () const { - set<packageversion>::iterator i = find_if (versions.begin(), versions.end(), hasSDesc); - if (i == versions.end()) - return std::string(); - return i->SDesc (); -}; + set<packageversion>::iterator i; + for (i = versions.begin(); i != versions.end(); i++) + { + if (i->SDesc().size()) + return i->SDesc (); + } + + return std::string(); +} /* Return an appropriate caption given the current action. */ std::string @@ -525,13 +598,10 @@ packagemeta::logAllVersions () const { Log (LOG_BABBLE) << " [" << trustLabel(*i) << "] ver=" << i->Canonical_version() << endLog; - if (i->depends()->size()) - { - std::ostream & logger = Log (LOG_BABBLE); - logger << " depends="; - dumpPackageDepends(i->depends(), logger); - logger << endLog; - } + std::ostream & logger = Log (LOG_BABBLE); + logger << " depends="; + dumpPackageDepends(i->depends(), logger); + logger << endLog; } #if 0 Log (LOG_BABBLE) << " inst=" << i-> diff --git a/package_meta.h b/package_meta.h index 8041aa1..32372e2 100644 --- a/package_meta.h +++ b/package_meta.h @@ -16,7 +16,8 @@ #ifndef SETUP_PACKAGE_META_H #define SETUP_PACKAGE_META_H -class packageversion; +class SolvableVersion; +typedef SolvableVersion packageversion; class packagemeta; #include <set> @@ -28,7 +29,7 @@ class packagemeta; typedef std::pair<const std::string, std::vector<packagemeta *> > Category; -/* NOTE: A packagemeta without 1 packageversion is invalid! */ +/* NOTE: A packagemeta without 1 version is invalid! */ class packagemeta { public: @@ -42,8 +43,8 @@ public: ~packagemeta (); - void add_version (packageversion &); - void set_installed (packageversion &); + void add_version (packageversion &, const SolverPool::addPackageData &); + void set_installed_version (const std::string &); void addToCategoryBase(); bool hasNoCategories() const; void setDefaultCategories(); @@ -77,6 +78,11 @@ public: message.set (message_id, message_string); } + void set_version_blacklist(std::set <std::string> &_list) + { + version_blacklist = _list; + } + std::string action_caption () const; packageversion trustp (bool _default, trusts const t) const { @@ -130,6 +136,8 @@ public: packageversion curr; /* ditto for "test" (experimental) */ packageversion exp; + /* which one is the default according to the solver */ + packageversion default_version; /* Now for the user stuff :] */ /* What version does the user want ? */ packageversion desired; @@ -154,6 +162,9 @@ public: void addScript(Script const &); std::vector <Script> &scripts(); + /* this version is undesirable */ + bool isBlacklisted(const packageversion &version) const; + protected: packagemeta &operator= (packagemeta const &); private: @@ -163,6 +174,8 @@ private: bool _picked; /* true if desired version is to be (re)installed */ bool _srcpicked; + + std::set <std::string> version_blacklist; }; #endif /* SETUP_PACKAGE_META_H */ diff --git a/package_version.cc b/package_version.cc deleted file mode 100644 index 2587fbb..0000000 --- a/package_version.cc +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (c) 2001, 2003 Robert Collins. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * A copy of the GNU General Public License can be found at - * http://www.gnu.org/ - * - * Written by Robert Collins <rbtcollins@hotmail.com> - * - */ - -/* this is the parent class for all package operations. - */ - -#include "package_version.h" -#include "package_db.h" -#include "package_meta.h" -#include "LogSingleton.h" -#include "state.h" -#include "resource.h" -#include <algorithm> -#include "csu_util/version_compare.h" - -using namespace std; - -/* a default class to avoid special casing empty packageversions */ - -/* TODO place into the class header */ -class _defaultversion : public _packageversion -{ -public: - _defaultversion() - { - // never try to free me! - ++references; - } - const std::string Name(){return std::string();} - const std::string Vendor_version() {return std::string();} - const std::string Package_version() {return std::string();} - const std::string Canonical_version() {return std::string();} - void setCanonicalVersion (const std::string& ) {} - package_type_t Type () {return package_binary;} - const std::string SDesc () {return std::string();} - void set_sdesc (const std::string& ) {} - const std::string LDesc () {return std::string();} - void set_ldesc (const std::string& ) {} -}; -static _defaultversion defaultversion; - -/* the wrapper class */ -packageversion::packageversion() : data (&defaultversion) -{ - ++data->references; -} - -/* Create from an actual package */ -packageversion::packageversion (_packageversion *pkg) -{ - if (pkg) - data = pkg; - else - data = &defaultversion; - ++data->references; -} - -packageversion::packageversion (packageversion const &existing) : -data(existing.data) -{ - ++data->references; -} - -packageversion::~packageversion() -{ - if (--data->references == 0) - delete data; -} - -packageversion & -packageversion::operator= (packageversion const &rhs) -{ - ++rhs.data->references; - if (--data->references == 0) - delete data; - data = rhs.data; - return *this; -} - -bool -packageversion::operator ! () const -{ - return !data->Name().size(); -} - -packageversion::operator bool () const -{ - return data->Name().size(); -} - -bool -packageversion::operator == (packageversion const &rhs) const -{ - if (this == &rhs || data == rhs.data) - return true; - else - return data->Name () == rhs.data->Name() && data->Canonical_version () == rhs.data->Canonical_version(); -} - -bool -packageversion::operator != (packageversion const &rhs) const -{ - return ! (*this == rhs); -} - -bool -packageversion::operator < (packageversion const &rhs) const -{ - int t = casecompare(data->Name(), rhs.data->Name()); - if (t < 0) - return true; - else if (t > 0) - return false; - else if (casecompare (data->Canonical_version(), rhs.data->Canonical_version()) < 0) - return true; - return false; -} - -const std::string -packageversion::Name () const -{ - return data->Name (); -} - -const std::string -packageversion::Vendor_version() const -{ - return data->Vendor_version(); -} - -const std::string -packageversion::Package_version() const -{ - return data->Package_version(); -} - -const std::string -packageversion::Canonical_version() const -{ - return data->Canonical_version(); -} - -void -packageversion::setCanonicalVersion (const std::string& ver) -{ - data->setCanonicalVersion (ver); -} - -package_type_t -packageversion::Type () const -{ - return data->Type (); -} - -const std::string -packageversion::SDesc () const -{ - return data->SDesc (); -} - -void -packageversion::set_sdesc (const std::string& sdesc) -{ - data->set_sdesc (sdesc); -} - -const std::string -packageversion::LDesc () const -{ - return data->LDesc (); -} - -void -packageversion::set_ldesc (const std::string& ldesc) -{ - data->set_ldesc (ldesc); -} - -packageversion -packageversion::sourcePackage() const -{ - return data->sourcePackage(); -} - -PackageSpecification & -packageversion::sourcePackageSpecification () const -{ - return data->sourcePackageSpecification (); -} - -void -packageversion::setSourcePackageSpecification (PackageSpecification const &spec) -{ - data->setSourcePackageSpecification(spec); -} - -PackageDepends * -packageversion::depends() -{ - return &data->depends; -} - -const PackageDepends * -packageversion::depends() const -{ - return &data->depends; -} - -packagesource * -packageversion::source () const -{ - return &data->source; -} - -bool -packageversion::accessible() const -{ - return data->accessible(); -} - - -int -packageversion::compareVersions(const packageversion &a, const packageversion &b) -{ - /* Compare Vendor_version */ - int comparison = version_compare(a.Vendor_version(), b.Vendor_version()); - -#if DEBUG - Log (LOG_BABBLE) << "vendor version comparison " << a.Vendor_version() << " and " << b.Vendor_version() << ", result was " << comparison << endLog; -#endif - - if (comparison != 0) - { - return comparison; - } - - /* Vendor_version are tied, compare Package_version */ -#if DEBUG - Log (LOG_BABBLE) << "package version comparison " << a.Package_version() << " and " << b.Package_version() << ", result was " << comparison << endLog; -#endif - - comparison = version_compare(a.Package_version(), b.Package_version()); - return comparison; -} - -/* the parent data class */ - -_packageversion::_packageversion ():references (0) -{ -} - -_packageversion::~_packageversion () -{ -} - -PackageSpecification & -_packageversion::sourcePackageSpecification () -{ - return _sourcePackage; -} - -void -_packageversion::setSourcePackageSpecification (PackageSpecification const &spec) -{ - _sourcePackage = spec; -} - -packageversion -_packageversion::sourcePackage () -{ - if (!sourceVersion) - { - packagedb db; - packagemeta * pkg; - pkg = db.findSource (_sourcePackage); - /* no valid source meta available, just return the default - (blank) package version - */ - if (!pkg) - return sourceVersion; - set<packageversion>::iterator i=pkg->versions.begin(); - while (i != pkg->versions.end()) - { - packageversion const & ver = * i; - if (_sourcePackage.satisfies (ver)) - sourceVersion = ver; - ++i; - } - } - return sourceVersion; -} - -// is archive accessible -bool -_packageversion::accessible() const -{ - // cached ? - if (source.Cached ()) - return true; - // net access allowed? - if (::source == IDC_SOURCE_LOCALDIR) - return false; - // retrievable ? - if (source.sites.size() || source.Cached ()) - return true; - // otherwise, not accessible - return false; -} diff --git a/package_version.h b/package_version.h index 90d576d..43cf146 100644 --- a/package_version.h +++ b/package_version.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2003 Robert Collins. + * Copyright (c) 2017 Jon Turney * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -9,165 +9,13 @@ * A copy of the GNU General Public License can be found at * http://www.gnu.org/ * - * Written by Robert Collins <rbtcollins@hotmail.com> - * - */ - -#ifndef SETUP_PACKAGE_VERSION_H -#define SETUP_PACKAGE_VERSION_H - -/* This is a package version abstrct class, that should be able to - * arbitrate acceess to cygwin binary packages, cygwin source package, - * and the rpm and deb equivalents of the same. - */ - -/* standard binary package metadata: - * Name (ie mutt - * Vendor Version (ie 2.5.1) - * Package Version (ie 16) - * Stability - * Files */ -/* For non installed files, this class can be populated via information about - * what is available on the net, or by parsing a specific package file. - * for installed packages, this class should represent what is currently installed, - * - updated by what net metadata has about it. - * i.e. the stability of this version will change simply because the net mirrors - * now consider it old. - */ - -class CategoryList; - -/*Required for parsing */ -#include "package_source.h" -#include "PackageSpecification.h" -#include "PackageTrust.h" -#include "package_depends.h" - -typedef enum -{ - package_invalid, - package_old, - package_current, - package_experimental -} -package_stability_t; - -typedef enum -{ - package_binary, - package_source -} -package_type_t; - -/* A wrapper class to be copied by value that - references the same package. - Nothing is virtual, because the wrapper cannot be inherited. - However, as all the methods are implemented in the referenced - _packageversion, that class allows virtual overriding. - */ - -class _packageversion; -class packagemeta; - -/* This class has pointer semantics - Specifically: a=b does not alter the value of *a. - */ -class packageversion -{ -public: - packageversion (); /* creates an empty packageversion */ - packageversion (_packageversion *); /* used when creating an instance */ - packageversion (packageversion const &); - ~packageversion (); - packageversion &operator= (packageversion const &); - bool operator ! () const; /* true if the package is invalid. (i.e. - uninitialised */ - operator bool () const; /* returns ! !() */ - bool operator == (packageversion const &) const; /* equality */ - bool operator != (packageversion const &) const; - bool operator < (packageversion const &) const; - bool operator <= (packageversion const &) const; - bool operator > (packageversion const &) const; - bool operator >= (packageversion const &) const; - - const std::string Name () const; - const std::string Vendor_version () const; - const std::string Package_version () const; - const std::string Canonical_version () const; - void setCanonicalVersion (const std::string& ); - package_type_t Type () const; - const std::string SDesc () const; - void set_sdesc (const std::string& ); - const std::string LDesc () const; - void set_ldesc (const std::string& ); - packageversion sourcePackage () const; - PackageSpecification & sourcePackageSpecification () const; - void setSourcePackageSpecification (PackageSpecification const &); - - /* invariant: these never return NULL */ - PackageDepends *depends(); - const PackageDepends *depends() const; - - /* invariant: never null */ - packagesource *source() const; /* where can we source the file from */ - - bool accessible () const; - - /* ensure that the depends clause is satisfied */ - int set_requirements (trusts deftrust, size_t depth = 0); - - /* utility function to compare package versions */ - static int compareVersions(const packageversion &a, const packageversion &b); - -private: - _packageversion *data; /* Invariant: * data is always valid */ -}; - -class _packageversion -{ -public: - _packageversion(); - virtual ~_packageversion(); - /* for list inserts/mgmt. */ - std::string key; - /* name is needed here, because if we are querying a file, the data may be embedded in - the file */ - virtual const std::string Name () = 0; - virtual const std::string Vendor_version () = 0; - virtual const std::string Package_version () = 0; - virtual const std::string Canonical_version () = 0; - virtual void setCanonicalVersion (const std::string& ) = 0; -// virtual package_stability_t Stability () = 0; - virtual package_type_t Type () = 0; - virtual const std::string SDesc () = 0; - virtual void set_sdesc (const std::string& ) = 0; - virtual const std::string LDesc () = 0; - virtual void set_ldesc (const std::string& ) = 0; - /* only semantically meaningful for binary packages */ - /* direct link to the source package for this binary */ - /* if multiple versions exist and the source doesn't discriminate - then the most recent is used - */ - virtual packageversion sourcePackage (); - virtual PackageSpecification & sourcePackageSpecification (); - virtual void setSourcePackageSpecification (PackageSpecification const &); - - PackageDepends depends; - - packagesource source; /* where can we source the file from */ +#ifndef PACKAGE_VERSION_H +#define PACKAGE_VERSION_H - virtual bool accessible () const; +#include "libsolv.h" - /* TODO: Implement me: - static package_meta * scan_package (io_stream *); - */ - size_t references; -protected: - /* only meaningful for binary packages */ - PackageSpecification _sourcePackage; - packageversion sourceVersion; -}; +typedef SolvableVersion packageversion; -#endif /* SETUP_PACKAGE_VERSION_H */ +#endif // PACKAGE_VERSION_H diff --git a/prereq.cc b/prereq.cc index 0a46ad1..8fcd3ba 100644 --- a/prereq.cc +++ b/prereq.cc @@ -13,27 +13,16 @@ * */ -#include "win32.h" -#include <commctrl.h> -#include <stdio.h> -#include <io.h> -#include <ctype.h> -#include <process.h> -#include <queue> - #include "prereq.h" -#include "dialog.h" #include "resource.h" #include "state.h" -#include "propsheet.h" #include "threebar.h" -#include "Generic.h" #include "LogSingleton.h" #include "ControlAdjuster.h" #include "package_db.h" -#include "package_meta.h" -#include "msg.h" + #include "Exception.h" +#include "getopt++/BoolOption.h" // Sizing information. static ControlAdjuster::ControlInfo PrereqControlsInfo[] = { @@ -43,6 +32,7 @@ static ControlAdjuster::ControlInfo PrereqControlsInfo[] = { }; extern ThreeBarProgressPage Progress; +BoolOption IncludeSource (false, 'I', "include-source", "Automatically install source for every package installed"); // --------------------------------------------------------------------------- // implements class PrereqPage @@ -73,7 +63,7 @@ void PrereqPage::OnActivate() { // if we have gotten this far, then PrereqChecker has already run isMet - // and found that there were missing packages; so we can just call + // and found that there were problems; so we can just call // getUnmetString to format the results and display it std::string s; @@ -88,53 +78,50 @@ long PrereqPage::OnNext () { HWND h = GetHWND (); + packagedb db; if (!IsDlgButtonChecked (h, IDC_PREREQ_CHECK)) { // breakage imminent! danger, danger int res = MessageBox (h, - "The listed packages are required for packages depending on them to " - "work. We strongly recommend that you allow Setup to select them." + "Some packages may not work properly if you continue." "\r\n\r\n" - "Are you sure you want to proceed without these packages?", - "WARNING - Required Packages Not Selected", + "Are you sure you want to proceed (NOT RECOMMENDED)?", + "WARNING - Unsolved Problems", MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2); if (res == IDNO) return -1; else - Log (LOG_PLAIN) << - "NOTE! User refused suggested missing dependencies! " + { + Log (LOG_PLAIN) << + "NOTE! User continued with unsolved problems! " "Expect some packages to give errors or not function at all." << endLog; + // Change the solver's transaction list to reflect the user's choices. + db.solution.db2trans(); + } } else { - // add the missing requirements - PrereqChecker p; - p.selectMissing (); + db.solution.applyDefaultProblemSolutions(); } - return whatNext(); -} + PrereqChecker p; + p.finalize(); -long -PrereqPage::whatNext () -{ - if (source == IDC_SOURCE_LOCALDIR) - { - // Next, install - Progress.SetActivateTask (WM_APP_START_INSTALL); - } - else - { - // Next, start download from internet - Progress.SetActivateTask (WM_APP_START_DOWNLOAD); - } - return IDD_INSTATUS; + return IDD_CONFIRM; } long PrereqPage::OnBack () { + // Add reinstall tasks + PrereqChecker p; + p.augment(); + + // Reset the package database to correspond to the solver's solution + packagedb db; + db.solution.trans2db(); + return IDD_CHOOSE; } @@ -145,11 +132,7 @@ PrereqPage::OnUnattended () if (unattended_mode == chooseronly) return -1; - // in unattended mode, add the missing requirements, then carry on to download/install - PrereqChecker p; - p.selectMissing (); - - return whatNext(); + return IDD_CONFIRM; } // --------------------------------------------------------------------------- @@ -157,170 +140,55 @@ PrereqPage::OnUnattended () // --------------------------------------------------------------------------- // instantiate the static members -map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> PrereqChecker::unmet; -map <std::string, vector <packagemeta *> > PrereqChecker::notfound; -trusts PrereqChecker::theTrust = TRUST_CURR; +bool PrereqChecker::use_test_packages; +SolverTasks PrereqChecker::q; -/* This function builds a list of unmet dependencies to present to the user on - the PrereqPage propsheet. - - The data is stored in two associative maps: - unmet[package] = vector of packages that depend on package. - notfound[package-name] = vector of packages that depend on package. -*/ bool PrereqChecker::isMet () { packagedb db; - Progress.SetText1 ("Checking prerequisites..."); + Progress.SetText1 ("Solving dependencies..."); Progress.SetText2 (""); Progress.SetText3 (""); - // clear static data each time this is called - unmet.clear (); - notfound.clear (); - - // packages that need to be checked for dependencies - queue <packagemeta *> todo; - - // go through all packages, adding desired ones to the initial work list - for (packagedb::packagecollection::iterator p = db.packages.begin (); - p != db.packages.end (); ++p) - { - if (p->second->desired) - todo.push (p->second); - } - - int max = todo.size(); - int pos = 0; + // Create task list corresponding to current state of package database + q.setTasks(); - // churn through the work list - while (!todo.empty ()) - { - // get the first package off the work list - packagemeta *pack = todo.front (); - todo.pop (); - - pos++; - Progress.SetText2 (pack->name.c_str()); - static char buf[100]; - sprintf(buf, "%d %% (%d/%d)", pos * 100 / max, pos, max); - Progress.SetText3(buf); - Progress.SetBar1(pos, max); - - // Fetch the dependencies of the package. This assumes that the - // dependencies of all versions are all the same. - const PackageDepends *deps = pack->curr.depends (); - - // go through the package's dependencies - for (PackageDepends::const_iterator d = - deps->begin (); d != deps->end (); ++d) - { - PackageSpecification *dep_spec = *d; - packagemeta *dep = db.findBinary (*dep_spec); - - if (dep) - { - if (!(dep->desired && dep_spec->satisfies (dep->desired))) - { - // we've got an unmet dependency - if (unmet.find (dep) == unmet.end ()) - { - // newly found dependency: add to worklist - todo.push (dep); - max++; - } - unmet[dep].push_back (pack); - } - } - else - { - // dependency on a package which doesn't have any binary versions - // (i.e. it is source only or doesn't exist) - Log (LOG_PLAIN) << "package " << pack->name << " has dependency " - << dep_spec->packageName() << " we can't find" << endLog; - notfound[dep_spec->packageName()].push_back (pack); - } - } - } - - return unmet.empty () && notfound.empty (); + // apply solver to those tasks and global state (use test or not) + return db.solution.update(q, SolverSolution::keep, use_test_packages); } -/* Formats 'unmet' as a string for display to the user. */ void -PrereqChecker::getUnmetString (std::string &s) +PrereqChecker::finalize () { - s = ""; + augment(); - { - map <std::string, vector <packagemeta *> >::iterator i; - for (i = notfound.begin(); i != notfound.end(); i++) - { - s = s + i->first - + "\t(not found)" - + "\r\n\tRequired by: "; - for (unsigned int j = 0; j < i->second.size(); j++) - { - s += i->second[j]->name; - if (j != i->second.size() - 1) - s += ", "; - } - s += "\r\n\r\n"; - } - } - - map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i; - for (i = unmet.begin(); i != unmet.end(); i++) - { - s = s + i->first->name - + "\t(" + i->first->trustp (false, theTrust).Canonical_version () - + ")\r\n\t" + i->first->SDesc () - + "\r\n\tRequired by: "; - for (unsigned int j = 0; j < i->second.size(); j++) - { - s += i->second[j]->name; - if (j != i->second.size() - 1) - s += ", "; - } - s += "\r\n\r\n"; - } + packagedb db; + db.solution.addSource(IncludeSource); + db.solution.dumpTransactionList(); } -/* Takes the keys of 'unmet' and selects them, using the current trust. */ void -PrereqChecker::selectMissing () +PrereqChecker::augment () { packagedb db; + db.solution.augmentTasks(q); +} - // provide a default, even though this should have been set for us - if (!theTrust) - theTrust = TRUST_CURR; +/* Formats problems and solutions as a string for display to the user. */ +void +PrereqChecker::getUnmetString (std::string &s) +{ + packagedb db; + s = db.solution.report(); - // get each of the keys of 'unmet' - map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i; - for (i = unmet.begin(); i != unmet.end(); i++) + // + size_t pos = 0; + while ((pos = s.find("\n", pos)) != std::string::npos) { - packagemeta *pkg = i->first; - packageversion vers = pkg->trustp (false, theTrust); - pkg->desired = vers; - pkg->srcpick (false); - - if (vers == i->first->installed) - { - pkg->pick (false); - Log (LOG_PLAIN) << "Adding required dependency " << i->first->name << - ": Selecting already-installed version " << - i->first->installed.Canonical_version () << "." << endLog; - } - else - { - pkg->pick (vers.accessible ()); - Log (LOG_PLAIN) << "Adding required dependency " << i->first->name << - ": Selecting version " << vers.Canonical_version () << - " for installation." << endLog; - } + s.replace(pos, 1, "\r\n"); + pos += 2; } } @@ -336,11 +204,8 @@ do_prereq_check_thread(HINSTANCE h, HWND owner) if (p.isMet ()) { - if (source == IDC_SOURCE_LOCALDIR) - Progress.SetActivateTask (WM_APP_START_INSTALL); // install - else - Progress.SetActivateTask (WM_APP_START_DOWNLOAD); // start download - retval = IDD_INSTATUS; + p.finalize(); + retval = IDD_CONFIRM; } else { diff --git a/prereq.h b/prereq.h index 163af6e..749d3eb 100644 --- a/prereq.h +++ b/prereq.h @@ -27,32 +27,27 @@ public: virtual long OnNext (); virtual long OnBack (); virtual long OnUnattended (); -private: - long whatNext (); }; class PrereqChecker { public: - // checks all dependecies, populates 'unmet' - // returns true if unsatisfied dependencies exist + // returns true if no dependency problems exist bool isMet (); - + // formats 'unmet' as a string for display void getUnmetString (std::string &s); - - // selects/picks the needed packages that were missing - void selectMissing (); - - // notes the current trust (for use in selectMissing) - static void setTrust (trusts t) { theTrust = t; }; + + // finialize the transaction list + void finalize (); + + void augment (); + + static void setTestPackages (bool t) { use_test_packages = t; }; private: - - // this is the actual hash_map that does all the work - static map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> unmet; - static map <std::string, vector <packagemeta *> > notfound; - static trusts theTrust; + static bool use_test_packages; + static SolverTasks q; }; #endif /* SETUP_PREREQ_H */ diff --git a/res.rc b/res.rc index 5b7d239..745b396 100644 --- a/res.rc +++ b/res.rc @@ -315,8 +315,9 @@ END // Right-aligned controls. #define SETUP_EXP_X (SETUP_STANDARD_DIALOG_W - SETUP_KPCE_W - 7) -#define SETUP_CURR_X (SETUP_EXP_X - SETUP_KPCE_W - 5) -#define SETUP_KEEP_X (SETUP_CURR_X - SETUP_KPCE_W - 5) +#define SETUP_SYNC_X (SETUP_EXP_X - SETUP_KPCE_W - 5) +#define SETUP_BEST_X (SETUP_SYNC_X - SETUP_KPCE_W - 5) +#define SETUP_KEEP_X (SETUP_BEST_X - SETUP_KPCE_W - 5) // Left-aligned controls. #define SETUP_VIEW_X (7) @@ -350,9 +351,11 @@ BEGIN SETUP_CLEAR_W, 14 CONTROL "&Keep", IDC_CHOOSE_KEEP, "Button", BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, SETUP_KEEP_X, 30, SETUP_KPCE_W, 14 - CONTROL "C&urrent", IDC_CHOOSE_CURR, "Button", BS_AUTORADIOBUTTON, - SETUP_CURR_X, 30, SETUP_KPCE_W, 14 - CONTROL "&Test", IDC_CHOOSE_EXP, "Button", BS_AUTORADIOBUTTON, + CONTROL "&Best", IDC_CHOOSE_BEST, "Button", BS_AUTORADIOBUTTON, + SETUP_BEST_X, 30, SETUP_KPCE_W, 14 + CONTROL "&Sync", IDC_CHOOSE_SYNC, "Button", BS_AUTORADIOBUTTON, + SETUP_SYNC_X, 30, SETUP_KPCE_W, 14 + CONTROL "&Test", IDC_CHOOSE_EXP, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, SETUP_EXP_X, 30, SETUP_KPCE_W, 14 CONTROL "", IDC_HEADSEPARATOR, "Static", SS_BLACKFRAME | SS_SUNKEN, 0, 28, SETUP_STANDARD_DIALOG_W, 1 @@ -378,9 +381,9 @@ BEGIN ICON IDI_CYGWIN,IDC_HEADICON,SETUP_HEADICON_X,0,21,20 LTEXT "Resolving Dependencies",IDC_STATIC_HEADER_TITLE ,7,0,258,8,NOT WS_GROUP - LTEXT "The following packages are required to satisfy " + LTEXT "The following problems occured trying to satisfy " "dependencies.",IDC_STATIC,21,9,239,16,NOT WS_GROUP - CONTROL "&Select required packages (RECOMMENDED)" + CONTROL "&Accept default problem solutions" ,IDC_PREREQ_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, 7,167,225,14 EDITTEXT IDC_PREREQ_EDIT,7,41,303,124,WS_VSCROLL | WS_HSCROLL | @@ -389,6 +392,22 @@ BEGIN END +IDD_CONFIRM DIALOG DISCARDABLE 0, 0, SETUP_STANDARD_DIALOG_DIMS +STYLE DS_MODALFRAME | DS_3DLOOK | WS_CHILD | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Cygwin Setup - Review and confirm changes" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "",IDC_HEADSEPARATOR,"Static",SS_BLACKFRAME | SS_SUNKEN, + 0,28,SETUP_STANDARD_DIALOG_W,1 + ICON IDI_CYGWIN,IDC_HEADICON,SETUP_HEADICON_X,0,21,20 + LTEXT "Review and confirm changes",IDC_STATIC_HEADER_TITLE + ,7,0,258,8,NOT WS_GROUP + EDITTEXT IDC_CONFIRM_EDIT,7,41,325,131,WS_VSCROLL | WS_HSCROLL | + ES_LEFT | ES_MULTILINE | ES_READONLY | ES_AUTOHSCROLL | + ES_AUTOVSCROLL +END + IDD_DROPPED DIALOG DISCARDABLE 0, 0, SETUP_STANDARD_DIALOG_W, 142 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Cygwin Setup - Use dropped mirrors?" @@ -558,10 +577,11 @@ BEGIN IDS_TRUSTKEEP_TOOLTIP "Sets all packages to their currently installed " "version. This is equivalent to telling setup not to make any " "changes to any package." - IDS_TRUSTCURR_TOOLTIP "Globally select the version that is currently " - "considered the most stable. (RECOMMENDED)" - IDS_TRUSTEXP_TOOLTIP "Globally select the most recent version, even if " - "that version is considered experimental or for test use by the maintainer." + IDS_TRUSTCURR_TOOLTIP "Sets all packages to the best version available. " + "(RECOMMENDED)" + IDS_TRUSTSYNC_TOOLTIP "Sets all packages to the version available from the " + "package respository, downgrading if necessary." + IDS_TRUSTEXP_TOOLTIP "Enable test packages." IDS_VIEWBUTTON_TOOLTIP "Select the package view. This determines " "which packages are shown below.\r\n" "\r\n" diff --git a/resource.h b/resource.h index 70d90ca..31e080f 100644 --- a/resource.h +++ b/resource.h @@ -40,6 +40,7 @@ #define IDS_INSTALLEDB_VERSION 140 #define IDS_DOWNLOAD_INCOMPLETE_EXIT 141 #define IDS_QUERY_CORRUPT 142 +#define IDS_TRUSTSYNC_TOOLTIP 143 // Dialogs @@ -68,6 +69,7 @@ #define IDD_POSTINSTALL 222 #define IDD_FILE_INUSE 223 #define IDD_DOWNLOAD_ERROR 224 +#define IDD_CONFIRM 225 // Bitmaps @@ -120,7 +122,7 @@ #define IDC_LISTVIEW_POS 530 #define IDC_CHOOSE_VIEW 531 #define IDC_CHOOSE_EXP 532 -#define IDC_CHOOSE_CURR 533 +#define IDC_CHOOSE_BEST 533 #define IDC_CHOOSE_LIST 535 #define IDC_INS_ACTION 536 #define IDC_ROOT_DESKTOP 537 @@ -179,3 +181,5 @@ #define IDC_NET_DIRECT_LEGACY 593 #define IDC_DOWNLOAD_EDIT 594 #define IDC_CHOOSE_DO_SEARCH 595 +#define IDC_CHOOSE_SYNC 596 +#define IDC_CONFIRM_EDIT 597