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
12 * Written by Robert Collins <rbtcollins@hotmail.com>
16 #include "package_meta.h"
25 #include "getopt++/StringArrayOption.h"
27 #include "io_stream.h"
30 #include "filemanip.h"
31 #include "LogSingleton.h"
32 /* io_stream needs a bit of tweaking to get rid of this. TODO */
34 /* this goes at the same time */
38 #include "package_db.h"
45 #include "Exception.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;
56 /* Return an appropriate category caption given the action */
58 packagemeta::action_caption (_actions _value
)
63 return IDS_ACTION_DEFAULT
;
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
) :
77 categories (rhs
.categories
), versions (rhs
.versions
),
78 installed (rhs
.installed
),
86 template<class T
> struct removeCategory
88 removeCategory(packagemeta
*pkg
) : _pkg (pkg
) {}
91 std::vector
<packagemeta
*> &aList
= packagedb::categories
[x
];
92 aList
.erase (find (aList
.begin(), aList
.end(), _pkg
));
98 packagemeta::~packagemeta()
100 for_each (categories
.begin (), categories
.end (), removeCategory
<std::string
> (this));
106 packagemeta::add_version (const SolverPool::addPackageData
&inpkgdata
)
108 SolverPool::addPackageData pkgdata
= inpkgdata
;
110 packageversion
*v
= NULL
;
111 switch (pkgdata
.stability
)
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
131 We rely on this by adding packages from installed.db last.
134 for (std::set
<packageversion
>::iterator i
= versions
.begin();
138 if (i
->Canonical_version() != pkgdata
.version
)
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
155 *v
= packageversion();
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
167 (suppress this for installed packages, as we are only guessing the
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
;
183 /* Create the SolvableVersion */
185 SolvableVersion thepkg
= db
.solver
.addPackage(name
, pkgdata
);
187 /* Add the version */
188 std::pair
<std::set
<packageversion
>::iterator
, bool> result
= versions
.insert (thepkg
);
191 Log (LOG_PLAIN
) << "Failed to add version " << thepkg
.Canonical_version() << " in package " << name
<< endLog
;
194 Log (LOG_PLAIN
) << "Added version " << thepkg
.Canonical_version() << " in package " << name
<< endLog
;
197 /* Record the highest version at a given stability level */
200 /* Any version is always greater than no version */
203 comparison
= SolvableVersion::compareVersions(thepkg
, *v
);
207 Log (LOG_BABBLE
) << "package " << thepkg
.Name() << " comparing versions " << thepkg
.Canonical_version() << " and " << v
->Canonical_version() << ", result was " << comparison
<< endLog
;
219 const packageversion
*
220 packagemeta::findVersion(std::string
&version
) const
222 for (std::set
<packageversion
>::iterator i
= versions
.begin();
226 if (i
->Canonical_version() == version
)
234 packagemeta::isBlacklisted(const packageversion
&version
) const
236 for (std::set
<std::string
>::iterator i
= version_blacklist
.begin();
237 i
!= version_blacklist
.end();
240 if (i
->compare(version
.Canonical_version()) == 0)
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)
256 /* and mark as Keep */
263 packagemeta::add_category (const std::string
& cat
)
265 if (categories
.find (cat
) != categories
.end())
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)
283 typedef const std::string argument_type
;
287 packagemeta::getReadableCategoryList () const
289 return for_each(categories
.begin(), categories
.end(),
291 StringConcatenator(", "),
292 std::bind(std::not_equal_to
<std::string
>(), "All", std::placeholders::_1
))
297 parseNames (std::set
<std::string
> &parsed
, std::string
&option
)
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
);
318 validatePackageNames (std::set
<std::string
> &names
)
321 for (std::set
<std::string
>::iterator n
= names
.begin();
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. */
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();
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);
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
);
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())
396 /* Wanted version is unspecified */
397 version
= packageversion();
399 /* ... unless a version was explicitly specified */
400 std::string v
= i
->second
;
403 const packageversion
*pv
= findVersion(v
);
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();
426 Log (LOG_BABBLE
) << "Added manual package " << name
<< endLog
;
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. */
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
);
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
;
477 Log (LOG_BABBLE
) << "Deleted manual package " << name
<< endLog
;
482 packagemeta::SDesc () const
484 for (std::set
<packageversion
>::iterator i
= versions
.begin(); i
!= versions
.end(); i
++)
486 if (i
->SDesc().size())
490 return std::string();
494 hasLDesc(packageversion
const &pkg
)
496 return pkg
.LDesc().size();
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();
508 /* Return an appropriate caption given the current action. */
510 packagemeta::action_caption () const
514 case Uninstall_action
:
515 return LoadStringW(IDS_ACTION_UNINSTALL
);
516 case NoChange_action
:
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
);
526 return string_to_wstring(desired
.Canonical_version());
528 return LoadStringW(IDS_ACTION_UNKNOWN
);
532 packagemeta::select_action (int id
, trusts
const deftrust
)
536 // Install a specific version
537 std::set
<packageversion
>::iterator i
= versions
.begin ();
538 for (int j
= -id
; j
> 0; j
--)
541 set_action(Install_action
, *i
, true);
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
);
554 set_action(Install_action
, v
, true);
556 set_action(NoChange_action
, installed
);
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)
566 packagemeta::toggle_action ()
568 if (desired
!= installed
)
570 set_action(NoChange_action
, installed
);
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);
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
)
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
);
604 std::wstring label
= string_to_wstring(i
->Canonical_version());
605 if (packagedb::solver
.is_test_package(*i
))
608 -std::distance(versions
.begin (), i
),
609 (_action
== Install_action
) && (*i
== desired
),
617 // Set a particular type of action.
619 packagemeta::set_action (_actions action
, packageversion
const &default_version
,
622 if (action
== NoChange_action
)
624 // if installed, keep
626 || categories
.find ("Base") != categories
.end ()
627 || categories
.find ("Orphaned") != categories
.end ())
629 desired
= default_version
;
632 pick (desired
!= installed
);
638 // else, if not installed, skip
639 desired
= packageversion ();
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.
649 if (desired
!= installed
)
650 if (desired
.accessible ())
652 /* Memorize the fact that the user picked to install this package at least once. */
666 action
= NoChange_action
;
672 else if (action
== Reinstall_action
)
675 if (desired
.accessible ())
682 action
= NoChange_action
;
687 else if (action
== Uninstall_action
)
689 desired
= packageversion ();
693 action
= NoChange_action
;
700 packagemeta::picked () const
706 packagemeta::pick (bool picked
)
710 // side effect: display message when picked (if not already seen)
712 this->message
.display ();
716 packagemeta::srcpicked () const
722 packagemeta::srcpick (bool picked
)
728 packagemeta::accessible () const
730 for (std::set
<packageversion
>::iterator i
=versions
.begin();
731 i
!= versions
.end(); ++i
)
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())
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
)))
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
);
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",
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");
798 packagemeta::trustLabel(packageversion
const &aVersion
) const
800 if (aVersion
== curr
)
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
;
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
;
831 pkg
.logAllVersions();
834 /* scan for local copies of package */
836 packagemeta::scan (const packageversion
&pkg
, bool mirror_mode
)
844 if (!check_for_cached (*(pkg
.source ()), NULL
, mirror_mode
, false)
845 && ::source
== IDC_SOURCE_LOCALDIR
)
848 catch (Exception
* e
)
850 // We can ignore these, since we're clearing the source list anyway
851 if (e
->errNo () == APPERR_CORRUPT_PACKAGE
)
854 // Unexpected exception.
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.
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
888 if (!accessible
&& !src_accessible
889 && *i
!= pkg
.installed
)
892 pkg
.curr
= packageversion ();
894 pkg
.exp
= packageversion ();
897 pkg
.versions
.erase (i
++);
899 /* For now, leave the source version alone */
905 /* Don't explicity iterate through sources - any sources that aren't
906 referenced are unselectable anyway. */
910 packagemeta::addToCategoryBase()
912 add_category ("Base");
916 packagemeta::hasNoCategories() const
918 return categories
.size() == 0;
922 packagemeta::setDefaultCategories()
924 add_category ("Orphaned");
928 packagemeta::addToCategoryAll()
930 add_category ("All");
934 packagemeta::addScript(Script
const &aScript
)
936 scripts_
.push_back(aScript
);
939 std::vector
<Script
> &
940 packagemeta::scripts()