Suggest URLs for updated setup based on build architecture
[cygwin-setup.git] / package_meta.cc
blob3daa9700944985ca2f87e27be687704a9cab92b4
1 /*
2 * Copyright (c) 2001, 2003 Robert Collins.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
12 * Written by Robert Collins <rbtcollins@hotmail.com>
16 #include "package_meta.h"
18 #include <string>
19 #include <set>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <strings.h>
25 #include "getopt++/StringArrayOption.h"
27 #include "io_stream.h"
28 #include "compress.h"
30 #include "filemanip.h"
31 #include "LogSingleton.h"
32 /* io_stream needs a bit of tweaking to get rid of this. TODO */
33 #include "mount.h"
34 /* this goes at the same time */
35 #include "win32.h"
37 #include "script.h"
38 #include "package_db.h"
40 #include <algorithm>
41 #include <functional>
43 #include "Generic.h"
44 #include "download.h"
45 #include "Exception.h"
46 #include "resource.h"
48 static StringArrayOption DeletePackageOption ('x', "remove-packages", IDS_HELPTEXT_REMOVE_PACKAGES);
49 static StringArrayOption DeleteCategoryOption ('c', "remove-categories", IDS_HELPTEXT_REMOVE_CATEGORIES);
50 static StringArrayOption PackageOption ('P', "packages", IDS_HELPTEXT_PACKAGES);
51 static StringArrayOption CategoryOption ('C', "categories", IDS_HELPTEXT_CATEGORIES);
52 bool hasManualSelections = 0;
54 /*****************/
56 /* Return an appropriate category caption given the action */
57 unsigned int
58 packagemeta::action_caption (_actions _value)
60 switch (_value)
62 case NoChange_action:
63 return IDS_ACTION_DEFAULT;
64 case Install_action:
65 return IDS_ACTION_INSTALL;
66 case Reinstall_action:
67 return IDS_ACTION_REINSTALL;
68 case Uninstall_action:
69 return IDS_ACTION_UNINSTALL;
72 return IDS_ACTION_UNKNOWN;
75 packagemeta::packagemeta (packagemeta const &rhs) :
76 name (rhs.name),
77 categories (rhs.categories), versions (rhs.versions),
78 installed (rhs.installed),
79 curr (rhs.curr),
80 exp (rhs.exp),
81 desired (rhs.desired)
86 template<class T> struct removeCategory
88 removeCategory(packagemeta *pkg) : _pkg (pkg) {}
89 void operator() (T x)
91 std::vector <packagemeta *> &aList = packagedb::categories[x];
92 aList.erase (find (aList.begin(), aList.end(), _pkg));
94 packagemeta *_pkg;
98 packagemeta::~packagemeta()
100 for_each (categories.begin (), categories.end (), removeCategory<std::string> (this));
101 categories.clear ();
102 versions.clear ();
105 SolvableVersion
106 packagemeta::add_version (const SolverPool::addPackageData &inpkgdata)
108 SolverPool::addPackageData pkgdata = inpkgdata;
110 packageversion *v = NULL;
111 switch (pkgdata.stability)
113 case TRUST_CURR:
114 v = &(this->curr);
115 break;
116 case TRUST_TEST:
117 v = &(this->exp);
118 break;
119 default:
120 break;
124 If a packageversion for the same version number is already present, allow
125 this version to replace it.
127 There is a problem where multiple repos provide a package. It's never been
128 clear which repo should win. With this implementation, the last one added
129 will win.
131 We rely on this by adding packages from installed.db last.
134 for (std::set <packageversion>::iterator i = versions.begin();
135 i != versions.end();
136 i++)
138 if (i->Canonical_version() != pkgdata.version)
139 continue;
141 if (pkgdata.vendor == i->Vendor())
143 /* Merge the site-list from any existing packageversion with the same
144 repository 'release:' label */
145 pkgdata.archive.sites.insert(pkgdata.archive.sites.end(),
146 i->source()->sites.begin(),
147 i->source()->sites.end());
149 /* Installed packages do not supersede repo packages */
150 if (pkgdata.reponame != "_installed")
152 /* Ensure a stability level doesn't point to a version we're about
153 to remove */
154 if (v && (*v == *i))
155 *v = packageversion();
157 i->remove();
160 else
162 /* Otherwise... if we had a way to set repo priorities, that could be
163 used to control which packageversion the solver picks. For the
164 moment, just warn that you might not be getting what you think you
165 should...
167 (suppress this for installed packages, as we are only guessing the
168 vendor, currently)
170 if (pkgdata.reponame != "_installed")
172 Log (LOG_PLAIN) << "Version " << pkgdata.version << " of package " <<
173 name << " is present in releases labelled " << pkgdata.vendor <<
174 " and " << i->Vendor() << endLog;
178 versions.erase(i);
180 break;
183 /* Create the SolvableVersion */
184 packagedb db;
185 SolvableVersion thepkg = db.solver.addPackage(name, pkgdata);
187 /* Add the version */
188 std::pair<std::set <packageversion>::iterator, bool> result = versions.insert (thepkg);
190 if (!result.second)
191 Log (LOG_PLAIN) << "Failed to add version " << thepkg.Canonical_version() << " in package " << name << endLog;
192 #ifdef DEBUG
193 else
194 Log (LOG_PLAIN) << "Added version " << thepkg.Canonical_version() << " in package " << name << endLog;
195 #endif
197 /* Record the highest version at a given stability level */
198 if (v)
200 /* Any version is always greater than no version */
201 int comparison = 1;
202 if (*v)
203 comparison = SolvableVersion::compareVersions(thepkg, *v);
205 #ifdef DEBUG
206 if ((bool)(*v))
207 Log (LOG_BABBLE) << "package " << thepkg.Name() << " comparing versions " << thepkg.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog;
208 #endif
210 if (comparison >= 0)
212 *v = thepkg;
216 return thepkg;
219 const packageversion *
220 packagemeta::findVersion(std::string &version) const
222 for (std::set <packageversion>::iterator i = versions.begin();
223 i != versions.end();
224 i++)
226 if (i->Canonical_version() == version)
227 return &(*i);
230 return NULL;
233 bool
234 packagemeta::isBlacklisted(const packageversion &version) const
236 for (std::set<std::string>::iterator i = version_blacklist.begin();
237 i != version_blacklist.end();
238 i++)
240 if (i->compare(version.Canonical_version()) == 0)
241 return true;
244 return false;
247 void
248 packagemeta::set_installed_version (const std::string &version)
250 for (std::set<packageversion>::iterator i = versions.begin(); i != versions.end(); i++)
252 if (version.compare(i->Canonical_version()) == 0)
254 installed = *i;
256 /* and mark as Keep */
257 desired = installed;
262 void
263 packagemeta::add_category (const std::string& cat)
265 if (categories.find (cat) != categories.end())
266 return;
267 /* add a new record for the package list */
268 packagedb::categories[cat].push_back (this);
269 categories.insert (cat);
272 struct StringConcatenator {
273 StringConcatenator(std::string aString) : gap(aString){}
274 void operator()(const std::string& aString)
276 if (result.size() != 0)
277 result += gap;
278 result += aString;
280 std::string result;
281 std::string gap;
283 typedef const std::string argument_type;
286 const std::string
287 packagemeta::getReadableCategoryList () const
289 return for_each(categories.begin(), categories.end(),
290 visit_if (
291 StringConcatenator(", "),
292 std::bind(std::not_equal_to<std::string>(), "All", std::placeholders::_1))
293 ).visitor.result;
296 static void
297 parseNames (std::set<std::string> &parsed, std::string &option)
299 std::string tname;
301 /* Split up the packages listed in the option. */
302 std::string::size_type loc = option.find (",", 0);
303 while (loc != std::string::npos)
305 tname = option.substr (0, loc);
306 option = option.substr (loc + 1);
307 parsed.insert (tname);
308 loc = option.find (",", 0);
311 /* At this point, no "," exists in option. Don't add
312 an empty string if the entire option was empty. */
313 if (option.length ())
314 parsed.insert (option);
317 static void
318 validatePackageNames (std::set<std::string> &names)
320 packagedb db;
321 for (std::set<std::string>::iterator n = names.begin();
322 n != names.end();
323 ++n)
325 if (db.packages.find(*n) == db.packages.end())
327 Log(LOG_PLAIN) << "Package '" << *n << "' not found." << endLog;
332 bool packagemeta::isManuallyWanted(packageversion &version) const
334 static bool parsed_yet = false;
335 static std::map<std::string, std::string> parsed_names;
336 hasManualSelections |= parsed_names.size ();
337 static std::set<std::string> parsed_categories;
338 hasManualSelections |= parsed_categories.size ();
339 bool bReturn = false;
341 /* First time through, we parse all the names out from the
342 option string and store them away in an STL set. */
343 if (!parsed_yet)
345 std::vector<std::string> packages_options = PackageOption;
346 std::vector<std::string> categories_options = CategoryOption;
348 std::set<std::string> items;
349 for (std::vector<std::string>::iterator n = packages_options.begin ();
350 n != packages_options.end (); ++n)
352 parseNames (items, *n);
355 std::set<std::string> packages;
356 /* Separate any 'package=version' into package and version parts */
357 for (std::set<std::string>::iterator n = items.begin();
358 n != items.end();
359 ++n)
361 std::string package;
362 std::string version;
363 std::string::size_type loc = n->find ("=", 0);
364 if (loc != std::string::npos)
366 package = n->substr(0, loc);
367 version = n->substr(loc+1);
369 else
371 package = *n;
372 version = "";
374 Log (LOG_BABBLE) << "package: " << package << " version: " << version << endLog;
375 parsed_names[package] = version;
376 packages.insert(package);
379 validatePackageNames (packages);
381 for (std::vector<std::string>::iterator n = categories_options.begin ();
382 n != categories_options.end (); ++n)
384 parseNames (parsed_categories, *n);
386 parsed_yet = true;
389 /* Once we've already parsed the option string, just do
390 a lookup in the cache of already-parsed names. */
391 std::map<std::string, std::string>::iterator i = parsed_names.find(name);
392 if (i != parsed_names.end())
394 bReturn = true;
396 /* Wanted version is unspecified */
397 version = packageversion();
399 /* ... unless a version was explicitly specified */
400 std::string v = i->second;
401 if (!v.empty())
403 const packageversion *pv = findVersion(v);
404 if (pv)
405 version = *pv;
406 else
407 Log (LOG_PLAIN) << "package: " << name << " version: " << v << " not found" << endLog;
411 /* If we didn't select the package manually, did we select any
412 of the categories it is in? */
413 if (!bReturn && parsed_categories.size ())
415 std::set<std::string, casecompare_lt_op>::iterator curcat;
416 for (curcat = categories.begin (); curcat != categories.end (); curcat++)
417 if (parsed_categories.find (*curcat) != parsed_categories.end ())
419 Log (LOG_BABBLE) << "Found category " << *curcat << " in package " << name << endLog;
420 version = packageversion();
421 bReturn = true;
425 if (bReturn)
426 Log (LOG_BABBLE) << "Added manual package " << name << endLog;
427 return bReturn;
430 bool packagemeta::isManuallyDeleted() const
432 static bool parsed_yet = false;
433 static std::set<std::string> parsed_delete;
434 hasManualSelections |= parsed_delete.size ();
435 static std::set<std::string> parsed_delete_categories;
436 hasManualSelections |= parsed_delete_categories.size ();
437 bool bReturn = false;
439 /* First time through, we parse all the names out from the
440 option string and store them away in an STL set. */
441 if (!parsed_yet)
443 std::vector<std::string> delete_options = DeletePackageOption;
444 std::vector<std::string> categories_options = DeleteCategoryOption;
445 for (std::vector<std::string>::iterator n = delete_options.begin ();
446 n != delete_options.end (); ++n)
448 parseNames (parsed_delete, *n);
450 validatePackageNames (parsed_delete);
451 for (std::vector<std::string>::iterator n = categories_options.begin ();
452 n != categories_options.end (); ++n)
454 parseNames (parsed_delete_categories, *n);
456 parsed_yet = true;
459 /* Once we've already parsed the option string, just do
460 a lookup in the cache of already-parsed names. */
461 bReturn = parsed_delete.find(name) != parsed_delete.end();
463 /* If we didn't select the package manually, did we select any
464 of the categories it is in? */
465 if (!bReturn && parsed_delete_categories.size ())
467 std::set<std::string, casecompare_lt_op>::iterator curcat;
468 for (curcat = categories.begin (); curcat != categories.end (); curcat++)
469 if (parsed_delete_categories.find (*curcat) != parsed_delete_categories.end ())
471 Log (LOG_BABBLE) << "Found category " << *curcat << " in package " << name << endLog;
472 bReturn = true;
476 if (bReturn)
477 Log (LOG_BABBLE) << "Deleted manual package " << name << endLog;
478 return bReturn;
481 const std::string
482 packagemeta::SDesc () const
484 for (std::set<packageversion>::iterator i = versions.begin(); i != versions.end(); i++)
486 if (i->SDesc().size())
487 return i->SDesc ();
490 return std::string();
493 static bool
494 hasLDesc(packageversion const &pkg)
496 return pkg.LDesc().size();
499 const std::string
500 packagemeta::LDesc () const
502 std::set<packageversion>::iterator i = find_if (versions.begin(), versions.end(), hasLDesc);
503 if (i == versions.end())
504 return std::string();
505 return i->LDesc ();
508 /* Return an appropriate caption given the current action. */
509 std::wstring
510 packagemeta::action_caption () const
512 switch (_action)
514 case Uninstall_action:
515 return LoadStringW(IDS_ACTION_UNINSTALL);
516 case NoChange_action:
517 if (!desired)
518 return LoadStringW(IDS_ACTION_SKIP);
519 if (desired.sourcePackage() && srcpicked())
520 /* FIXME: Redo source should come up if the tarball is already present locally */
521 return LoadStringW(IDS_ACTION_SOURCE);
522 return LoadStringW(IDS_ACTION_KEEP);
523 case Reinstall_action:
524 return LoadStringW(packagedb::task == PackageDB_Install ? IDS_ACTION_REINSTALL : IDS_ACTION_RETRIEVE);
525 case Install_action:
526 return string_to_wstring(desired.Canonical_version());
528 return LoadStringW(IDS_ACTION_UNKNOWN);
531 void
532 packagemeta::select_action (int id, trusts const deftrust)
534 if (id <= 0)
536 // Install a specific version
537 std::set<packageversion>::iterator i = versions.begin ();
538 for (int j = -id; j > 0; j--)
539 i++;
541 set_action(Install_action, *i, true);
543 else
545 if (id == packagemeta::NoChange_action)
546 set_action((packagemeta::_actions)id, installed);
547 else if (id == packagemeta::Install_action)
549 // Ignore install request if the default version is not accessible.
550 // This assumes that all available versions are already known.
551 // This is not always the case when set_action is called directly.
552 packageversion v = trustp (true, deftrust);
553 if (v.accessible ())
554 set_action(Install_action, v, true);
555 else
556 set_action(NoChange_action, installed);
558 else
559 set_action((packagemeta::_actions)id, trustp (true, deftrust), true);
563 // toggle between the currently installed version (or uninstalled, if not
564 // installed), and the naively preferred version (the highest non-test version)
565 void
566 packagemeta::toggle_action ()
568 if (desired != installed)
570 set_action(NoChange_action, installed);
572 else
574 packageversion naively_preferred;
575 std::set<packageversion>::iterator i = versions.begin ();
576 for (i = versions.begin (); i != versions.end (); ++i)
577 if (!packagedb::solver.is_test_package(*i))
578 naively_preferred = *i;
580 set_action(Install_action, naively_preferred, true);
584 ActionList *
585 packagemeta::list_actions(trusts const trust)
587 // build the list of possible actions
588 ActionList *al = new ActionList();
590 al->add(IDS_ACTION_UNINSTALL, (int)Uninstall_action, (_action == Uninstall_action), bool(installed));
591 al->add(IDS_ACTION_SKIP, (int)NoChange_action, (_action == NoChange_action) && !installed, !installed);
593 std::set<packageversion>::iterator i;
594 for (i = versions.begin (); i != versions.end (); ++i)
596 if (*i == installed)
598 al->add(IDS_ACTION_KEEP, (int)NoChange_action, (_action == NoChange_action), TRUE);
599 al->add(packagedb::task == PackageDB_Install ? IDS_ACTION_REINSTALL : IDS_ACTION_RETRIEVE,
600 (int)Reinstall_action, (_action == Reinstall_action), TRUE);
602 else
604 std::wstring label = string_to_wstring(i->Canonical_version());
605 if (packagedb::solver.is_test_package(*i))
606 label += L" (Test)";
607 al->add(label,
608 -std::distance(versions.begin (), i),
609 (_action == Install_action) && (*i == desired),
610 TRUE);
614 return al;
617 // Set a particular type of action.
618 void
619 packagemeta::set_action (_actions action, packageversion const &default_version,
620 bool useraction)
622 if (action == NoChange_action)
624 // if installed, keep
625 if (installed
626 || categories.find ("Base") != categories.end ()
627 || categories.find ("Orphaned") != categories.end ())
629 desired = default_version;
630 if (desired)
632 pick (desired != installed);
633 srcpick (false);
636 else
638 // else, if not installed, skip
639 desired = packageversion ();
640 pick(false);
643 else if (action == Install_action)
645 desired = default_version;
646 // If desired is empty, it will be set to the solver's preferred version later.
647 if (desired)
649 if (desired != installed)
650 if (desired.accessible ())
652 /* Memorize the fact that the user picked to install this package at least once. */
653 if (useraction)
654 user_picked = true;
656 pick (true);
657 srcpick (false);
659 else
661 pick (false);
662 srcpick (true);
664 else
666 action = NoChange_action;
667 pick (false);
668 srcpick (false);
672 else if (action == Reinstall_action)
674 desired = installed;
675 if (desired.accessible ())
677 pick (true);
678 srcpick (false);
680 else
682 action = NoChange_action;
683 pick (false);
684 srcpick (false);
687 else if (action == Uninstall_action)
689 desired = packageversion ();
690 pick (false);
691 srcpick (false);
692 if (!installed)
693 action = NoChange_action;
696 _action = action;
699 bool
700 packagemeta::picked () const
702 return _picked;
705 void
706 packagemeta::pick (bool picked)
708 _picked = picked;
710 // side effect: display message when picked (if not already seen)
711 if (picked)
712 this->message.display ();
715 bool
716 packagemeta::srcpicked () const
718 return _srcpicked;
721 void
722 packagemeta::srcpick (bool picked)
724 _srcpicked = picked;
727 bool
728 packagemeta::accessible () const
730 for (std::set<packageversion>::iterator i=versions.begin();
731 i != versions.end(); ++i)
732 if (i->accessible())
733 return true;
734 return false;
737 bool
738 packagemeta::sourceAccessible () const
740 for (std::set<packageversion>::iterator i=versions.begin();
741 i != versions.end(); ++i)
743 packageversion bin=*i;
744 if (bin.sourcePackage().accessible())
745 return true;
748 return false;
751 bool
752 packagemeta::isBinary () const
754 for (std::set<packageversion>::iterator i=versions.begin();
755 i != versions.end(); ++i)
756 if ((i->Type() == package_binary) && (i->accessible() || (*i == installed)))
757 return true;
759 return false;
762 void
763 packagemeta::logAllVersions () const
765 for (std::set<packageversion>::iterator i = versions.begin();
766 i != versions.end(); ++i)
768 Log (LOG_BABBLE) << " [" << trustLabel(*i) <<
769 "] ver=" << i->Canonical_version() << endLog;
770 std::ostream & logger = Log (LOG_BABBLE);
771 logger << " depends=";
772 dumpPackageDepends(i->depends(), logger);
773 logger << endLog;
775 #if 0
776 Log (LOG_BABBLE) << " inst=" << i->
777 /* FIXME: Reinstate this code, but spit out all mirror sites */
779 for (int t = 1; t < NTRUST; t++)
781 if (pkg->info[t].install)
782 Log (LOG_BABBLE) << " [%s] ver=%s\n"
783 " inst=%s %d exists=%s\n"
784 " src=%s %d exists=%s",
785 infos[t],
786 pkg->info[t].version ? : "(none)",
787 pkg->info[t].install ? : "(none)",
788 pkg->info[t].install_size,
789 (pkg->info[t].install_exists) ? "yes" : "no",
790 pkg->info[t].source ? : "(none)",
791 pkg->info[t].source_size,
792 (pkg->info[t].source_exists) ? "yes" : "no");
794 #endif
797 std::string
798 packagemeta::trustLabel(packageversion const &aVersion) const
800 if (aVersion == curr)
801 return "Curr";
802 if (aVersion == exp)
803 return "Test";
804 return "Unknown";
807 void
808 packagemeta::logSelectionStatus() const
810 packagemeta const & pkg = *this;
811 const char *trust = ((pkg.desired == pkg.curr) ? "curr"
812 : (pkg.desired == pkg.exp) ? "test" : "unknown");
813 const std::string installed =
814 pkg.installed ? pkg.installed.Canonical_version () : "none";
816 Log (LOG_BABBLE) << "[" << pkg.name << "] action=" << _action << " trust=" << trust << " installed=" << installed << " src?=" << (pkg.desired && srcpicked() ? "yes" : "no") << endLog;
817 if (pkg.categories.size ())
818 Log (LOG_BABBLE) << " categories=" << for_each(pkg.categories.begin(), pkg.categories.end(), StringConcatenator(", ")).result << endLog;
819 #if 0
820 if (pkg.desired.required())
822 /* List other packages this package depends on */
823 Dependency *dp = pkg.desired->required;
824 std::string requires = dp->package.serialise ();
825 for (dp = dp->next; dp; dp = dp->next)
826 requires += std::string (", ") + dp->package.serialise ();
828 Log (LOG_BABBLE) << " requires=" << requires;
830 #endif
831 pkg.logAllVersions();
834 /* scan for local copies of package */
835 bool
836 packagemeta::scan (const packageversion &pkg, bool mirror_mode)
838 /* empty version */
839 if (!pkg)
840 return false;
844 if (!check_for_cached (*(pkg.source ()), NULL, mirror_mode, false)
845 && ::source == IDC_SOURCE_LOCALDIR)
846 return false;
848 catch (Exception * e)
850 // We can ignore these, since we're clearing the source list anyway
851 if (e->errNo () == APPERR_CORRUPT_PACKAGE)
852 return false;
854 // Unexpected exception.
855 throw e;
858 return true;
861 void
862 packagemeta::ScanDownloadedFiles (bool mirror_mode)
864 /* Look at every known package, in all the known mirror dirs,
865 * and fill in the Cached attribute if it exists.
867 packagedb db;
868 for (packagedb::packagecollection::iterator n = db.packages.begin ();
869 n != db.packages.end (); ++n)
871 packagemeta & pkg = *(n->second);
872 std::set<packageversion>::iterator i = pkg.versions.begin ();
873 while (i != pkg.versions.end ())
875 /* scan doesn't alter operator == for packageversions */
876 bool lazy_scan = mirror_mode
877 && (*i != pkg.installed
878 || pkg.installed == pkg.curr
879 || pkg.installed == pkg.exp);
880 bool accessible = scan (*i, lazy_scan);
881 packageversion foo = *i;
882 packageversion pkgsrcver = foo.sourcePackage ();
883 bool src_accessible = scan (pkgsrcver, lazy_scan);
885 /* For local installs, if there is no src and no bin, the version
886 * is unavailable
888 if (!accessible && !src_accessible
889 && *i != pkg.installed)
891 if (pkg.curr == *i)
892 pkg.curr = packageversion ();
893 if (pkg.exp == *i)
894 pkg.exp = packageversion ();
896 i->remove();
897 pkg.versions.erase (i++);
899 /* For now, leave the source version alone */
901 else
902 ++i;
905 /* Don't explicity iterate through sources - any sources that aren't
906 referenced are unselectable anyway. */
909 void
910 packagemeta::addToCategoryBase()
912 add_category ("Base");
915 bool
916 packagemeta::hasNoCategories() const
918 return categories.size() == 0;
921 void
922 packagemeta::setDefaultCategories()
924 add_category ("Orphaned");
927 void
928 packagemeta::addToCategoryAll()
930 add_category ("All");
933 void
934 packagemeta::addScript(Script const &aScript)
936 scripts_.push_back(aScript);
939 std::vector <Script> &
940 packagemeta::scripts()
942 return scripts_;