2 * Copyright (c) 2005 Brian Dessent
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.
9 * A copy of the GNU General Public License can be found at
12 * Written by Brian Dessent <brian@dessent.net>
28 #include "propsheet.h"
31 #include "LogSingleton.h"
32 #include "ControlAdjuster.h"
33 #include "package_db.h"
34 #include "package_meta.h"
36 #include "Exception.h"
38 // Sizing information.
39 static ControlAdjuster::ControlInfo PrereqControlsInfo
[] = {
40 {IDC_PREREQ_CHECK
, CP_LEFT
, CP_BOTTOM
},
41 {IDC_PREREQ_EDIT
, CP_STRETCH
, CP_STRETCH
},
45 extern ThreeBarProgressPage Progress
;
47 // ---------------------------------------------------------------------------
48 // implements class PrereqPage
49 // ---------------------------------------------------------------------------
51 PrereqPage::PrereqPage ()
53 sizeProcessor
.AddControlInfo (PrereqControlsInfo
);
59 return PropertyPage::Create (IDD_PREREQ
);
65 // start with the checkbox set
66 CheckDlgButton (GetHWND (), IDC_PREREQ_CHECK
, BST_CHECKED
);
68 // set the edit-area to a larger font
69 SetDlgItemFont(IDC_PREREQ_EDIT
, "MS Shell Dlg", 10);
73 PrereqPage::OnActivate()
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
82 SetDlgItemText (GetHWND (), IDC_PREREQ_EDIT
, s
.c_str ());
84 SetFocus (GetDlgItem (IDC_PREREQ_CHECK
));
92 if (!IsDlgButtonChecked (h
, IDC_PREREQ_CHECK
))
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."
99 "Are you sure you want to proceed without these packages?",
100 "WARNING - Required Packages Not Selected",
101 MB_YESNO
| MB_ICONEXCLAMATION
| MB_DEFBUTTON2
);
106 "NOTE! User refused suggested missing dependencies! "
107 "Expect some packages to give errors or not function at all." << endLog
;
111 // add the missing requirements
120 PrereqPage::whatNext ()
122 if (source
== IDC_SOURCE_LOCALDIR
)
125 Progress
.SetActivateTask (WM_APP_START_INSTALL
);
129 // Next, start download from internet
130 Progress
.SetActivateTask (WM_APP_START_DOWNLOAD
);
136 PrereqPage::OnBack ()
142 PrereqPage::OnUnattended ()
144 // in chooser-only mode, show this page so the user can choose to fix dependency problems or not
145 if (unattended_mode
== chooseronly
)
148 // in unattended mode, add the missing requirements, then carry on to download/install
155 // ---------------------------------------------------------------------------
156 // implements class PrereqChecker
157 // ---------------------------------------------------------------------------
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
;
164 /* This function builds a list of unmet dependencies to present to the user on
165 the PrereqPage propsheet.
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.
172 PrereqChecker::isMet ()
176 Progress
.SetText1 ("Checking prerequisites...");
177 Progress
.SetText2 ("");
178 Progress
.SetText3 ("");
180 // clear static data each time this is called
184 // packages that need to be checked for dependencies
185 queue
<packagemeta
*> todo
;
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
)
191 if (p
->second
->desired
)
192 todo
.push (p
->second
);
195 int max
= todo
.size();
198 // churn through the work list
199 while (!todo
.empty ())
201 // get the first package off the work list
202 packagemeta
*pack
= todo
.front ();
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
);
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 ();
216 // go through the package's dependencies
217 for (PackageDepends::const_iterator d
=
218 deps
.begin (); d
!= deps
.end (); ++d
)
220 PackageSpecification
*dep_spec
= *d
;
221 packagemeta
*dep
= db
.findBinary (*dep_spec
);
225 if (!(dep
->desired
&& dep_spec
->satisfies (dep
->desired
)))
227 // we've got an unmet dependency
228 if (unmet
.find (dep
) == unmet
.end ())
230 // newly found dependency: add to worklist
234 unmet
[dep
].push_back (pack
);
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
);
248 return unmet
.empty () && notfound
.empty ();
251 /* Formats 'unmet' as a string for display to the user. */
253 PrereqChecker::getUnmetString (std::string
&s
)
258 map
<std::string
, vector
<packagemeta
*> >::iterator i
;
259 for (i
= notfound
.begin(); i
!= notfound
.end(); i
++)
263 + "\r\n\tRequired by: ";
264 for (unsigned int j
= 0; j
< i
->second
.size(); j
++)
266 s
+= i
->second
[j
]->name
;
267 if (j
!= i
->second
.size() - 1)
274 map
<packagemeta
*, vector
<packagemeta
*>, packagemeta_ltcomp
>::iterator i
;
275 for (i
= unmet
.begin(); i
!= unmet
.end(); i
++)
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
++)
283 s
+= i
->second
[j
]->name
;
284 if (j
!= i
->second
.size() - 1)
291 /* Takes the keys of 'unmet' and selects them, using the current trust. */
293 PrereqChecker::selectMissing ()
297 // provide a default, even though this should have been set for us
299 theTrust
= TRUST_CURR
;
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
++)
305 packagemeta
*pkg
= i
->first
;
306 packageversion vers
= pkg
->trustp (false, theTrust
);
308 pkg
->srcpick (false);
310 if (vers
== i
->first
->installed
)
313 Log (LOG_PLAIN
) << "Adding required dependency " << i
->first
->name
<<
314 ": Selecting already-installed version " <<
315 i
->first
->installed
.Canonical_version () << "." << endLog
;
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
;
327 // ---------------------------------------------------------------------------
328 // progress page glue
329 // ---------------------------------------------------------------------------
332 do_prereq_check_thread(HINSTANCE h
, HWND owner
)
339 if (source
== IDC_SOURCE_LOCALDIR
)
340 Progress
.SetActivateTask (WM_APP_START_INSTALL
); // install
342 Progress
.SetActivateTask (WM_APP_START_DOWNLOAD
); // start download
343 retval
= IDD_INSTATUS
;
347 // rut-roh, some required things are not selected
355 do_prereq_check_reflector (void *p
)
358 context
= (HANDLE
*) p
;
362 int next_dialog
= do_prereq_check_thread ((HINSTANCE
) context
[0], (HWND
) context
[1]);
364 // Tell the progress page that we're done prereq checking
365 Progress
.PostMessageNow (WM_APP_PREREQ_CHECK_THREAD_COMPLETE
, 0, next_dialog
);
367 TOPLEVEL_CATCH((HWND
) context
[1], "prereq_check");
372 static HANDLE context
[2];
375 do_prereq_check (HINSTANCE h
, HWND owner
)
381 CreateThread (NULL
, 0, do_prereq_check_reflector
, context
, 0, &threadID
);