Display broken packages when depedencies are unsolvable
[mspang/inapt.git] / inapt.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <getopt.h>
4 #include <sys/utsname.h>
5 #include <iostream>
6 #include <cstdio>
7 #include <fstream>
8 #include <set>
9 #include <apt-pkg/pkgcache.h>
10 #include <apt-pkg/cachefile.h>
11 #include <apt-pkg/dpkgdb.h>
12 #include <apt-pkg/progress.h>
13 #include <apt-pkg/init.h>
14 #include <apt-pkg/error.h>
15 #include <apt-pkg/algorithms.h>
16 #include <apt-pkg/sptr.h>
17 #include <apt-pkg/acquire-item.h>
18
19 #include "inapt.h"
20 #include "util.h"
21 #include "acqprogress.h"
22
23 char *prog = NULL;
24
25 static struct option opts[] = {
26     { "auto-remove", 0, NULL, 'g' },
27     { "simulate", 0, NULL, 's' },
28     { "purge", 0, NULL, 'u' },
29     { NULL, 0, NULL, '\0' },
30 };
31
32 bool run_install(pkgCacheFile &cache) {
33    if (_config->FindB("Inapt::Purge", false))
34       for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
35          if (!i.Purge() && cache[i].Mode == pkgDepCache::ModeDelete)
36             cache->MarkDelete(i, true);
37
38    if (cache->BrokenCount())
39        fatal("broken packages during install");
40
41    if (!cache->DelCount() && !cache->InstCount() && !cache->BadCount())
42       return true;
43
44    pkgRecords Recs (cache);
45    if (_error->PendingError())
46       return false;
47
48    FileFd Lock;
49    Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
50    if (_error->PendingError())
51        return _error->Error("Unable to lock the download directory");
52
53    unsigned int width = 80;
54    AcqTextStatus status (width, 0);
55    pkgAcquire Fetcher (&status);
56
57    pkgSourceList List;
58    if (List.ReadMainList() == false)
59       return _error->Error("The list of sources could not be read");
60
61    SPtr<pkgPackageManager> PM = _system->CreatePM(cache);
62    if (PM->GetArchives(&Fetcher, &List, &Recs) == false ||
63        _error->PendingError())
64       return false;
65
66   if (Fetcher.Run() == pkgAcquire::Failed)
67      return false;
68
69   bool Failed = false;
70   for (pkgAcquire::ItemIterator i = Fetcher.ItemsBegin(); i != Fetcher.ItemsEnd(); i++) {
71      if ((*i)->Status != pkgAcquire::Item::StatDone || (*i)->Complete != true) {
72          fprintf(stderr,("Failed to fetch %s  %s\n"),(*i)->DescURI().c_str(),
73                  (*i)->ErrorText.c_str());
74          Failed = true;
75      }
76   }
77
78   if (Failed)
79      return _error->Error("Unable to fetch some archives");
80
81   _system->UnLock();
82
83   pkgPackageManager::OrderResult Res = PM->DoInstall(-1);
84   if (Res == pkgPackageManager::Completed)
85      return true;
86
87   return false;
88 }
89
90 void run_autoremove(pkgCacheFile &cache) {
91     bool purge = _config->FindB("Inapt::Purge", false);
92
93     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
94         if (cache[i].Garbage) {
95             debug("autoremove: %s", i.Name());
96             cache->MarkDelete(i, purge);
97         }
98     }
99
100     if (cache->BrokenCount())
101         fatal("automatic removal broke packages");
102 }
103
104 static void usage() {
105     fprintf(stderr, "Usage: %s [filename..]\n", prog);
106     exit(2);
107 }
108
109 static bool test_macro(const char *macro, std::set<std::string> *defines) {
110     return (*macro != '!' && defines->find(macro) != defines->end())
111             || (*macro == '!' && defines->find(macro + 1) == defines->end());
112 }
113
114 static pkgCache::PkgIterator eval_pkg(inapt_package *package, pkgCacheFile &cache) {
115     pkgCache::PkgIterator pkg;
116
117     for (std::vector<std::string>::iterator i = package->alternates.begin(); i != package->alternates.end(); i++) {
118         pkgCache::PkgIterator tmp = cache->FindPkg(*i);
119
120         /* no such package */
121         if (tmp.end())
122             continue;
123
124         /* real package */
125         if (cache[tmp].CandidateVer) {
126             pkg = tmp;
127             break;
128         }
129
130         /* virtual package */
131         if (tmp->ProvidesList) {
132             if (!tmp.ProvidesList()->NextProvides) {
133                 pkgCache::PkgIterator provide = tmp.ProvidesList().OwnerPkg();
134                 if (package->action == inapt_action::INSTALL) {
135                     debug("selecting %s instead of %s", provide.Name(), tmp.Name());
136                     pkg = provide;
137                     break;
138                 } else {
139                     debug("will not remove %s instead of virtual package %s", provide.Name(), tmp.Name());
140                 }
141             } else {
142                 debug("%s is a virtual package", tmp.Name());
143             }
144         } else {
145             debug("%s is a virtual packages with no provides", tmp.Name());
146         }
147     }
148
149     if (pkg.end()) {
150         if (package->alternates.size() == 1) {
151             _error->Error("%s:%d: No such package: %s", package->filename, package->linenum, package->alternates[0].c_str());
152         } else {
153             std::vector<std::string>::iterator i = package->alternates.begin();
154             std::string message = *(i++);
155             while (i != package->alternates.end()) {
156                 message.append(", ");
157                 message.append(*(i++));
158             }
159             _error->Error("%s:%d: No alternative available: %s", package->filename, package->linenum, message.c_str());
160         }
161     }
162
163     return pkg;
164 }
165
166 static bool test_macros(vector<std::string> *macros, std::set<std::string> *defines) {
167     bool ok = true;
168     for (vector<std::string>::iterator j = macros->begin(); j < macros->end(); j++) {
169         if (!test_macro((*j).c_str(), defines)) {
170             ok = false;
171             break;
172         }
173     }
174     return ok;
175 }
176
177 static void eval_action(inapt_action *action, std::set<std::string> *defines, std::vector<inapt_package *> *final_actions) {
178     for (vector<inapt_package *>::iterator i = action->packages.begin(); i < action->packages.end(); i++) {
179         if (test_macros(&(*i)->predicates, defines))
180             final_actions->push_back(*i);
181     }
182 }
183
184 static void eval_block(inapt_block *block, std::set<std::string> *defines, std::vector<inapt_package *> *final_actions) {
185     if (!block)
186         return;
187
188     for (vector<inapt_action *>::iterator i = block->actions.begin(); i < block->actions.end(); i++)
189         if (test_macros(&(*i)->predicates, defines))
190             eval_action(*i, defines, final_actions);
191
192     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
193         if (test_macro((*i)->condition, defines))
194             eval_block((*i)->then_block, defines, final_actions);
195         else
196             eval_block((*i)->else_block, defines, final_actions);
197     }
198 }
199
200 static void eval_profiles(inapt_block *block, std::set<std::string> *defines) {
201     if (!block)
202         return;
203
204     for (vector<inapt_profiles *>::iterator i = block->profiles.begin(); i < block->profiles.end(); i++)
205         if (test_macros(&(*i)->predicates, defines))
206             for (vector<std::string>::iterator j = (*i)->profiles.begin(); j != (*i)->profiles.end(); j++)
207                 defines->insert(*j);
208
209     for (vector<inapt_conditional *>::iterator i = block->children.begin(); i < block->children.end(); i++) {
210         if (test_macro((*i)->condition, defines))
211             eval_profiles((*i)->then_block, defines);
212         else
213             eval_profiles((*i)->else_block, defines);
214     }
215 }
216
217 static void dump_nondownloadable(pkgCacheFile &cache) {
218     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
219        if (i.CurrentVer() && !i.CurrentVer().Downloadable())
220            debug("package %s version %s is not downloadable", i.Name(), i.CurrentVer().VerStr());
221 }
222
223 static void dump_actions(pkgCacheFile &cache) {
224     debug("inst %lu del %lu keep %lu broken %lu bad %lu",
225                     cache->InstCount(), cache->DelCount(), cache->KeepCount(),
226                     cache->BrokenCount(), cache->BadCount());
227     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++) {
228        if (cache[i].Install())
229          debug("installing %s", i.Name());
230        if (cache[i].Delete())
231          debug("removing %s", i.Name());
232        if (cache[i].InstBroken())
233          debug("install broken %s", i.Name());
234        if (cache[i].NowBroken())
235          debug("now broken %s", i.Name());
236     }
237 }
238
239 static void show_breakage(pkgCacheFile &cache) {
240     fprintf(stderr, "fatal: Unable to solve dependencies\n");
241     fprintf(stderr, "The following packages are broken:");
242     for (pkgCache::PkgIterator i = cache->PkgBegin(); !i.end(); i++)
243         if (cache[i].NowBroken() || cache[i].InstBroken())
244             fprintf(stderr, " %s", i.Name());
245     fprintf(stderr, "\n");
246     exit(1);
247 }
248
249 static void exec_actions(std::vector<inapt_package *> *final_actions) {
250     int marked = 0;
251     bool purge = _config->FindB("Inapt::Purge", false);
252
253     pkgInitConfig(*_config);
254     pkgInitSystem(*_config, _system);
255
256 //     _config->Set("Debug::pkgProblemResolver", true);
257 //     _config->Set("Debug::pkgAutoRemove", true);
258
259     OpTextProgress prog;
260     pkgCacheFile cache;
261
262     if (cache.Open(prog) == false)
263         return;
264
265     pkgDepCache::ActionGroup group (cache);
266
267     for (vector<inapt_package *>::iterator i = final_actions->begin(); i != final_actions->end(); i++)
268         (*i)->pkg = eval_pkg(*i, cache);
269
270     if (_error->PendingError())
271         return;
272
273     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
274         pkgCache::PkgIterator k = (*i)->pkg;
275         switch ((*i)->action) {
276             case inapt_action::INSTALL:
277                 if (!k.CurrentVer() || cache[k].Delete()) {
278                     debug("install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
279                     cache->MarkInstall(k, true);
280                 }
281                 break;
282             case inapt_action::REMOVE:
283                 break;
284             default:
285                 fatal("uninitialized action");
286         }
287     }
288
289     for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++) {
290         pkgCache::PkgIterator k = (*i)->pkg;
291         switch ((*i)->action) {
292             case inapt_action::INSTALL:
293                 if ((!k.CurrentVer() && !cache[k].Install()) || cache[k].Delete()) {
294                     debug("install %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
295                     cache->MarkInstall(k, false);
296                 }
297                 if (cache[k].Flags & pkgCache::Flag::Auto) {
298                     debug("marking %s as manually installed", (*i)->pkg.Name());
299                     cache->MarkAuto(k, false);
300                     marked++;
301                 }
302                 break;
303             case inapt_action::REMOVE:
304                 if ((k.CurrentVer() && !cache[k].Delete()) || cache[k].Install())
305                     debug("remove %s %s:%d", (*i)->pkg.Name(), (*i)->filename, (*i)->linenum);
306
307                 /* always mark so purge works */
308                 cache->MarkDelete(k, purge);
309                 break;
310             default:
311                 fatal("uninitialized action");
312         }
313     }
314
315     if (_error->PendingError())
316         return;
317
318     dump_nondownloadable(cache);
319     dump_actions(cache);
320
321     if (cache->BrokenCount()) {
322         pkgProblemResolver fix (cache);
323         for (vector<inapt_package *>::iterator i = final_actions->begin(); i < final_actions->end(); i++)
324             fix.Protect((*i)->pkg);
325         fix.Resolve();
326
327         if (cache->BrokenCount())
328             show_breakage(cache);
329     }
330
331     if (_config->FindB("Inapt::AutomaticRemove", false)) {
332         cache->MarkAndSweep();
333         run_autoremove(cache);
334     }
335
336     if (_config->FindB("Inapt::Simulate", false)) {
337         pkgSimulate PM (cache);
338         PM.DoInstall(-1);
339         return;
340     }
341
342     run_install(cache);
343     if (_error->PendingError())
344         return;
345
346     if (marked) {
347         if (_config->FindB("Inapt::Simulate", false)) {
348             debug("marked %d packages", marked);
349         } else {
350             debug("marked %d packages, writing state file", marked);
351             cache->writeStateFile(NULL);
352         }
353     }
354 }
355
356 static void debug_profiles(std::set<std::string> *defines) {
357     std::string profiles = "profiles:";
358
359     for (std::set<std::string>::iterator i = defines->begin(); i != defines->end(); i++) {
360         profiles.append(" ");
361         profiles.append(*i);
362     }
363
364     debug("%s", profiles.c_str());
365 }
366
367 static void auto_profiles(std::set<std::string> *defines) {
368     struct utsname uts;
369     if (uname(&uts))
370         fatalpe("uname");
371     defines->insert(uts.nodename);
372 }
373
374 int main(int argc, char *argv[]) {
375     int opt;
376
377     std::set<std::string> defines;
378
379     prog = xstrdup(basename(argv[0]));
380     while ((opt = getopt_long(argc, argv, "p:sdg", opts, NULL)) != -1) {
381         switch (opt) {
382             case 'p':
383                 defines.insert(optarg);
384                 break;
385             case '?':
386                 usage();
387                 break;
388             case 'g':
389                 _config->Set("Inapt::AutomaticRemove", true);
390                 break;
391             case 's':
392                 _config->Set("Inapt::Simulate", true);
393                 break;
394             case 'u':
395                 _config->Set("Inapt::Purge", true);
396                 break;
397             case 'd':
398                 debug_enabled = true;
399                 break;
400             default:
401                 fatal("error parsing arguments");
402         }
403     }
404
405     int num_files = argc - optind;
406
407     inapt_block context;
408     std::vector<inapt_package *> final_actions;
409
410     if (!num_files)
411         parser(NULL, &context);
412
413     while (num_files--)
414         parser(argv[optind++], &context);
415
416     auto_profiles(&defines);
417     eval_profiles(&context, &defines);
418     debug_profiles(&defines);
419     eval_block(&context, &defines, &final_actions);
420     exec_actions(&final_actions);
421
422     if (_error->PendingError()) {
423         _error->DumpErrors();
424         exit(1);
425     }
426 }