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 /* this is the package database class.
17 * It lists all known packages, including custom ones, ones from a mirror and
31 #include "io_stream.h"
34 #include "filemanip.h"
35 #include "package_version.h"
36 #include "package_db.h"
37 #include "package_meta.h"
38 #include "Exception.h"
40 #include "LogSingleton.h"
43 #include "csu_util/version_compare.h"
44 #include "getopt++/BoolOption.h"
46 static BoolOption
MirrorOption (false, 'm', "mirror-mode", IDS_HELPTEXT_MIRROR_MODE
);
48 packagedb::packagedb ()
60 sourcePackages
.clear();
64 basepkg
= packageversion();
65 dependencyOrderedPackages
.clear();
73 /* Read in the local installation database. */
75 db
= io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
79 char line
[1000], pkgname
[1000];
81 if (db
->gets (line
, 1000))
83 /* Look for header line (absent in version 1) */
86 sscanf (line
, "%s %d", pkgname
, &instsz
);
87 if (!strcasecmp (pkgname
, "INSTALLED.DB"))
94 Log (LOG_BABBLE
) << "INSTALLED.DB version " << dbver
<< endLog
;
101 io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
103 // skip over already-parsed header line
105 db
->gets (line
, 1000);
107 while (db
->gets (line
, 1000))
114 int res
= sscanf (line
, "%s %s %d", pkgname
, inst
, &user_picked
);
116 if (res
< 3 || pkgname
[0] == '\0' || inst
[0] == '\0')
120 parseable
= parse_filename (inst
, f
);
124 SolverPool::addPackageData data
;
125 data
.reponame
= "_installed";
126 data
.version
= f
.ver
;
127 data
.type
= package_binary
;
129 // very limited information is available from installed.db, so
130 // we put our best guesses here...
131 data
.vendor
= "cygwin";
132 data
.requires
= NULL
;
133 data
.obsoletes
= NULL
;
134 data
.provides
= NULL
;
135 data
.conflicts
= NULL
;
138 data
.stability
= TRUST_UNKNOWN
;
139 data
.spkg
= PackageSpecification(std::string(pkgname
) + "-src", f
.ver
);
141 // supplement this with sdesc, source, and stability
142 // information from setup.ini, if possible...
143 packageversion pv
= findBinaryVersion(PackageSpecification(pkgname
, f
.ver
));
150 data
.sdesc
= pv
.SDesc();
151 data
.ldesc
= pv
.LDesc();
152 data
.archive
= *pv
.source();
153 data
.stability
= pv
.Stability();
154 data
.spkg_id
= pv
.sourcePackage();
155 data
.spkg
= pv
.sourcePackageName();
157 data
.requires
= &dep
;
158 obs
= pv
.obsoletes();
159 data
.obsoletes
= &obs
;
160 prov
= pv
.provides();
161 data
.provides
= &prov
;
162 conf
= pv
.conflicts();
163 data
.conflicts
= &conf
;
166 // This version is no longer available. It could
167 // be old, or it could be a previous test release
168 // that has been replaced by a new test release.
169 // Try to get some info from the packagemeta.
171 packagemeta
*pkgm
= findBinary(PackageSpecification(pkgname
));
174 data
.sdesc
= pkgm
->curr
.SDesc();
175 data
.ldesc
= pkgm
->curr
.LDesc();
177 && version_compare (f
.ver
, pkgm
->curr
.Canonical_version()) > 0)
178 data
.stability
= TRUST_TEST
;
182 packagemeta
*pkg
= packagedb::addBinary (pkgname
, data
);
184 pkg
->set_installed_version (f
.ver
);
187 pkg
->user_picked
= (user_picked
& 1);
195 fatal(NULL
, IDS_INSTALLEDB_VERSION
);
198 installeddbver
= dbver
;
201 solver
.internalize();
204 /* Create the fictitious basepkg */
206 packagedb::makeBase()
208 SolverPool::addPackageData data
;
209 data
.reponame
= "_installed";
210 data
.version
= "0.0-0";
211 data
.type
= package_binary
;
212 data
.vendor
= "cygwin";
213 data
.sdesc
= "Ficitious package that requires all Base packages";
214 data
.ldesc
= "Ficitious package that requires all Base packages";
215 data
.obsoletes
= NULL
;
216 data
.provides
= NULL
;
217 data
.conflicts
= NULL
;
218 data
.stability
= TRUST_CURR
;
219 // data.spkg = PackageSpecification();
220 // data.spkg_id = packageversion();
223 for (std::vector
<packagemeta
*>::const_iterator i
= categories
["Base"].begin();
224 i
!= categories
["Base"].end(); i
++)
226 packagemeta
*pkg
= *i
;
227 PackageSpecification
*spec
= new PackageSpecification(pkg
->name
);
230 data
.requires
= &dep
;
232 basepkg
= solver
.addPackage("base", data
);
233 /* We don't register this in packagemeta */
236 /* Create the fictitious windows package */
238 packagedb::makeWindows()
241 v
<< OSMajorVersion() << "." << OSMinorVersion() << "." << OSBuildNumber();
243 SolverPool::addPackageData data
;
244 data
.reponame
= "_installed";
245 data
.version
= v
.str();
246 data
.type
= package_binary
;
247 data
.vendor
= "cygwin";
248 data
.sdesc
= "Ficitious package indicating Windows version";
249 data
.ldesc
= "Ficitious package indicating Windows version";
250 data
.requires
= NULL
;
251 data
.obsoletes
= NULL
;
252 data
.provides
= NULL
;
253 data
.conflicts
= NULL
;
254 data
.stability
= TRUST_CURR
;
256 solver
.addPackage("_windows", data
);
257 /* We don't register this in packagemeta */
260 /* Add a package version to the packagedb */
262 packagedb::addBinary (const std::string
&pkgname
,
263 const SolverPool::addPackageData
&pkgdata
)
265 /* If pkgname isn't already in packagedb, add a packagemeta */
266 packagemeta
*pkg
= findBinary (PackageSpecification(pkgname
));
269 pkg
= new packagemeta (pkgname
);
270 packages
.insert (packagedb::packagecollection::value_type(pkgname
, pkg
));
273 /* Create the SolvableVersion and register it in packagemeta */
274 pkg
->add_version (pkgdata
);
280 packagedb::addSource (const std::string
&pkgname
,
281 const SolverPool::addPackageData
&pkgdata
)
283 /* If pkgname isn't already in packagedb, add a packagemeta */
284 packagemeta
*pkg
= findSource (PackageSpecification(pkgname
));
287 pkg
= new packagemeta (pkgname
);
288 sourcePackages
.insert (packagedb::packagecollection::value_type(pkgname
, pkg
));
291 /* Create the SolvableVersion and register it in packagemeta */
292 SolvableVersion sv
= pkg
->add_version (pkgdata
);
300 /* naive approach - just dump the lot */
301 char const *odbn
= "cygfile:///etc/setup/installed.db";
302 char const *ndbn
= "cygfile:///etc/setup/installed.db.new";
304 io_stream::mkpath_p (PATH_TO_FILE
, ndbn
, 0755);
306 io_stream
*ndb
= io_stream::open (ndbn
, "wb", 0644);
308 // XXX if this failed, try removing any existing .new database?
310 return errno
? errno
: 1;
312 ndb
->write ("INSTALLED.DB 3\n", strlen ("INSTALLED.DB 3\n"));
313 for (packagedb::packagecollection::iterator i
= packages
.begin ();
314 i
!= packages
.end (); ++i
)
316 packagemeta
& pkgm
= *(i
->second
);
320 In INSTALLED.DB 3, lines are: 'packagename version flags', where
321 version is encoded in a notional filename for backwards
322 compatibility, and the only currently defined flag is user-picked
326 line
= pkgm
.name
+ " " +
327 pkgm
.name
+ "-" + std::string(pkgm
.installed
.Canonical_version()) + ".tar.bz2 " +
328 (pkgm
.user_picked
? "1" : "0") + "\n";
329 ndb
->write (line
.c_str(), line
.size());
335 io_stream::remove (odbn
);
337 if (io_stream::move (ndbn
, odbn
))
338 return errno
? errno
: 1;
345 if (installeddbver
< 3)
347 /* Guess which packages were user_picked. This has to take place after
348 setup.ini has been parsed as it needs dependency information. */
355 packagedb::findBinary (PackageSpecification
const &spec
) const
357 packagedb::packagecollection::iterator n
= packages
.find(spec
.packageName());
358 if (n
!= packages
.end())
360 packagemeta
& pkgm
= *(n
->second
);
361 for (std::set
<packageversion
>::iterator i
=pkgm
.versions
.begin();
362 i
!= pkgm
.versions
.end(); ++i
)
363 if (spec
.satisfies (*i
))
370 packagedb::findBinaryVersion (PackageSpecification
const &spec
) const
372 packagedb::packagecollection::iterator n
= packages
.find(spec
.packageName());
373 if (n
!= packages
.end())
375 packagemeta
& pkgm
= *(n
->second
);
376 for (std::set
<packageversion
>::iterator i
=pkgm
.versions
.begin();
377 i
!= pkgm
.versions
.end(); ++i
)
378 if (spec
.satisfies (*i
))
381 return packageversion();
385 packagedb::findSource (PackageSpecification
const &spec
) const
387 packagedb::packagecollection::iterator n
= sourcePackages
.find(spec
.packageName());
388 if (n
!= sourcePackages
.end())
390 packagemeta
& pkgm
= *(n
->second
);
391 for (std::set
<packageversion
>::iterator i
= pkgm
.versions
.begin();
392 i
!= pkgm
.versions
.end(); ++i
)
393 if (spec
.satisfies (*i
))
400 packagedb::findSourceVersion (PackageSpecification
const &spec
) const
402 packagedb::packagecollection::iterator n
= sourcePackages
.find(spec
.packageName());
403 if (n
!= sourcePackages
.end())
405 packagemeta
& pkgm
= *(n
->second
);
406 for (std::set
<packageversion
>::iterator i
= pkgm
.versions
.begin();
407 i
!= pkgm
.versions
.end(); ++i
)
408 if (spec
.satisfies (*i
))
411 return packageversion();
416 int packagedb::installeddbread
= 0;
417 int packagedb::installeddbver
= 0;
418 bool packagedb::prepped
= false;
419 packagedb::packagecollection
packagedb::packages
;
420 packagedb::categoriesType
packagedb::categories
;
421 packagedb::packagecollection
packagedb::sourcePackages
;
422 PackageDBActions
packagedb::task
= PackageDB_Install
;
423 packageversion
packagedb::basepkg
;
424 std::vector
<packagemeta
*> packagedb::dependencyOrderedPackages
;
425 SolverPool
packagedb::solver
;
426 SolverSolution
packagedb::solution(packagedb::solver
);
434 ConnectedLoopFinder(void);
437 size_t visit (packagemeta
*pkg
);
442 typedef std::map
<packagemeta
*, size_t> visitMap
;
444 std::stack
<packagemeta
*> nodesInStronglyConnectedComponent
;
447 ConnectedLoopFinder::ConnectedLoopFinder() : visited(0)
449 for (packagedb::packagecollection::iterator i
= db
.packages
.begin ();
450 i
!= db
.packages
.end (); ++i
)
451 visitOrder
.insert(visitMap::value_type(i
->second
, 0));
455 ConnectedLoopFinder::doIt()
457 /* XXX this could be done useing a class to hold both the visitedInIteration and the package
458 * meta reference. Then we could use a range, not an int loop.
460 /* We have to expect dependency loops. These loops break the topological
461 sorting which would be a result of the below algorithm looking for
462 strongly connected components in a directed graph. Unfortunately it's
463 not possible to order a directed graph with loops topologially.
464 So we always have to make sure that the really important packages don't
465 introduce dependency loops, since we can't do this from within setup. */
466 for (packagedb::packagecollection::iterator i
= db
.packages
.begin ();
467 i
!= db
.packages
.end (); ++i
)
469 packagemeta
&pkg (*(i
->second
));
470 if (pkg
.installed
&& !visitOrder
[&pkg
])
473 Log (LOG_BABBLE
) << "Visited: " << visited
<< " nodes out of "
474 << db
.packages
.size() << " while creating dependency order."
479 checkForInstalled (PackageSpecification
*spec
)
482 packagemeta
*required
= db
.findBinary (*spec
);
485 if (spec
->satisfies (required
->installed
)
486 && required
->desired
== required
->installed
)
487 /* done, found a satisfactory installed version that will remain
494 ConnectedLoopFinder::visit(packagemeta
*nodeToVisit
)
496 if (!nodeToVisit
->installed
)
497 /* Can't visit this node, and it is not less than any visted node */
498 return db
.packages
.size() + 1;
500 if (visitOrder
[nodeToVisit
])
501 return visitOrder
[nodeToVisit
];
504 visitOrder
[nodeToVisit
] = visited
;
507 Log (LOG_PLAIN
) << "visited '" << nodeToVisit
->name
<< "', assigned id " << visited
<< endLog
;
510 size_t minimumVisitId
= visited
;
511 nodesInStronglyConnectedComponent
.push(nodeToVisit
);
513 /* walk through each node */
514 const PackageDepends deps
= nodeToVisit
->installed
.depends();
515 PackageDepends::const_iterator dp
= deps
.begin();
516 while (dp
!= deps
.end())
518 /* check for an installed match */
519 if (checkForInstalled (*dp
))
521 /* we found an installed ok package */
522 /* visit it if needed */
523 /* UGLY. Need to refactor. iterators in the outer would help as we could simply
526 const packagedb::packagecollection::iterator n
= db
.packages
.find((*dp
)->packageName());
528 if (n
== db
.packages
.end())
529 Log (LOG_PLAIN
) << "Search for package '" << (*dp
)->packageName() << "' failed." << endLog
;
532 packagemeta
*nodeJustVisited
= n
->second
;
533 minimumVisitId
= std::min (minimumVisitId
, visit (nodeJustVisited
));
536 /* not installed or not available we ignore */
540 if (minimumVisitId
== visitOrder
[nodeToVisit
])
544 popped
= nodesInStronglyConnectedComponent
.top();
545 nodesInStronglyConnectedComponent
.pop();
546 db
.dependencyOrderedPackages
.push_back(popped
);
547 /* mark as displayed in a connected component */
548 visitOrder
[popped
] = db
.packages
.size() + 2;
549 } while (popped
!= nodeToVisit
);
552 return minimumVisitId
;
555 PackageDBConnectedIterator
556 packagedb::connectedBegin()
558 if (!dependencyOrderedPackages
.size())
560 ConnectedLoopFinder doMe
;
562 std::string s
= "Dependency order of packages: ";
564 for (std::vector
<packagemeta
*>::iterator i
=
565 dependencyOrderedPackages
.begin();
566 i
!= dependencyOrderedPackages
.end(); ++i
)
567 s
= s
+ (*i
)->name
+ " ";
568 Log (LOG_BABBLE
) << s
<< endLog
;
570 return dependencyOrderedPackages
.begin();
573 PackageDBConnectedIterator
574 packagedb::connectedEnd()
576 return dependencyOrderedPackages
.end();
580 packagedb::setExistence ()
582 /* binary packages */
583 /* Remove packages that are in the db, not installed, and have no
584 mirror info and are not cached for both binary and source packages. */
585 packagedb::packagecollection::iterator i
= packages
.begin ();
586 while (i
!= packages
.end ())
588 packagemeta
& pkg
= *(i
->second
);
589 if (!pkg
.installed
&& !pkg
.accessible() &&
590 !pkg
.sourceAccessible() )
592 packagemeta
*pkgm
= (*i
).second
;
594 packages
.erase (i
++);
601 /* remove any source packages which are not accessible */
602 vector
<packagemeta
*>::iterator i
= db
.sourcePackages
.begin();
603 while (i
!= db
.sourcePackages
.end())
605 packagemeta
& pkg
= **i
;
606 if (!packageAccessible (pkg
))
608 packagemeta
*pkgm
= *i
;
610 i
= db
.sourcePackages
.erase (i
);
619 packagedb::fillMissingCategory ()
621 for (packagedb::packagecollection::iterator i
= packages
.begin(); i
!= packages
.end(); i
++)
623 if (i
->second
->hasNoCategories())
624 i
->second
->setDefaultCategories();
626 i
->second
->addToCategoryAll();
631 packagedb::defaultTrust (SolverTasks
&q
, SolverSolution::updateMode mode
, bool test
)
633 solution
.update(q
, mode
, test
);
635 // reflect that task list into packagedb
640 packagedb::removeEmptyCategories()
642 std::vector
<std::string
> empty
;
644 for (packagedb::categoriesType::iterator n
= packagedb::categories
.begin();
645 n
!= packagedb::categories
.end(); ++n
)
646 if (!n
->second
.size())
648 empty
.push_back(n
->first
);
651 for (unsigned int i
= 0; i
< empty
.size(); ++i
)
653 packagedb::categoriesType::iterator n
= packagedb::categories
.find(empty
[i
]);
654 Log (LOG_BABBLE
) << "Removing empty category " << empty
[i
] << endLog
;
655 if (n
!= packagedb::categories
.end())
656 packagedb::categories
.erase(n
);
661 packagedb::guessUserPicked()
664 Assume that any non-base installed package which is a dependency of an
665 installed package wasn't user_picked
667 i.e. only installed packages which aren't in the base category, and aren't
668 a dependency of any installed package are user_picked
671 /* First mark all installed non-base packages */
672 for (packagedb::packagecollection::iterator i
= packages
.begin ();
673 i
!= packages
.end (); ++i
)
675 packagemeta
& pkgm
= *(i
->second
);
677 if (pkgm
.categories
.find ("Base") != pkgm
.categories
.end ())
681 pkgm
.user_picked
= TRUE
;
684 /* Then clear the mark for all dependencies of all installed packages */
685 for (packagedb::packagecollection::iterator i
= packages
.begin ();
686 i
!= packages
.end (); ++i
)
688 packagemeta
& pkgm
= *(i
->second
);
693 /* walk through each node */
694 const PackageDepends deps
= pkgm
.installed
.depends();
695 std::vector
<PackageSpecification
*>::const_iterator dp
= deps
.begin();
696 while (dp
!= deps
.end())
698 /* check for an installed match */
699 if (checkForInstalled(*dp
))
701 const packagedb::packagecollection::iterator n
= packages
.find((*dp
)->packageName());
702 if (n
!= packages
.end())
704 packagemeta
*pkgm2
= n
->second
;
705 pkgm2
->user_picked
= FALSE
;
707 /* skip to next and clause */
717 packagedb::fixup_source_package_ids()
719 for (packagecollection::iterator i
= packages
.begin ();
720 i
!= packages
.end (); ++i
)
722 packagemeta
&pkgm
= *(i
->second
);
724 for (std::set
<packageversion
>::iterator i
= pkgm
.versions
.begin();
725 i
!= pkgm
.versions
.end(); ++i
)
727 /* Some packages really have no source, indicated by no [sS]ource:
728 line in setup.ini, which becomes an empty source package name */
729 const std::string spkg
= i
->sourcePackageName();
733 /* Otherwise, we need to find the source package and fix up the source
735 packageversion spkg_id
= findSourceVersion(PackageSpecification(spkg
,
736 i
->Canonical_version()));
740 if (i
->sourcePackage() != spkg_id
)
741 i
->fixup_spkg_id(spkg_id
);
745 Log (LOG_BABBLE
) << "No source package for '" << i
->Name() << "' " << i
->Canonical_version() << ", source package name '" << spkg
<< "'" << endLog
;
750 solver
.internalize();
756 /* make packagedb ready for use for chooser */
764 fixup_source_package_ids();
765 removeEmptyCategories();
767 /* XXX: this needs to be broken out somewhere where it can do progress
768 reporting, as it can take a long time... */
769 if (source
== IDC_SOURCE_DOWNLOAD
|| source
==IDC_SOURCE_LOCALDIR
)
770 packagemeta::ScanDownloadedFiles (MirrorOption
);
773 fillMissingCategory ();
779 packagedb::noChanges ()
781 for (packagecollection::iterator i
= packages
.begin();
782 i
!= packages
.end(); i
++)
784 packagemeta
*pkg
= i
->second
;
785 pkg
->set_action(packagemeta::NoChange_action
, pkg
->installed
);
786 pkg
->default_version
= pkg
->installed
;