/* FIXME: do we need to init the desired fields ? */
static int ta[] = { IDC_CHOOSE_KEEP, IDC_CHOOSE_CURR, IDC_CHOOSE_EXP, 0 };
rbset (GetHWND (), ta, IDC_CHOOSE_CURR);
+ changeTrust (TRUST_CURR);
+
ClearBusy ();
}
pkg.pick(false);
}
chooser->refresh();
+
+ PrereqChecker::setUpgrade(false);
}
void
SetBusy ();
chooser->defaultTrust (aTrust);
chooser->refresh();
- PrereqChecker::setTrust (aTrust);
+ PrereqChecker::setUpgrade(true);
+ PrereqChecker::setTestPackages(aTrust == TRUST_TEST);
ClearBusy ();
}
#include "solv/evr.h"
#include "LogSingleton.h"
+#include <iomanip>
// ---------------------------------------------------------------------------
// Utility functions for mapping between Operators and Relation Ids
repodata_internalize(i->second->data);
}
}
+
+void
+SolverPool::use_test_packages(bool use_test_packages)
+{
+ // Only enable repos containing test packages if wanted
+ for (RepoList::iterator i = repos.begin();
+ i != repos.end();
+ i++)
+ {
+ if (i->second->test)
+ {
+ i->second->repo->disabled = !use_test_packages;
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+// implements class SolverSolution
+//
+// A wrapper around the libsolv solver
+// ---------------------------------------------------------------------------
+
+SolverSolution::~SolverSolution()
+{
+ if (solv)
+ {
+ solver_free(solv);
+ solv = NULL;
+ }
+}
+
+static
+std::ostream &operator<<(std::ostream &stream,
+ SolverTransaction::transType type)
+{
+ switch (type)
+ {
+ case SolverTransaction::transInstall:
+ stream << "install";
+ break;
+ case SolverTransaction::transErase:
+ stream << "erase";
+ break;
+ default:
+ stream << "unknown";
+ }
+ return stream;
+}
+
+bool
+SolverSolution::update(SolverTasks &tasks, bool update, bool use_test_packages, bool include_source)
+{
+ Log (LOG_PLAIN) << "solving: " << tasks.tasks.size() << " tasks," <<
+ " update: " << (update ? "yes" : "no") << "," <<
+ " use test packages: " << (use_test_packages ? "yes" : "no") << "," <<
+ " include_source: " << (include_source ? "yes" : "no") << endLog;
+
+ pool.use_test_packages(use_test_packages);
+
+ Queue job;
+ queue_init(&job);
+ // solver accepts a queue containing pairs of (cmd, id) tasks
+ // cmd is job and selection flags ORed together
+ for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
+ i != tasks.tasks.end();
+ i++)
+ {
+ const SolvableVersion &sv = (*i).first;
+
+ switch ((*i).second)
+ {
+ case SolverTasks::taskInstall:
+ queue_push2(&job, SOLVER_INSTALL | SOLVER_SOLVABLE, sv.id);
+ break;
+ case SolverTasks::taskUninstall:
+ queue_push2(&job, SOLVER_ERASE | SOLVER_SOLVABLE, sv.id);
+ break;
+ case SolverTasks::taskReinstall:
+ // we don't know how to ask solver for this, so we just add the erase
+ // and install later
+ break;
+ default:
+ Log (LOG_PLAIN) << "unknown task " << (*i).second << endLog;
+ }
+ }
+
+ if (update)
+ queue_push2(&job, SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, 0);
+
+ if (!solv)
+ solv = solver_create(pool.pool);
+
+ solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
+ solver_set_flag(solv, SOLVER_FLAG_ALLOW_DOWNGRADE, 0);
+ solver_solve(solv, &job);
+ queue_free(&job);
+
+ int pcnt = solver_problem_count(solv);
+ solver_printdecisions(solv);
+
+ // get transactions for solution
+ Transaction *t = solver_create_transaction(solv);
+ transaction_order(t, 0);
+ transaction_print(t);
+
+ // massage into SolverTransactions form
+ trans.clear();
+
+ for (int i = 0; i < t->steps.count; i++)
+ {
+ Id id = t->steps.elements[i];
+ SolverTransaction::transType tt = type(t, i);
+ trans.push_back(SolverTransaction(SolvableVersion(id, pool.pool), tt));
+ }
+
+ // add install and remove tasks for anything marked as reinstall
+ for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
+ i != tasks.tasks.end();
+ i++)
+ {
+ const SolvableVersion &sv = (*i).first;
+
+ if (((*i).second) == SolverTasks::taskReinstall)
+ {
+ trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool),
+ SolverTransaction::transErase));
+ trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool),
+ SolverTransaction::transInstall));
+ }
+ }
+
+ // if include_source mode is on, also install source for everything we are
+ // installing
+ if (include_source)
+ {
+ // (this uses indicies into the vector, as iterators might become
+ // invalidated by doing push_back)
+ size_t n = trans.size();
+ for (size_t i = 0; i < n; i++)
+ {
+ if (trans[i].type == SolverTransaction::transInstall)
+ {
+ SolvableVersion src_version = trans[i].version.sourcePackage();
+ if (src_version)
+ trans.push_back(SolverTransaction(src_version,
+ SolverTransaction::transInstall));
+ }
+ }
+ }
+
+ if (trans.size())
+ {
+ Log (LOG_PLAIN) << "Augmented Transaction List:" << endLog;
+ for (SolverTransactionList::iterator i = trans.begin ();
+ i != trans.end ();
+ ++i)
+ {
+ Log (LOG_PLAIN) << std::setw(4) << std::distance(trans.begin(), i)
+ << std::setw(8) << i->type
+ << std::setw(48) << i->version.Name()
+ << std::setw(20) << i->version.Canonical_version() << endLog;
+ }
+ }
+
+ transaction_free(t);
+
+ return (pcnt == 0);
+}
+
+const SolverTransactionList &
+SolverSolution::transactions() const
+{
+ return trans;
+}
+
+// Construct a string reporting the problems and solutions
+std::string
+SolverSolution::report() const
+{
+ std::string r = "";
+ int pcnt = solver_problem_count(solv);
+ for (Id problem = 1; problem <= pcnt; problem++)
+ {
+ r += "Problem " + std::to_string(problem) + "/" + std::to_string(pcnt);
+ r += "\n";
+
+ Id probr = solver_findproblemrule(solv, problem);
+ Id dep, source, target;
+ SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep);
+ r += solver_problemruleinfo2str(solv, type, source, target, dep);
+ r += "\n";
+
+ int scnt = solver_solution_count(solv, problem);
+ for (Id solution = 1; solution <= scnt; solution++)
+ {
+ r += "Solution " + std::to_string(solution) + "/" + std::to_string(scnt);
+ r += "\n";
+
+ Id p, rp, element;
+ element = 0;
+ while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
+ {
+ r += " - ";
+ r += solver_solutionelement2str(solv, p, rp);
+ r += "\n";
+ }
+ }
+ }
+ return r;
+}
+
+// helper function to map transaction type
+SolverTransaction::transType
+SolverSolution::type(Transaction *trans, int pos)
+{
+ Id tt = transaction_type(trans, trans->steps.elements[pos],
+ SOLVER_TRANSACTION_SHOW_ACTIVE);
+
+ // if active side of transaction is nothing, ask again for passive side of
+ // transaction
+ if (tt == SOLVER_TRANSACTION_IGNORE)
+ tt = transaction_type(trans, trans->steps.elements[pos], 0);
+
+ switch(tt)
+ {
+ case SOLVER_TRANSACTION_INSTALL:
+ case SOLVER_TRANSACTION_REINSTALL:
+ case SOLVER_TRANSACTION_UPGRADE:
+ case SOLVER_TRANSACTION_DOWNGRADE:
+ return SolverTransaction::transInstall;
+ case SOLVER_TRANSACTION_ERASE:
+ case SOLVER_TRANSACTION_REINSTALLED:
+ case SOLVER_TRANSACTION_UPGRADED:
+ case SOLVER_TRANSACTION_DOWNGRADED:
+ return SolverTransaction::transErase;
+ default:
+ Log (LOG_PLAIN) << "unknown transaction type " << std::hex << tt << endLog;
+ case SOLVER_TRANSACTION_IGNORE:
+ return SolverTransaction::transIgnore;
+ }
+};
#include "solv/pool.h"
#include "solv/repo.h"
+#include "solv/solver.h"
#include "PackageSpecification.h"
#include "PackageTrust.h"
#include "package_source.h"
// ---------------------------------------------------------------------------
class SolverPool;
+class SolverSolution;
class SolvableVersion
{
Pool *pool;
friend SolverPool;
+ friend SolverSolution;
};
// ---------------------------------------------------------------------------
const addPackageData &pkgdata);
void internalize(void);
+ void use_test_packages(bool use_test_packages);
+
private:
Id makedeps(Repo *repo, PackageDepends *requires);
typedef std::map<std::string, SolvRepo *> RepoList;
RepoList repos;
+
+ friend SolverSolution;
};
+// ---------------------------------------------------------------------------
+// interface to class SolverTaskQueue
+//
+// This is used to contain a set of package install/uninstall tasks selected via
+// the UI to be passed to solver
+// ---------------------------------------------------------------------------
+
+class SolverTasks
+{
+ public:
+ enum task
+ {
+ taskInstall,
+ taskUninstall,
+ taskReinstall
+ };
+ void add(const SolvableVersion &v, task t)
+ {
+ tasks.push_back(taskList::value_type(v, t));
+ };
+ private:
+ typedef std::vector<std::pair<const SolvableVersion &, task>> taskList;
+ taskList tasks;
+
+ friend SolverSolution;
+};
+
+// ---------------------------------------------------------------------------
+// SolverTransactionList
+//
+// a list of transactions output by the solver
+// ---------------------------------------------------------------------------
+
+class SolverTransaction
+{
+ public:
+ typedef enum
+ {
+ transIgnore,
+ transInstall,
+ transErase,
+ } transType;
+
+ SolverTransaction(SolvableVersion version_, transType type_) :
+ version(version_), type(type_) {};
+
+ SolvableVersion version;
+ transType type;
+};
+
+typedef std::vector<SolverTransaction> SolverTransactionList;
+
+// ---------------------------------------------------------------------------
+// interface to class SolverSolution
+//
+// A wrapper around the libsolv solver
+// ---------------------------------------------------------------------------
+
+class SolverSolution
+{
+ public:
+ SolverSolution(SolverPool &_pool) : pool(_pool), solv(NULL) {};
+ ~SolverSolution();
+
+ bool update(SolverTasks &tasks, bool update, bool use_test_packages, bool include_source);
+ std::string report() const;
+
+ const SolverTransactionList &transactions() const;
+
+ private:
+ static SolverTransaction::transType type(Transaction *trans, int pos);
+
+ SolverPool &pool;
+ Solver *solv;
+ SolverTransactionList trans;
+};
+
#endif // LIBSOLV_H
PackageDBActions packagedb::task = PackageDB_Install;
std::vector <packagemeta *> packagedb::dependencyOrderedPackages;
SolverPool packagedb::solver;
+SolverSolution packagedb::solution(packagedb::solver);
#include <stack>
static PackageDBActions task;
static SolverPool solver;
+ static SolverSolution solution;
private:
static int installeddbread; /* do we have to reread this */
#include "package_meta.h"
#include "msg.h"
#include "Exception.h"
+#include "getopt++/BoolOption.h"
// Sizing information.
static ControlAdjuster::ControlInfo PrereqControlsInfo[] = {
};
extern ThreeBarProgressPage Progress;
+BoolOption IncludeSource (false, 'I', "include-source", "Automatically install source for every package installed");
// ---------------------------------------------------------------------------
// implements class PrereqPage
PrereqPage::OnActivate()
{
// if we have gotten this far, then PrereqChecker has already run isMet
- // and found that there were missing packages; so we can just call
+ // and found that there were problems; so we can just call
// getUnmetString to format the results and display it
std::string s;
if (!IsDlgButtonChecked (h, IDC_PREREQ_CHECK))
{
- // breakage imminent! danger, danger
- int res = MessageBox (h,
- "The listed packages are required for packages depending on them to "
- "work. We strongly recommend that you allow Setup to select them."
- "\r\n\r\n"
- "Are you sure you want to proceed without these packages?",
- "WARNING - Required Packages Not Selected",
- MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2);
- if (res == IDNO)
- return -1;
- else
- Log (LOG_PLAIN) <<
- "NOTE! User refused suggested missing dependencies! "
- "Expect some packages to give errors or not function at all." << endLog;
- }
- else
- {
- // add the missing requirements
- PrereqChecker p;
- p.selectMissing ();
+ return -1;
}
return whatNext();
if (unattended_mode == chooseronly)
return -1;
- // in unattended mode, add the missing requirements, then carry on to download/install
- PrereqChecker p;
- p.selectMissing ();
-
return whatNext();
}
+bool
+PrereqPage::OnMessageCmd (int id, HWND hwndctl, UINT code)
+{
+ if ((code == BN_CLICKED) && (id == IDC_PREREQ_CHECK))
+ {
+ GetOwner ()->SetButtons (PSWIZB_BACK | (IsButtonChecked (id) ? PSWIZB_NEXT : 0));
+ return true;
+ }
+
+ return false;
+}
+
// ---------------------------------------------------------------------------
// implements class PrereqChecker
// ---------------------------------------------------------------------------
// instantiate the static members
-map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> PrereqChecker::unmet;
-map <std::string, vector <packagemeta *> > PrereqChecker::notfound;
-trusts PrereqChecker::theTrust = TRUST_CURR;
+bool PrereqChecker::upgrade;
+bool PrereqChecker::use_test_packages;
-/* This function builds a list of unmet dependencies to present to the user on
- the PrereqPage propsheet.
-
- The data is stored in two associative maps:
- unmet[package] = vector of packages that depend on package.
- notfound[package-name] = vector of packages that depend on package.
-*/
bool
PrereqChecker::isMet ()
{
packagedb db;
- Progress.SetText1 ("Checking prerequisites...");
+ Progress.SetText1 ("Solving dependencies...");
Progress.SetText2 ("");
Progress.SetText3 ("");
- // clear static data each time this is called
- unmet.clear ();
- notfound.clear ();
-
- // packages that need to be checked for dependencies
- queue <packagemeta *> todo;
+ // go through all packages, adding changed ones to the solver task list
+ SolverTasks q;
- // go through all packages, adding desired ones to the initial work list
for (packagedb::packagecollection::iterator p = db.packages.begin ();
p != db.packages.end (); ++p)
{
- if (p->second->desired)
- todo.push (p->second);
- }
-
- int max = todo.size();
- int pos = 0;
+ packagemeta *pkg = p->second;
- // churn through the work list
- while (!todo.empty ())
- {
- // get the first package off the work list
- packagemeta *pack = todo.front ();
- todo.pop ();
-
- pos++;
- Progress.SetText2 (pack->name.c_str());
- static char buf[100];
- sprintf(buf, "%d %% (%d/%d)", pos * 100 / max, pos, max);
- Progress.SetText3(buf);
- Progress.SetBar1(pos, max);
-
- // Fetch the dependencies of the package. This assumes that the
- // dependencies of all versions are all the same.
- const PackageDepends deps = pack->curr.depends ();
-
- // go through the package's dependencies
- for (PackageDepends::const_iterator d =
- deps.begin (); d != deps.end (); ++d)
+ // decode UI state to action
+ // skip and keep don't change dependency solution
+ if (pkg->installed != pkg->desired)
{
- PackageSpecification *dep_spec = *d;
- packagemeta *dep = db.findBinary (*dep_spec);
-
- if (dep)
- {
- if (!(dep->desired && dep_spec->satisfies (dep->desired)))
- {
- // we've got an unmet dependency
- if (unmet.find (dep) == unmet.end ())
- {
- // newly found dependency: add to worklist
- todo.push (dep);
- max++;
- }
- unmet[dep].push_back (pack);
- }
- }
+ if (pkg->desired)
+ q.add(pkg->desired, SolverTasks::taskInstall); // install/upgrade
else
- {
- // dependency on a package which doesn't have any binary versions
- // (i.e. it is source only or doesn't exist)
- Log (LOG_PLAIN) << "package " << pack->name << " has dependency "
- << dep_spec->packageName() << " we can't find" << endLog;
- notfound[dep_spec->packageName()].push_back (pack);
- }
+ q.add(pkg->installed, SolverTasks::taskUninstall); // uninstall
}
- }
-
- return unmet.empty () && notfound.empty ();
-}
-
-/* Formats 'unmet' as a string for display to the user. */
-void
-PrereqChecker::getUnmetString (std::string &s)
-{
- s = "";
-
- {
- map <std::string, vector <packagemeta *> >::iterator i;
- for (i = notfound.begin(); i != notfound.end(); i++)
- {
- s = s + i->first
- + "\t(not found)"
- + "\r\n\tRequired by: ";
- for (unsigned int j = 0; j < i->second.size(); j++)
- {
- s += i->second[j]->name;
- if (j != i->second.size() - 1)
- s += ", ";
- }
- s += "\r\n\r\n";
- }
- }
+ else
+ if (pkg->picked())
+ q.add(pkg->installed, SolverTasks::taskReinstall); // reinstall
- map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
- for (i = unmet.begin(); i != unmet.end(); i++)
- {
- s = s + i->first->name
- + "\t(" + i->first->trustp (false, theTrust).Canonical_version ()
- + ")\r\n\t" + i->first->SDesc ()
- + "\r\n\tRequired by: ";
- for (unsigned int j = 0; j < i->second.size(); j++)
+ // only install action makes sense for source packages
+ if (pkg->srcpicked())
{
- s += i->second[j]->name;
- if (j != i->second.size() - 1)
- s += ", ";
+ if (pkg->desired)
+ q.add(pkg->desired.sourcePackage(), SolverTasks::taskInstall);
+ else
+ q.add(pkg->installed.sourcePackage(), SolverTasks::taskInstall);
}
- s += "\r\n\r\n";
}
+
+ // apply solver to those tasks and the chooser global state (keep, curr, test)
+ return db.solution.update(q, upgrade, use_test_packages, IncludeSource);
}
-/* Takes the keys of 'unmet' and selects them, using the current trust. */
+/* Formats problems and solutions as a string for display to the user. */
void
-PrereqChecker::selectMissing ()
+PrereqChecker::getUnmetString (std::string &s)
{
packagedb db;
+ s = db.solution.report();
- // provide a default, even though this should have been set for us
- if (!theTrust)
- theTrust = TRUST_CURR;
-
- // get each of the keys of 'unmet'
- map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
- for (i = unmet.begin(); i != unmet.end(); i++)
+ //
+ size_t pos = 0;
+ while ((pos = s.find("\n", pos)) != std::string::npos)
{
- packagemeta *pkg = i->first;
- packageversion vers = pkg->trustp (false, theTrust);
- pkg->desired = vers;
- pkg->srcpick (false);
-
- if (vers == i->first->installed)
- {
- pkg->pick (false);
- Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
- ": Selecting already-installed version " <<
- i->first->installed.Canonical_version () << "." << endLog;
- }
- else
- {
- pkg->pick (vers.accessible ());
- Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
- ": Selecting version " << vers.Canonical_version () <<
- " for installation." << endLog;
- }
+ s.replace(pos, 1, "\r\n");
+ pos += 2;
}
}
virtual long OnNext ();
virtual long OnBack ();
virtual long OnUnattended ();
+ virtual bool OnMessageCmd (int id, HWND hwndctl, UINT code);
private:
long whatNext ();
};
class PrereqChecker
{
public:
- // checks all dependecies, populates 'unmet'
- // returns true if unsatisfied dependencies exist
+ // returns true if no dependency problems exist
bool isMet ();
-
+
// formats 'unmet' as a string for display
void getUnmetString (std::string &s);
-
- // selects/picks the needed packages that were missing
- void selectMissing ();
-
- // notes the current trust (for use in selectMissing)
- static void setTrust (trusts t) { theTrust = t; };
+
+ static void setUpgrade (bool u) { upgrade = u; };
+ static void setTestPackages (bool t) { use_test_packages = t; };
private:
-
- // this is the actual hash_map that does all the work
- static map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> unmet;
- static map <std::string, vector <packagemeta *> > notfound;
- static trusts theTrust;
+ static bool upgrade;
+ static bool use_test_packages;
};
#endif /* SETUP_PREREQ_H */
ICON IDI_CYGWIN,IDC_HEADICON,SETUP_HEADICON_X,0,21,20
LTEXT "Resolving Dependencies",IDC_STATIC_HEADER_TITLE
,7,0,258,8,NOT WS_GROUP
- LTEXT "The following packages are required to satisfy "
+ LTEXT "The following problems occured trying to satisfy "
"dependencies.",IDC_STATIC,21,9,239,16,NOT WS_GROUP
- CONTROL "&Select required packages (RECOMMENDED)"
+ CONTROL "&Accept default problem solutions"
,IDC_PREREQ_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
7,167,225,14
EDITTEXT IDC_PREREQ_EDIT,7,41,303,124,WS_VSCROLL | WS_HSCROLL |