]> cygwin.com Git - cygwin-apps/setup.git/blob - prereq.cc
* Makefile.am: Treat libgetopt++ as full-fledged SUBDIRS.
[cygwin-apps/setup.git] / prereq.cc
1 /*
2 * Copyright (c) 2005 Brian Dessent
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 Brian Dessent <brian@dessent.net>
13 *
14 */
15
16 #if 0
17 static const char *cvsid =
18 "\n%%% $Id$\n";
19 #endif
20
21 #include "win32.h"
22 #include <commctrl.h>
23 #include <stdio.h>
24 #include <io.h>
25 #include <ctype.h>
26 #include <process.h>
27 #include <queue>
28
29 #include "prereq.h"
30 #include "dialog.h"
31 #include "resource.h"
32 #include "state.h"
33 #include "propsheet.h"
34 #include "threebar.h"
35 #include "Generic.h"
36 #include "LogSingleton.h"
37 #include "ControlAdjuster.h"
38 #include "package_db.h"
39 #include "package_meta.h"
40 #include "msg.h"
41
42 // Sizing information.
43 static ControlAdjuster::ControlInfo PrereqControlsInfo[] = {
44 {IDC_PREREQ_CHECK, CP_LEFT, CP_BOTTOM},
45 {IDC_PREREQ_EDIT, CP_STRETCH, CP_STRETCH},
46 {0, CP_LEFT, CP_TOP}
47 };
48
49 extern ThreeBarProgressPage Progress;
50
51 // ---------------------------------------------------------------------------
52 // implements class PrereqPage
53 // ---------------------------------------------------------------------------
54
55 PrereqPage::PrereqPage ()
56 {
57 sizeProcessor.AddControlInfo (PrereqControlsInfo);
58 }
59
60 bool
61 PrereqPage::Create ()
62 {
63 return PropertyPage::Create (IDD_PREREQ);
64 }
65
66 void
67 PrereqPage::OnInit ()
68 {
69 // start with the checkbox set
70 CheckDlgButton (GetHWND (), IDC_PREREQ_CHECK, BST_CHECKED);
71
72 // set the edit-area to a larger font
73 SetDlgItemFont(IDC_PREREQ_EDIT, "MS Shell Dlg", 10);
74 }
75
76 void
77 PrereqPage::OnActivate()
78 {
79 // if we have gotten this far, then PrereqChecker has already run isMet
80 // and found that there were missing packages; so we can just call
81 // getUnmetString to format the results and display it
82
83 std::string s;
84 PrereqChecker p;
85 p.getUnmetString (s);
86 SetDlgItemText (GetHWND (), IDC_PREREQ_EDIT, s.c_str ());
87
88 SetFocus (GetDlgItem (IDC_PREREQ_CHECK));
89 }
90
91 long
92 PrereqPage::OnNext ()
93 {
94 HWND h = GetHWND ();
95
96 if (!IsDlgButtonChecked (h, IDC_PREREQ_CHECK))
97 {
98 // breakage imminent! danger, danger
99 int res = MessageBox (h,
100 "If you continue without correcting the listed conflicts, your "
101 "Cygwin installation will not function properly.\r\n"
102 "We strongly recommend that you let Setup install the listed packages.\r\n\r\n"
103 "Are you sure you want to proceed?",
104 "WARNING - Required Packages Not Selected",
105 MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2);
106 if (res == IDNO)
107 return -1;
108 else
109 log (LOG_PLAIN) <<
110 "NOTE! User refused suggested missing dependencies! "
111 "Expect some packages to give errors or not function at all." << endLog;
112 }
113 else
114 {
115 // add the missing requirements
116 PrereqChecker p;
117 p.selectMissing ();
118 }
119 if (source == IDC_SOURCE_CWD)
120 {
121 // Next, install
122 Progress.SetActivateTask (WM_APP_START_INSTALL);
123 }
124 else
125 {
126 // Next, start download from internet
127 Progress.SetActivateTask (WM_APP_START_DOWNLOAD);
128 }
129 return IDD_INSTATUS;
130 }
131
132 long
133 PrereqPage::OnBack ()
134 {
135 return IDD_CHOOSE;
136 }
137
138
139 // ---------------------------------------------------------------------------
140 // implements class PrereqChecker
141 // ---------------------------------------------------------------------------
142
143 // instantiate the static members
144 map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> PrereqChecker::unmet;
145 trusts PrereqChecker::theTrust = TRUST_CURR;
146
147 /* This function builds a list of unmet dependencies to present to the user on
148 the PrereqPage propsheet. The data is stored as an associative map of
149 unmet[missing-package] = vector of packages that depend on missing-package */
150 bool
151 PrereqChecker::isMet ()
152 {
153 packagedb db;
154
155 // unmet is static - clear it each time this is called
156 unmet.clear ();
157
158 // packages that need to be checked for dependencies
159 queue <packagemeta *> todo;
160
161 // go through all packages, adding desired ones to the initial work list
162 for (vector <packagemeta *>::iterator p = db.packages.begin ();
163 p != db.packages.end (); ++p)
164 {
165 if ((*p)->desired)
166 todo.push (*p);
167 }
168
169 // churn through the work list
170 while (!todo.empty ())
171 {
172 // get the first package off the work list
173 packagemeta *pack = todo.front ();
174 todo.pop ();
175
176 // Fetch the dependencies of the package. This assumes that the
177 // dependencies of the prev, curr, and exp versions are all the same.
178 vector <vector <PackageSpecification *> *> *deps = pack->curr.depends ();
179
180 // go through the package's dependencies
181 for (vector <vector <PackageSpecification *> *>::iterator d =
182 deps->begin (); d != deps->end (); ++d)
183 {
184 // XXX: the following assumes that there is only a single
185 // node in each OR clause, which is currently the case.
186 // if setup is ever pushed to use AND/OR in "depends:"
187 // lines this will have to be updated
188 PackageSpecification *dep_spec = (*d)->at(0);
189 packagemeta *dep = db.findBinary (*dep_spec);
190
191 if (dep && !(dep->desired && dep_spec->satisfies (dep->desired)))
192 {
193 // we've got an unmet dependency
194 if (unmet.find (dep) == unmet.end ())
195 {
196 // newly found dependency: add to worklist
197 todo.push (dep);
198 }
199 unmet[dep].push_back (pack);
200 }
201 }
202 }
203
204 return unmet.empty ();
205 }
206
207 /* Formats 'unmet' as a string for display to the user. */
208 void
209 PrereqChecker::getUnmetString (std::string &s)
210 {
211 s = "";
212
213 map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
214 for (i = unmet.begin(); i != unmet.end(); i++)
215 {
216 s = s + i->first->name
217 + "\t(" + i->first->trustp (theTrust).Canonical_version ()
218 + ")\r\n\t" + i->first->SDesc ()
219 + "\r\n\tRequired by: ";
220 for (unsigned int j = 0; j < i->second.size(); j++)
221 {
222 s += i->second[j]->name;
223 if (j != i->second.size() - 1)
224 s += ", ";
225 }
226 s += "\r\n\r\n";
227 }
228 }
229
230 /* Takes the keys of 'unmet' and selects them, using the current trust. */
231 void
232 PrereqChecker::selectMissing ()
233 {
234 packagedb db;
235
236 // provide a default, even though this should have been set for us
237 if (!theTrust)
238 theTrust = TRUST_CURR;
239
240 // get each of the keys of 'unmet'
241 map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
242 for (i = unmet.begin(); i != unmet.end(); i++)
243 {
244 packageversion vers = i->first->trustp (theTrust);
245 i->first->desired = vers;
246 vers.sourcePackage ().pick (false, NULL);
247
248 if (vers == i->first->installed)
249 {
250 vers.pick (false, NULL);
251 log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
252 ": Selecting already-installed version " <<
253 i->first->installed.Canonical_version () << "." << endLog;
254 }
255 else
256 {
257 vers.pick (vers.accessible (), i->first);
258 log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
259 ": Selecting version " << vers.Canonical_version () <<
260 " for installation." << endLog;
261 }
262 }
263 }
This page took 0.044563 seconds and 5 git commands to generate.