]> cygwin.com Git - cygwin-apps/setup.git/blob - prereq.cc
56b200606227fd39bec6e80093036890ed426881
[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 #include "win32.h"
17 #include <commctrl.h>
18 #include <stdio.h>
19 #include <io.h>
20 #include <ctype.h>
21 #include <process.h>
22 #include <queue>
23
24 #include "prereq.h"
25 #include "dialog.h"
26 #include "resource.h"
27 #include "state.h"
28 #include "propsheet.h"
29 #include "threebar.h"
30 #include "Generic.h"
31 #include "LogSingleton.h"
32 #include "ControlAdjuster.h"
33 #include "package_db.h"
34 #include "package_meta.h"
35 #include "msg.h"
36 #include "Exception.h"
37
38 // Sizing information.
39 static ControlAdjuster::ControlInfo PrereqControlsInfo[] = {
40 {IDC_PREREQ_CHECK, CP_LEFT, CP_BOTTOM},
41 {IDC_PREREQ_EDIT, CP_STRETCH, CP_STRETCH},
42 {0, CP_LEFT, CP_TOP}
43 };
44
45 extern ThreeBarProgressPage Progress;
46
47 // ---------------------------------------------------------------------------
48 // implements class PrereqPage
49 // ---------------------------------------------------------------------------
50
51 PrereqPage::PrereqPage ()
52 {
53 sizeProcessor.AddControlInfo (PrereqControlsInfo);
54 }
55
56 bool
57 PrereqPage::Create ()
58 {
59 return PropertyPage::Create (IDD_PREREQ);
60 }
61
62 void
63 PrereqPage::OnInit ()
64 {
65 // start with the checkbox set
66 CheckDlgButton (GetHWND (), IDC_PREREQ_CHECK, BST_CHECKED);
67
68 // set the edit-area to a larger font
69 SetDlgItemFont(IDC_PREREQ_EDIT, "MS Shell Dlg", 10);
70 }
71
72 void
73 PrereqPage::OnActivate()
74 {
75 // if we have gotten this far, then PrereqChecker has already run isMet
76 // and found that there were missing packages; so we can just call
77 // getUnmetString to format the results and display it
78
79 std::string s;
80 PrereqChecker p;
81 p.getUnmetString (s);
82 SetDlgItemText (GetHWND (), IDC_PREREQ_EDIT, s.c_str ());
83
84 SetFocus (GetDlgItem (IDC_PREREQ_CHECK));
85 }
86
87 long
88 PrereqPage::OnNext ()
89 {
90 HWND h = GetHWND ();
91
92 if (!IsDlgButtonChecked (h, IDC_PREREQ_CHECK))
93 {
94 // breakage imminent! danger, danger
95 int res = MessageBox (h,
96 "The listed packages are required for packages depending on them to "
97 "work. We strongly recommend that you allow Setup to select them."
98 "\r\n\r\n"
99 "Are you sure you want to proceed without these packages?",
100 "WARNING - Required Packages Not Selected",
101 MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2);
102 if (res == IDNO)
103 return -1;
104 else
105 Log (LOG_PLAIN) <<
106 "NOTE! User refused suggested missing dependencies! "
107 "Expect some packages to give errors or not function at all." << endLog;
108 }
109 else
110 {
111 // add the missing requirements
112 PrereqChecker p;
113 p.selectMissing ();
114 }
115
116 return whatNext();
117 }
118
119 long
120 PrereqPage::whatNext ()
121 {
122 if (source == IDC_SOURCE_LOCALDIR)
123 {
124 // Next, install
125 Progress.SetActivateTask (WM_APP_START_INSTALL);
126 }
127 else
128 {
129 // Next, start download from internet
130 Progress.SetActivateTask (WM_APP_START_DOWNLOAD);
131 }
132 return IDD_INSTATUS;
133 }
134
135 long
136 PrereqPage::OnBack ()
137 {
138 return IDD_CHOOSE;
139 }
140
141 long
142 PrereqPage::OnUnattended ()
143 {
144 // in chooser-only mode, show this page so the user can choose to fix dependency problems or not
145 if (unattended_mode == chooseronly)
146 return -1;
147
148 // in unattended mode, add the missing requirements, then carry on to download/install
149 PrereqChecker p;
150 p.selectMissing ();
151
152 return whatNext();
153 }
154
155 // ---------------------------------------------------------------------------
156 // implements class PrereqChecker
157 // ---------------------------------------------------------------------------
158
159 // instantiate the static members
160 map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> PrereqChecker::unmet;
161 map <std::string, vector <packagemeta *> > PrereqChecker::notfound;
162 trusts PrereqChecker::theTrust = TRUST_CURR;
163
164 /* This function builds a list of unmet dependencies to present to the user on
165 the PrereqPage propsheet.
166
167 The data is stored in two associative maps:
168 unmet[package] = vector of packages that depend on package.
169 notfound[package-name] = vector of packages that depend on package.
170 */
171 bool
172 PrereqChecker::isMet ()
173 {
174 packagedb db;
175
176 Progress.SetText1 ("Checking prerequisites...");
177 Progress.SetText2 ("");
178 Progress.SetText3 ("");
179
180 // clear static data each time this is called
181 unmet.clear ();
182 notfound.clear ();
183
184 // packages that need to be checked for dependencies
185 queue <packagemeta *> todo;
186
187 // go through all packages, adding desired ones to the initial work list
188 for (packagedb::packagecollection::iterator p = db.packages.begin ();
189 p != db.packages.end (); ++p)
190 {
191 if (p->second->desired)
192 todo.push (p->second);
193 }
194
195 int max = todo.size();
196 int pos = 0;
197
198 // churn through the work list
199 while (!todo.empty ())
200 {
201 // get the first package off the work list
202 packagemeta *pack = todo.front ();
203 todo.pop ();
204
205 pos++;
206 Progress.SetText2 (pack->name.c_str());
207 static char buf[100];
208 sprintf(buf, "%d %% (%d/%d)", pos * 100 / max, pos, max);
209 Progress.SetText3(buf);
210 Progress.SetBar1(pos, max);
211
212 // Fetch the dependencies of the package. This assumes that the
213 // dependencies of all versions are all the same.
214 const PackageDepends deps = pack->curr.depends ();
215
216 // go through the package's dependencies
217 for (PackageDepends::const_iterator d =
218 deps.begin (); d != deps.end (); ++d)
219 {
220 PackageSpecification *dep_spec = *d;
221 packagemeta *dep = db.findBinary (*dep_spec);
222
223 if (dep)
224 {
225 if (!(dep->desired && dep_spec->satisfies (dep->desired)))
226 {
227 // we've got an unmet dependency
228 if (unmet.find (dep) == unmet.end ())
229 {
230 // newly found dependency: add to worklist
231 todo.push (dep);
232 max++;
233 }
234 unmet[dep].push_back (pack);
235 }
236 }
237 else
238 {
239 // dependency on a package which doesn't have any binary versions
240 // (i.e. it is source only or doesn't exist)
241 Log (LOG_PLAIN) << "package " << pack->name << " has dependency "
242 << dep_spec->packageName() << " we can't find" << endLog;
243 notfound[dep_spec->packageName()].push_back (pack);
244 }
245 }
246 }
247
248 return unmet.empty () && notfound.empty ();
249 }
250
251 /* Formats 'unmet' as a string for display to the user. */
252 void
253 PrereqChecker::getUnmetString (std::string &s)
254 {
255 s = "";
256
257 {
258 map <std::string, vector <packagemeta *> >::iterator i;
259 for (i = notfound.begin(); i != notfound.end(); i++)
260 {
261 s = s + i->first
262 + "\t(not found)"
263 + "\r\n\tRequired by: ";
264 for (unsigned int j = 0; j < i->second.size(); j++)
265 {
266 s += i->second[j]->name;
267 if (j != i->second.size() - 1)
268 s += ", ";
269 }
270 s += "\r\n\r\n";
271 }
272 }
273
274 map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
275 for (i = unmet.begin(); i != unmet.end(); i++)
276 {
277 s = s + i->first->name
278 + "\t(" + i->first->trustp (false, theTrust).Canonical_version ()
279 + ")\r\n\t" + i->first->SDesc ()
280 + "\r\n\tRequired by: ";
281 for (unsigned int j = 0; j < i->second.size(); j++)
282 {
283 s += i->second[j]->name;
284 if (j != i->second.size() - 1)
285 s += ", ";
286 }
287 s += "\r\n\r\n";
288 }
289 }
290
291 /* Takes the keys of 'unmet' and selects them, using the current trust. */
292 void
293 PrereqChecker::selectMissing ()
294 {
295 packagedb db;
296
297 // provide a default, even though this should have been set for us
298 if (!theTrust)
299 theTrust = TRUST_CURR;
300
301 // get each of the keys of 'unmet'
302 map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
303 for (i = unmet.begin(); i != unmet.end(); i++)
304 {
305 packagemeta *pkg = i->first;
306 packageversion vers = pkg->trustp (false, theTrust);
307 pkg->desired = vers;
308 pkg->srcpick (false);
309
310 if (vers == i->first->installed)
311 {
312 pkg->pick (false);
313 Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
314 ": Selecting already-installed version " <<
315 i->first->installed.Canonical_version () << "." << endLog;
316 }
317 else
318 {
319 pkg->pick (vers.accessible ());
320 Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
321 ": Selecting version " << vers.Canonical_version () <<
322 " for installation." << endLog;
323 }
324 }
325 }
326
327 // ---------------------------------------------------------------------------
328 // progress page glue
329 // ---------------------------------------------------------------------------
330
331 static int
332 do_prereq_check_thread(HINSTANCE h, HWND owner)
333 {
334 PrereqChecker p;
335 int retval;
336
337 if (p.isMet ())
338 {
339 if (source == IDC_SOURCE_LOCALDIR)
340 Progress.SetActivateTask (WM_APP_START_INSTALL); // install
341 else
342 Progress.SetActivateTask (WM_APP_START_DOWNLOAD); // start download
343 retval = IDD_INSTATUS;
344 }
345 else
346 {
347 // rut-roh, some required things are not selected
348 retval = IDD_PREREQ;
349 }
350
351 return retval;
352 }
353
354 static DWORD WINAPI
355 do_prereq_check_reflector (void *p)
356 {
357 HANDLE *context;
358 context = (HANDLE *) p;
359
360 try
361 {
362 int next_dialog = do_prereq_check_thread ((HINSTANCE) context[0], (HWND) context[1]);
363
364 // Tell the progress page that we're done prereq checking
365 Progress.PostMessageNow (WM_APP_PREREQ_CHECK_THREAD_COMPLETE, 0, next_dialog);
366 }
367 TOPLEVEL_CATCH((HWND) context[1], "prereq_check");
368
369 ExitThread(0);
370 }
371
372 static HANDLE context[2];
373
374 void
375 do_prereq_check (HINSTANCE h, HWND owner)
376 {
377 context[0] = h;
378 context[1] = owner;
379
380 DWORD threadID;
381 CreateThread (NULL, 0, do_prereq_check_reflector, context, 0, &threadID);
382 }
This page took 0.051868 seconds and 4 git commands to generate.