]> cygwin.com Git - cygwin-apps/setup.git/blob - package_db.cc
Use UTF8 encoding in .rc file
[cygwin-apps/setup.git] / package_db.cc
1 /*
2 * Copyright (c) 2001, 2003 Robert Collins.
3 *
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.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by Robert Collins <rbtcollins@hotmail.com>
13 *
14 */
15
16 /* this is the package database class.
17 * It lists all known packages, including custom ones, ones from a mirror and
18 * installed ones.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <strings.h>
25 #include <algorithm>
26 #include <sstream>
27 #if HAVE_ERRNO_H
28 #include <errno.h>
29 #endif
30
31 #include "io_stream.h"
32 #include "compress.h"
33
34 #include "filemanip.h"
35 #include "package_version.h"
36 #include "package_db.h"
37 #include "package_meta.h"
38 #include "Exception.h"
39 #include "Generic.h"
40 #include "LogSingleton.h"
41 #include "resource.h"
42 #include "libsolv.h"
43 #include "csu_util/version_compare.h"
44 #include "getopt++/BoolOption.h"
45
46 static BoolOption MirrorOption (false, 'm', "mirror-mode", "Skip package availability check when installing from local directory (requires local directory to be clean mirror!)");
47
48 packagedb::packagedb ()
49 {
50 }
51
52 void
53 packagedb::init ()
54 {
55 installeddbread = 0;
56 installeddbver = 0;
57 prepped = false;
58
59 packages.clear();
60 sourcePackages.clear();
61 categories.clear();
62 solver.clear();
63 solution.clear();
64 basepkg = packageversion();
65 dependencyOrderedPackages.clear();
66 }
67
68 void
69 packagedb::read ()
70 {
71 if (!installeddbread)
72 {
73 /* Read in the local installation database. */
74 io_stream *db = 0;
75 db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
76 installeddbread = 1;
77 if (!db)
78 return;
79 char line[1000], pkgname[1000];
80
81 if (db->gets (line, 1000))
82 {
83 /* Look for header line (absent in version 1) */
84 int instsz;
85 int dbver;
86 sscanf (line, "%s %d", pkgname, &instsz);
87 if (!strcasecmp (pkgname, "INSTALLED.DB"))
88 dbver = instsz;
89 else
90 dbver = 1;
91 delete db;
92 db = 0;
93
94 Log (LOG_BABBLE) << "INSTALLED.DB version " << dbver << endLog;
95
96 if (dbver <= 3)
97 {
98 char inst[1000];
99
100 db =
101 io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
102
103 // skip over already-parsed header line
104 if (dbver >= 2)
105 db->gets (line, 1000);
106
107 while (db->gets (line, 1000))
108 {
109 int parseable;
110 int user_picked = 0;
111 pkgname[0] = '\0';
112 inst[0] = '\0';
113
114 int res = sscanf (line, "%s %s %d", pkgname, inst, &user_picked);
115
116 if (res < 3 || pkgname[0] == '\0' || inst[0] == '\0')
117 continue;
118
119 fileparse f;
120 parseable = parse_filename (inst, f);
121 if (!parseable)
122 continue;
123
124 SolverPool::addPackageData data;
125 data.reponame = "_installed";
126 data.version = f.ver;
127 data.type = package_binary;
128
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;
136 data.sdesc = "";
137 data.ldesc = "";
138 data.stability = TRUST_UNKNOWN;
139 data.spkg = PackageSpecification(std::string(pkgname) + "-src", f.ver);
140
141 // supplement this with sdesc, source, and stability
142 // information from setup.ini, if possible...
143 packageversion pv = findBinaryVersion(PackageSpecification(pkgname, f.ver));
144 PackageDepends dep;
145 PackageDepends obs;
146 PackageDepends prov;
147 PackageDepends conf;
148 if (pv)
149 {
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();
156 dep = pv.depends();
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;
164 }
165 else
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.
170 {
171 packagemeta *pkgm = findBinary(PackageSpecification(pkgname));
172 if (pkgm)
173 {
174 data.sdesc = pkgm->curr.SDesc();
175 data.ldesc = pkgm->curr.LDesc();
176 if (pkgm->curr
177 && version_compare (f.ver, pkgm->curr.Canonical_version()) > 0)
178 data.stability = TRUST_TEST;
179 }
180 }
181
182 packagemeta *pkg = packagedb::addBinary (pkgname, data);
183
184 pkg->set_installed_version (f.ver);
185
186 if (dbver == 3)
187 pkg->user_picked = (user_picked & 1);
188
189 }
190 delete db;
191 db = 0;
192 }
193 else
194 {
195 fatal(NULL, IDS_INSTALLEDB_VERSION);
196 }
197
198 installeddbver = dbver;
199 }
200 }
201 solver.internalize();
202 }
203
204 /* Create the fictitious basepkg */
205 void
206 packagedb::makeBase()
207 {
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();
221
222 PackageDepends dep;
223 for (std::vector <packagemeta *>::const_iterator i = categories["Base"].begin();
224 i != categories["Base"].end(); i++)
225 {
226 packagemeta *pkg = *i;
227 PackageSpecification *spec = new PackageSpecification(pkg->name);
228 dep.push_back(spec);
229 }
230 data.requires = &dep;
231
232 basepkg = solver.addPackage("base", data);
233 /* We don't register this in packagemeta */
234 }
235
236 /* Create the fictitious windows package */
237 void
238 packagedb::makeWindows()
239 {
240 std::stringstream v;
241 v << OSMajorVersion() << "." << OSMinorVersion() << "." << OSBuildNumber();
242
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;
255
256 solver.addPackage("_windows", data);
257 /* We don't register this in packagemeta */
258 }
259
260 /* Add a package version to the packagedb */
261 packagemeta *
262 packagedb::addBinary (const std::string &pkgname,
263 const SolverPool::addPackageData &pkgdata)
264 {
265 /* If pkgname isn't already in packagedb, add a packagemeta */
266 packagemeta *pkg = findBinary (PackageSpecification(pkgname));
267 if (!pkg)
268 {
269 pkg = new packagemeta (pkgname);
270 packages.insert (packagedb::packagecollection::value_type(pkgname, pkg));
271 }
272
273 /* Create the SolvableVersion and register it in packagemeta */
274 pkg->add_version (pkgdata);
275
276 return pkg;
277 }
278
279 packageversion
280 packagedb::addSource (const std::string &pkgname,
281 const SolverPool::addPackageData &pkgdata)
282 {
283 /* If pkgname isn't already in packagedb, add a packagemeta */
284 packagemeta *pkg = findSource (PackageSpecification(pkgname));
285 if (!pkg)
286 {
287 pkg = new packagemeta (pkgname);
288 sourcePackages.insert (packagedb::packagecollection::value_type(pkgname, pkg));
289 }
290
291 /* Create the SolvableVersion and register it in packagemeta */
292 SolvableVersion sv = pkg->add_version (pkgdata);
293
294 return sv;
295 }
296
297 int
298 packagedb::flush ()
299 {
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";
303
304 io_stream::mkpath_p (PATH_TO_FILE, ndbn, 0755);
305
306 io_stream *ndb = io_stream::open (ndbn, "wb", 0644);
307
308 // XXX if this failed, try removing any existing .new database?
309 if (!ndb)
310 return errno ? errno : 1;
311
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)
315 {
316 packagemeta & pkgm = *(i->second);
317 if (pkgm.installed)
318 {
319 /*
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
323 (bit 0).
324 */
325 std::string line;
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());
330 }
331 }
332
333 delete ndb;
334
335 io_stream::remove (odbn);
336
337 if (io_stream::move (ndbn, odbn))
338 return errno ? errno : 1;
339 return 0;
340 }
341
342 void
343 packagedb::upgrade()
344 {
345 if (installeddbver < 3)
346 {
347 /* Guess which packages were user_picked. This has to take place after
348 setup.ini has been parsed as it needs dependency information. */
349 guessUserPicked();
350 installeddbver = 3;
351 }
352 }
353
354 packagemeta *
355 packagedb::findBinary (PackageSpecification const &spec) const
356 {
357 packagedb::packagecollection::iterator n = packages.find(spec.packageName());
358 if (n != packages.end())
359 {
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))
364 return &pkgm;
365 }
366 return NULL;
367 }
368
369 packageversion
370 packagedb::findBinaryVersion (PackageSpecification const &spec) const
371 {
372 packagedb::packagecollection::iterator n = packages.find(spec.packageName());
373 if (n != packages.end())
374 {
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))
379 return *i;
380 }
381 return packageversion();
382 }
383
384 packagemeta *
385 packagedb::findSource (PackageSpecification const &spec) const
386 {
387 packagedb::packagecollection::iterator n = sourcePackages.find(spec.packageName());
388 if (n != sourcePackages.end())
389 {
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))
394 return &pkgm;
395 }
396 return NULL;
397 }
398
399 packageversion
400 packagedb::findSourceVersion (PackageSpecification const &spec) const
401 {
402 packagedb::packagecollection::iterator n = sourcePackages.find(spec.packageName());
403 if (n != sourcePackages.end())
404 {
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))
409 return *i;
410 }
411 return packageversion();
412 }
413
414 /* static members */
415
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);
427
428 #include <stack>
429
430 class
431 ConnectedLoopFinder
432 {
433 public:
434 ConnectedLoopFinder(void);
435 void doIt(void);
436 private:
437 size_t visit (packagemeta *pkg);
438
439 packagedb db;
440 size_t visited;
441
442 typedef std::map<packagemeta *, size_t> visitMap;
443 visitMap visitOrder;
444 std::stack<packagemeta *> nodesInStronglyConnectedComponent;
445 };
446
447 ConnectedLoopFinder::ConnectedLoopFinder() : visited(0)
448 {
449 for (packagedb::packagecollection::iterator i = db.packages.begin ();
450 i != db.packages.end (); ++i)
451 visitOrder.insert(visitMap::value_type(i->second, 0));
452 }
453
454 void
455 ConnectedLoopFinder::doIt()
456 {
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.
459 */
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)
468 {
469 packagemeta &pkg (*(i->second));
470 if (pkg.installed && !visitOrder[&pkg])
471 visit (&pkg);
472 }
473 Log (LOG_BABBLE) << "Visited: " << visited << " nodes out of "
474 << db.packages.size() << " while creating dependency order."
475 << endLog;
476 }
477
478 static bool
479 checkForInstalled (PackageSpecification *spec)
480 {
481 packagedb db;
482 packagemeta *required = db.findBinary (*spec);
483 if (!required)
484 return false;
485 if (spec->satisfies (required->installed)
486 && required->desired == required->installed )
487 /* done, found a satisfactory installed version that will remain
488 installed */
489 return true;
490 return false;
491 }
492
493 size_t
494 ConnectedLoopFinder::visit(packagemeta *nodeToVisit)
495 {
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;
499
500 if (visitOrder[nodeToVisit])
501 return visitOrder[nodeToVisit];
502
503 ++visited;
504 visitOrder[nodeToVisit] = visited;
505
506 #if DEBUG
507 Log (LOG_PLAIN) << "visited '" << nodeToVisit->name << "', assigned id " << visited << endLog;
508 #endif
509
510 size_t minimumVisitId = visited;
511 nodesInStronglyConnectedComponent.push(nodeToVisit);
512
513 /* walk through each node */
514 const PackageDepends deps = nodeToVisit->installed.depends();
515 PackageDepends::const_iterator dp = deps.begin();
516 while (dp != deps.end())
517 {
518 /* check for an installed match */
519 if (checkForInstalled (*dp))
520 {
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
524 * vist the iterator
525 */
526 const packagedb::packagecollection::iterator n = db.packages.find((*dp)->packageName());
527
528 if (n == db.packages.end())
529 Log (LOG_PLAIN) << "Search for package '" << (*dp)->packageName() << "' failed." << endLog;
530 else
531 {
532 packagemeta *nodeJustVisited = n->second;
533 minimumVisitId = std::min (minimumVisitId, visit (nodeJustVisited));
534 }
535 }
536 /* not installed or not available we ignore */
537 ++dp;
538 }
539
540 if (minimumVisitId == visitOrder[nodeToVisit])
541 {
542 packagemeta *popped;
543 do {
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);
550 }
551
552 return minimumVisitId;
553 }
554
555 PackageDBConnectedIterator
556 packagedb::connectedBegin()
557 {
558 if (!dependencyOrderedPackages.size())
559 {
560 ConnectedLoopFinder doMe;
561 doMe.doIt();
562 std::string s = "Dependency order of packages: ";
563
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;
569 }
570 return dependencyOrderedPackages.begin();
571 }
572
573 PackageDBConnectedIterator
574 packagedb::connectedEnd()
575 {
576 return dependencyOrderedPackages.end();
577 }
578
579 void
580 packagedb::setExistence ()
581 {
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 ())
587 {
588 packagemeta & pkg = *(i->second);
589 if (!pkg.installed && !pkg.accessible() &&
590 !pkg.sourceAccessible() )
591 {
592 packagemeta *pkgm = (*i).second;
593 delete pkgm;
594 packages.erase (i++);
595 }
596 else
597 ++i;
598 }
599
600 #if 0
601 /* remove any source packages which are not accessible */
602 vector <packagemeta *>::iterator i = db.sourcePackages.begin();
603 while (i != db.sourcePackages.end())
604 {
605 packagemeta & pkg = **i;
606 if (!packageAccessible (pkg))
607 {
608 packagemeta *pkgm = *i;
609 delete pkgm;
610 i = db.sourcePackages.erase (i);
611 }
612 else
613 ++i;
614 }
615 #endif
616 }
617
618 void
619 packagedb::fillMissingCategory ()
620 {
621 for (packagedb::packagecollection::iterator i = packages.begin(); i != packages.end(); i++)
622 {
623 if (i->second->hasNoCategories())
624 i->second->setDefaultCategories();
625
626 i->second->addToCategoryAll();
627 }
628 }
629
630 void
631 packagedb::defaultTrust (SolverTasks &q, SolverSolution::updateMode mode, bool test)
632 {
633 solution.update(q, mode, test);
634
635 // reflect that task list into packagedb
636 solution.trans2db();
637 }
638
639 void
640 packagedb::removeEmptyCategories()
641 {
642 std::vector<std::string> empty;
643
644 for (packagedb::categoriesType::iterator n = packagedb::categories.begin();
645 n != packagedb::categories.end(); ++n)
646 if (!n->second.size())
647 {
648 empty.push_back(n->first);
649 }
650
651 for (unsigned int i = 0; i < empty.size(); ++i)
652 {
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);
657 }
658 }
659
660 void
661 packagedb::guessUserPicked()
662 {
663 /*
664 Assume that any non-base installed package which is a dependency of an
665 installed package wasn't user_picked
666
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
669 */
670
671 /* First mark all installed non-base packages */
672 for (packagedb::packagecollection::iterator i = packages.begin ();
673 i != packages.end (); ++i)
674 {
675 packagemeta & pkgm = *(i->second);
676
677 if (pkgm.categories.find ("Base") != pkgm.categories.end ())
678 continue;
679
680 if (pkgm.installed)
681 pkgm.user_picked = TRUE;
682 }
683
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)
687 {
688 packagemeta & pkgm = *(i->second);
689
690 if (!pkgm.installed)
691 continue;
692
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())
697 {
698 /* check for an installed match */
699 if (checkForInstalled(*dp))
700 {
701 const packagedb::packagecollection::iterator n = packages.find((*dp)->packageName());
702 if (n != packages.end())
703 {
704 packagemeta *pkgm2 = n->second;
705 pkgm2->user_picked = FALSE;
706 }
707 /* skip to next and clause */
708 ++dp;
709 continue;
710 }
711 ++dp;
712 }
713 }
714 }
715
716 void
717 packagedb::fixup_source_package_ids()
718 {
719 for (packagecollection::iterator i = packages.begin ();
720 i != packages.end (); ++i)
721 {
722 packagemeta &pkgm = *(i->second);
723
724 for (std::set<packageversion>::iterator i = pkgm.versions.begin();
725 i != pkgm.versions.end(); ++i)
726 {
727 /* If spkg_id is already known for this package, there's nothing to
728 fix. */
729 if (i->sourcePackage())
730 continue;
731
732 /* Some packages really have no source, indicated by no [sS]ource:
733 line in setup.ini, which becomes an empty source package name */
734 const std::string spkg = i->sourcePackageName();
735 if (spkg.empty())
736 continue;
737
738 /* Otherwise, we need to find the source package and fix up the source
739 package id*/
740 packageversion spkg_id = findSourceVersion(PackageSpecification(spkg,
741 i->Canonical_version()));
742
743 if (spkg_id)
744 {
745 i->fixup_spkg_id(spkg_id);
746 }
747 else
748 {
749 Log (LOG_BABBLE) << "No source package for '" << i->Name() << "' " << i->Canonical_version() << ", source package name '" << spkg << "'" << endLog;
750 }
751 }
752 }
753 }
754
755 void
756 packagedb::prep()
757 {
758 /* make packagedb ready for use for chooser */
759 if (prepped)
760 return;
761
762 makeBase();
763 makeWindows();
764 read();
765 upgrade();
766 fixup_source_package_ids();
767 removeEmptyCategories();
768
769 /* XXX: this needs to be broken out somewhere where it can do progress
770 reporting, as it can take a long time... */
771 if (source == IDC_SOURCE_DOWNLOAD || source ==IDC_SOURCE_LOCALDIR)
772 packagemeta::ScanDownloadedFiles (MirrorOption);
773
774 setExistence ();
775 fillMissingCategory ();
776
777 prepped = true;
778 }
779
780 void
781 packagedb::noChanges ()
782 {
783 for (packagecollection::iterator i = packages.begin();
784 i != packages.end(); i++)
785 {
786 packagemeta *pkg = i->second;
787 pkg->set_action(packagemeta::NoChange_action, pkg->installed);
788 pkg->default_version = pkg->installed;
789 }
790 }
This page took 0.073337 seconds and 5 git commands to generate.