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"
43 #include "Exception.h"
46 static StringArrayOption
DeletePackageOption ('x', "remove-packages", "Specify packages to uninstall");
47 static StringArrayOption
DeleteCategoryOption ('c', "remove-categories", "Specify categories to uninstall");
48 static StringArrayOption
PackageOption ('P', "packages", "Specify packages to install");
49 static StringArrayOption
CategoryOption ('C', "categories", "Specify entire categories to install");
50 bool hasManualSelections
= 0;
54 /* Return an appropriate category caption given the action */
56 packagemeta::action_caption (_actions _value
)
61 return IDS_ACTION_DEFAULT
;
63 return IDS_ACTION_INSTALL
;
64 case Reinstall_action
:
65 return IDS_ACTION_REINSTALL
;
66 case Uninstall_action
:
67 return IDS_ACTION_UNINSTALL
;
70 return IDS_ACTION_UNKNOWN
;
73 packagemeta::packagemeta (packagemeta
const &rhs
) :
75 categories (rhs
.categories
), versions (rhs
.versions
),
76 installed (rhs
.installed
),
84 template<class T
> struct removeCategory
: public std::unary_function
<T
, void>
86 removeCategory(packagemeta
*pkg
) : _pkg (pkg
) {}
89 std::vector
<packagemeta
*> &aList
= packagedb::categories
[x
];
90 aList
.erase (find (aList
.begin(), aList
.end(), _pkg
));
96 packagemeta::~packagemeta()
98 for_each (categories
.begin (), categories
.end (), removeCategory
<std::string
> (this));
104 packagemeta::add_version (const SolverPool::addPackageData
&inpkgdata
)
106 SolverPool::addPackageData pkgdata
= inpkgdata
;
108 packageversion
*v
= NULL
;
109 switch (pkgdata
.stability
)
122 If a packageversion for the same version number is already present, allow
123 this version to replace it.
125 There is a problem where multiple repos provide a package. It's never been
126 clear which repo should win. With this implementation, the last one added
129 We rely on this by adding packages from installed.db last.
132 for (std::set
<packageversion
>::iterator i
= versions
.begin();
136 if (i
->Canonical_version() != pkgdata
.version
)
139 if (pkgdata
.vendor
== i
->Vendor())
141 /* Merge the site-list from any existing packageversion with the same
142 repository 'release:' label */
143 pkgdata
.archive
.sites
.insert(pkgdata
.archive
.sites
.end(),
144 i
->source()->sites
.begin(),
145 i
->source()->sites
.end());
147 /* Installed packages do not supersede repo packages */
148 if (pkgdata
.reponame
!= "_installed")
150 /* Ensure a stability level doesn't point to a version we're about
153 *v
= packageversion();
160 /* Otherwise... if we had a way to set repo priorities, that could be
161 used to control which packageversion the solver picks. For the
162 moment, just warn that you might not be getting what you think you
165 (suppress this for installed packages, as we are only guessing the
168 if (pkgdata
.reponame
!= "_installed")
170 Log (LOG_PLAIN
) << "Version " << pkgdata
.version
<< " of package " <<
171 name
<< " is present in releases labelled " << pkgdata
.vendor
<<
172 " and " << i
->Vendor() << endLog
;
181 /* Create the SolvableVersion */
183 SolvableVersion thepkg
= db
.solver
.addPackage(name
, pkgdata
);
185 /* Add the version */
186 std::pair
<std::set
<packageversion
>::iterator
, bool> result
= versions
.insert (thepkg
);
189 Log (LOG_PLAIN
) << "Failed to add version " << thepkg
.Canonical_version() << " in package " << name
<< endLog
;
192 Log (LOG_PLAIN
) << "Added version " << thepkg
.Canonical_version() << " in package " << name
<< endLog
;
195 /* Record the highest version at a given stability level */
198 /* Any version is always greater than no version */
201 comparison
= SolvableVersion::compareVersions(thepkg
, *v
);
205 Log (LOG_BABBLE
) << "package " << thepkg
.Name() << " comparing versions " << thepkg
.Canonical_version() << " and " << v
->Canonical_version() << ", result was " << comparison
<< endLog
;
217 const packageversion
*
218 packagemeta::findVersion(std::string
&version
) const
220 for (std::set
<packageversion
>::iterator i
= versions
.begin();
224 if (i
->Canonical_version() == version
)
232 packagemeta::isBlacklisted(const packageversion
&version
) const
234 for (std::set
<std::string
>::iterator i
= version_blacklist
.begin();
235 i
!= version_blacklist
.end();
238 if (i
->compare(version
.Canonical_version()) == 0)
246 packagemeta::set_installed_version (const std::string
&version
)
248 for (std::set
<packageversion
>::iterator i
= versions
.begin(); i
!= versions
.end(); i
++)
250 if (version
.compare(i
->Canonical_version()) == 0)
254 /* and mark as Keep */
261 packagemeta::add_category (const std::string
& cat
)
263 if (categories
.find (cat
) != categories
.end())
265 /* add a new record for the package list */
266 packagedb::categories
[cat
].push_back (this);
267 categories
.insert (cat
);
270 struct StringConcatenator
: public std::unary_function
<const std::string
, void>{
271 StringConcatenator(std::string aString
) : gap(aString
){}
272 void operator()(const std::string
& aString
)
274 if (result
.size() != 0)
283 packagemeta::getReadableCategoryList () const
285 return for_each(categories
.begin(), categories
.end(),
287 StringConcatenator(", "), bind1st(std::not_equal_to
<std::string
>(), "All"))
292 parseNames (std::set
<std::string
> &parsed
, std::string
&option
)
296 /* Split up the packages listed in the option. */
297 std::string::size_type loc
= option
.find (",", 0);
298 while (loc
!= std::string::npos
)
300 tname
= option
.substr (0, loc
);
301 option
= option
.substr (loc
+ 1);
302 parsed
.insert (tname
);
303 loc
= option
.find (",", 0);
306 /* At this point, no "," exists in option. Don't add
307 an empty string if the entire option was empty. */
308 if (option
.length ())
309 parsed
.insert (option
);
313 validatePackageNames (std::set
<std::string
> &names
)
316 for (std::set
<std::string
>::iterator n
= names
.begin();
320 if (db
.packages
.find(*n
) == db
.packages
.end())
322 Log(LOG_PLAIN
) << "Package '" << *n
<< "' not found." << endLog
;
327 bool packagemeta::isManuallyWanted(packageversion
&version
) const
329 static bool parsed_yet
= false;
330 static std::map
<std::string
, std::string
> parsed_names
;
331 hasManualSelections
|= parsed_names
.size ();
332 static std::set
<std::string
> parsed_categories
;
333 hasManualSelections
|= parsed_categories
.size ();
334 bool bReturn
= false;
336 /* First time through, we parse all the names out from the
337 option string and store them away in an STL set. */
340 std::vector
<std::string
> packages_options
= PackageOption
;
341 std::vector
<std::string
> categories_options
= CategoryOption
;
343 std::set
<std::string
> items
;
344 for (std::vector
<std::string
>::iterator n
= packages_options
.begin ();
345 n
!= packages_options
.end (); ++n
)
347 parseNames (items
, *n
);
350 std::set
<std::string
> packages
;
351 /* Separate any 'package=version' into package and version parts */
352 for (std::set
<std::string
>::iterator n
= items
.begin();
358 std::string::size_type loc
= n
->find ("=", 0);
359 if (loc
!= std::string::npos
)
361 package
= n
->substr(0, loc
);
362 version
= n
->substr(loc
+1);
369 Log (LOG_BABBLE
) << "package: " << package
<< " version: " << version
<< endLog
;
370 parsed_names
[package
] = version
;
371 packages
.insert(package
);
374 validatePackageNames (packages
);
376 for (std::vector
<std::string
>::iterator n
= categories_options
.begin ();
377 n
!= categories_options
.end (); ++n
)
379 parseNames (parsed_categories
, *n
);
384 /* Once we've already parsed the option string, just do
385 a lookup in the cache of already-parsed names. */
386 std::map
<std::string
, std::string
>::iterator i
= parsed_names
.find(name
);
387 if (i
!= parsed_names
.end())
391 /* Wanted version is unspecified */
392 version
= packageversion();
394 /* ... unless a version was explicitly specified */
395 std::string v
= i
->second
;
398 const packageversion
*pv
= findVersion(v
);
402 Log (LOG_PLAIN
) << "package: " << name
<< " version: " << v
<< " not found" << endLog
;
406 /* If we didn't select the package manually, did we select any
407 of the categories it is in? */
408 if (!bReturn
&& parsed_categories
.size ())
410 std::set
<std::string
, casecompare_lt_op
>::iterator curcat
;
411 for (curcat
= categories
.begin (); curcat
!= categories
.end (); curcat
++)
412 if (parsed_categories
.find (*curcat
) != parsed_categories
.end ())
414 Log (LOG_BABBLE
) << "Found category " << *curcat
<< " in package " << name
<< endLog
;
415 version
= packageversion();
421 Log (LOG_BABBLE
) << "Added manual package " << name
<< endLog
;
425 bool packagemeta::isManuallyDeleted() const
427 static bool parsed_yet
= false;
428 static std::set
<std::string
> parsed_delete
;
429 hasManualSelections
|= parsed_delete
.size ();
430 static std::set
<std::string
> parsed_delete_categories
;
431 hasManualSelections
|= parsed_delete_categories
.size ();
432 bool bReturn
= false;
434 /* First time through, we parse all the names out from the
435 option string and store them away in an STL set. */
438 std::vector
<std::string
> delete_options
= DeletePackageOption
;
439 std::vector
<std::string
> categories_options
= DeleteCategoryOption
;
440 for (std::vector
<std::string
>::iterator n
= delete_options
.begin ();
441 n
!= delete_options
.end (); ++n
)
443 parseNames (parsed_delete
, *n
);
445 validatePackageNames (parsed_delete
);
446 for (std::vector
<std::string
>::iterator n
= categories_options
.begin ();
447 n
!= categories_options
.end (); ++n
)
449 parseNames (parsed_delete_categories
, *n
);
454 /* Once we've already parsed the option string, just do
455 a lookup in the cache of already-parsed names. */
456 bReturn
= parsed_delete
.find(name
) != parsed_delete
.end();
458 /* If we didn't select the package manually, did we select any
459 of the categories it is in? */
460 if (!bReturn
&& parsed_delete_categories
.size ())
462 std::set
<std::string
, casecompare_lt_op
>::iterator curcat
;
463 for (curcat
= categories
.begin (); curcat
!= categories
.end (); curcat
++)
464 if (parsed_delete_categories
.find (*curcat
) != parsed_delete_categories
.end ())
466 Log (LOG_BABBLE
) << "Found category " << *curcat
<< " in package " << name
<< endLog
;
472 Log (LOG_BABBLE
) << "Deleted manual package " << name
<< endLog
;
477 packagemeta::SDesc () const
479 for (std::set
<packageversion
>::iterator i
= versions
.begin(); i
!= versions
.end(); i
++)
481 if (i
->SDesc().size())
485 return std::string();
489 hasLDesc(packageversion
const &pkg
)
491 return pkg
.LDesc().size();
495 packagemeta::LDesc () const
497 std::set
<packageversion
>::iterator i
= find_if (versions
.begin(), versions
.end(), hasLDesc
);
498 if (i
== versions
.end())
499 return std::string();
503 /* Return an appropriate caption given the current action. */
505 packagemeta::action_caption () const
509 case Uninstall_action
:
510 return LoadStringW(IDS_ACTION_UNINSTALL
);
511 case NoChange_action
:
513 return LoadStringW(IDS_ACTION_SKIP
);
514 if (desired
.sourcePackage() && srcpicked())
515 /* FIXME: Redo source should come up if the tarball is already present locally */
516 return LoadStringW(IDS_ACTION_SOURCE
);
517 return LoadStringW(IDS_ACTION_KEEP
);
518 case Reinstall_action
:
519 return LoadStringW(packagedb::task
== PackageDB_Install
? IDS_ACTION_REINSTALL
: IDS_ACTION_RETRIEVE
);
521 return string_to_wstring(desired
.Canonical_version());
523 return LoadStringW(IDS_ACTION_UNKNOWN
);
527 packagemeta::select_action (int id
, trusts
const deftrust
)
531 // Install a specific version
532 std::set
<packageversion
>::iterator i
= versions
.begin ();
533 for (int j
= -id
; j
> 0; j
--)
536 set_action(Install_action
, *i
, true);
540 if (id
== packagemeta::NoChange_action
)
541 set_action((packagemeta::_actions
)id
, installed
);
543 set_action((packagemeta::_actions
)id
, trustp (true, deftrust
), true);
547 // toggle between the currently installed version (or uninstalled, if not
548 // installed), and the naively preferred version (the highest non-test version)
550 packagemeta::toggle_action ()
552 if (desired
!= installed
)
554 set_action(NoChange_action
, installed
);
558 packageversion naively_preferred
;
559 std::set
<packageversion
>::iterator i
= versions
.begin ();
560 for (i
= versions
.begin (); i
!= versions
.end (); ++i
)
561 if (!packagedb::solver
.is_test_package(*i
))
562 naively_preferred
= *i
;
564 set_action(Install_action
, naively_preferred
, true);
569 packagemeta::list_actions(trusts
const trust
)
571 // build the list of possible actions
572 ActionList
*al
= new ActionList();
574 al
->add(IDS_ACTION_UNINSTALL
, (int)Uninstall_action
, (_action
== Uninstall_action
), bool(installed
));
575 al
->add(IDS_ACTION_SKIP
, (int)NoChange_action
, (_action
== NoChange_action
) && !installed
, !installed
);
577 std::set
<packageversion
>::iterator i
;
578 for (i
= versions
.begin (); i
!= versions
.end (); ++i
)
582 al
->add(IDS_ACTION_KEEP
, (int)NoChange_action
, (_action
== NoChange_action
), TRUE
);
583 al
->add(packagedb::task
== PackageDB_Install
? IDS_ACTION_REINSTALL
: IDS_ACTION_RETRIEVE
,
584 (int)Reinstall_action
, (_action
== Reinstall_action
), TRUE
);
588 std::wstring label
= string_to_wstring(i
->Canonical_version());
589 if (packagedb::solver
.is_test_package(*i
))
592 -std::distance(versions
.begin (), i
),
593 (_action
== Install_action
) && (*i
== desired
),
601 // Set a particular type of action.
603 packagemeta::set_action (_actions action
, packageversion
const &default_version
,
606 if (action
== NoChange_action
)
608 // if installed, keep
610 || categories
.find ("Base") != categories
.end ()
611 || categories
.find ("Orphaned") != categories
.end ())
613 desired
= default_version
;
616 pick (desired
!= installed
);
622 // else, if not installed, skip
623 desired
= packageversion ();
627 else if (action
== Install_action
)
629 desired
= default_version
;
632 if (desired
!= installed
)
633 if (desired
.accessible ())
635 /* Memorize the fact that the user picked to install this package at least once. */
649 action
= NoChange_action
;
655 else if (action
== Reinstall_action
)
665 action
= NoChange_action
;
670 else if (action
== Uninstall_action
)
672 desired
= packageversion ();
679 packagemeta::picked () const
685 packagemeta::pick (bool picked
)
689 // side effect: display message when picked (if not already seen)
691 this->message
.display ();
695 packagemeta::srcpicked () const
701 packagemeta::srcpick (bool picked
)
707 packagemeta::accessible () const
709 for (std::set
<packageversion
>::iterator i
=versions
.begin();
710 i
!= versions
.end(); ++i
)
717 packagemeta::sourceAccessible () const
719 for (std::set
<packageversion
>::iterator i
=versions
.begin();
720 i
!= versions
.end(); ++i
)
722 packageversion bin
=*i
;
723 if (bin
.sourcePackage().accessible())
731 packagemeta::isBinary () const
733 for (std::set
<packageversion
>::iterator i
=versions
.begin();
734 i
!= versions
.end(); ++i
)
735 if ((i
->Type() == package_binary
) && (i
->accessible() || (*i
== installed
)))
742 packagemeta::logAllVersions () const
744 for (std::set
<packageversion
>::iterator i
= versions
.begin();
745 i
!= versions
.end(); ++i
)
747 Log (LOG_BABBLE
) << " [" << trustLabel(*i
) <<
748 "] ver=" << i
->Canonical_version() << endLog
;
749 std::ostream
& logger
= Log (LOG_BABBLE
);
750 logger
<< " depends=";
751 dumpPackageDepends(i
->depends(), logger
);
755 Log (LOG_BABBLE
) << " inst=" << i
->
756 /* FIXME: Reinstate this code, but spit out all mirror sites */
758 for (int t
= 1; t
< NTRUST
; t
++)
760 if (pkg
->info
[t
].install
)
761 Log (LOG_BABBLE
) << " [%s] ver=%s\n"
762 " inst=%s %d exists=%s\n"
763 " src=%s %d exists=%s",
765 pkg
->info
[t
].version
? : "(none)",
766 pkg
->info
[t
].install
? : "(none)",
767 pkg
->info
[t
].install_size
,
768 (pkg
->info
[t
].install_exists
) ? "yes" : "no",
769 pkg
->info
[t
].source
? : "(none)",
770 pkg
->info
[t
].source_size
,
771 (pkg
->info
[t
].source_exists
) ? "yes" : "no");
777 packagemeta::trustLabel(packageversion
const &aVersion
) const
779 if (aVersion
== curr
)
787 packagemeta::logSelectionStatus() const
789 packagemeta
const & pkg
= *this;
790 const char *trust
= ((pkg
.desired
== pkg
.curr
) ? "curr"
791 : (pkg
.desired
== pkg
.exp
) ? "test" : "unknown");
792 const std::string installed
=
793 pkg
.installed
? pkg
.installed
.Canonical_version () : "none";
795 Log (LOG_BABBLE
) << "[" << pkg
.name
<< "] action=" << _action
<< " trust=" << trust
<< " installed=" << installed
<< " src?=" << (pkg
.desired
&& srcpicked() ? "yes" : "no") << endLog
;
796 if (pkg
.categories
.size ())
797 Log (LOG_BABBLE
) << " categories=" << for_each(pkg
.categories
.begin(), pkg
.categories
.end(), StringConcatenator(", ")).result
<< endLog
;
799 if (pkg
.desired
.required())
801 /* List other packages this package depends on */
802 Dependency
*dp
= pkg
.desired
->required
;
803 std::string requires
= dp
->package
.serialise ();
804 for (dp
= dp
->next
; dp
; dp
= dp
->next
)
805 requires
+= std::string (", ") + dp
->package
.serialise ();
807 Log (LOG_BABBLE
) << " requires=" << requires
;
810 pkg
.logAllVersions();
813 /* scan for local copies of package */
815 packagemeta::scan (const packageversion
&pkg
, bool mirror_mode
)
823 if (!check_for_cached (*(pkg
.source ()), NULL
, mirror_mode
, false)
824 && ::source
== IDC_SOURCE_LOCALDIR
)
827 catch (Exception
* e
)
829 // We can ignore these, since we're clearing the source list anyway
830 if (e
->errNo () == APPERR_CORRUPT_PACKAGE
)
833 // Unexpected exception.
841 packagemeta::ScanDownloadedFiles (bool mirror_mode
)
843 /* Look at every known package, in all the known mirror dirs,
844 * and fill in the Cached attribute if it exists.
847 for (packagedb::packagecollection::iterator n
= db
.packages
.begin ();
848 n
!= db
.packages
.end (); ++n
)
850 packagemeta
& pkg
= *(n
->second
);
851 std::set
<packageversion
>::iterator i
= pkg
.versions
.begin ();
852 while (i
!= pkg
.versions
.end ())
854 /* scan doesn't alter operator == for packageversions */
855 bool lazy_scan
= mirror_mode
856 && (*i
!= pkg
.installed
857 || pkg
.installed
== pkg
.curr
858 || pkg
.installed
== pkg
.exp
);
859 bool accessible
= scan (*i
, lazy_scan
);
860 packageversion foo
= *i
;
861 packageversion pkgsrcver
= foo
.sourcePackage ();
862 bool src_accessible
= scan (pkgsrcver
, lazy_scan
);
864 /* For local installs, if there is no src and no bin, the version
867 if (!accessible
&& !src_accessible
868 && *i
!= pkg
.installed
)
871 pkg
.curr
= packageversion ();
873 pkg
.exp
= packageversion ();
876 pkg
.versions
.erase (i
++);
878 /* For now, leave the source version alone */
884 /* Don't explicity iterate through sources - any sources that aren't
885 referenced are unselectable anyway. */
889 packagemeta::addToCategoryBase()
891 add_category ("Base");
895 packagemeta::hasNoCategories() const
897 return categories
.size() == 0;
901 packagemeta::setDefaultCategories()
903 add_category ("Orphaned");
907 packagemeta::addToCategoryAll()
909 add_category ("All");
913 packagemeta::addScript(Script
const &aScript
)
915 scripts_
.push_back(aScript
);
918 std::vector
<Script
> &
919 packagemeta::scripts()