source: trunk/Packages/ModularSystem/ModularSystem.pas

Last change on this file was 77, checked in by chronos, 6 months ago
  • Modified: Compiler targets moved into modules.
File size: 14.7 KB
Line 
1unit ModularSystem;
2
3interface
4
5uses
6 Classes, SysUtils, RegistryEx, Generics.Collections;
7
8type
9 TModuleManager = class;
10 TModule = class;
11 TModules = class;
12
13 TAPI = class(TComponent)
14 end;
15
16 TModuleCondition = (mcAll, mcEnabled, mcNotEnabled, mcInstalled, mcNotInstalled,
17 mcRunning, mcNotRunning);
18 TModuleConditions = set of TModuleCondition;
19 TModuleAction = (maStart, maStop, maInstall, maUninstall, maUpgrade, maEnable,
20 maDisable);
21 TModuleActions = array of TModuleAction;
22
23 { TModule }
24
25 TModule = class(TComponent)
26 private
27 FCategory: string;
28 FEnabled: Boolean;
29 FOnUpdate: TNotifyEvent;
30 FReleaseTime: TDateTime;
31 FRunning: Boolean;
32 FInstalled: Boolean;
33 FManager: TModuleManager;
34 FVersion: string;
35 FIdentification: string;
36 FTitle: string;
37 FLicense: string;
38 FAuthor: string;
39 FDependencies: TStringList;
40 FDescription: TStringList;
41 FFileName: string;
42 FWebSite: string;
43 FStartUpTime: TDateTime;
44 procedure SetEnabled(AValue: Boolean);
45 procedure SetInstalled(AValue: Boolean);
46 procedure SetManager(AValue: TModuleManager);
47 procedure SetRunning(AValue: Boolean);
48 procedure DoUpdate;
49 protected
50 procedure DoStart; virtual;
51 procedure DoStop; virtual;
52 procedure DoInstall; virtual;
53 procedure DoUninstall; virtual;
54 procedure DoUpgrade; virtual;
55 public
56 API: TAPI;
57 procedure Enable;
58 procedure Disable;
59 procedure Start;
60 procedure Stop;
61 procedure Restart;
62 procedure Install;
63 procedure Uninstall;
64 procedure Reinstall;
65 procedure Upgrade;
66 procedure EnumDependenciesCascade(ModuleList: TModules;
67 Conditions: TModuleConditions = [mcAll]);
68 procedure EnumSuperiorDependenciesCascade(ModuleList: TModules;
69 Conditions: TModuleConditions = [mcAll]);
70 procedure SetInstalledState(Value: Boolean);
71 constructor Create(Owner: TComponent); override;
72 destructor Destroy; override;
73 property Running: Boolean read FRunning write SetRunning;
74 property Installed: Boolean read FInstalled write SetInstalled;
75 property Enabled: Boolean read FEnabled write SetEnabled;
76 property StartUpTime: TDateTime read FStartUpTime;
77 published
78 property Identification: string read FIdentification write FIdentification; // Unique system name
79 property Manager: TModuleManager read FManager write SetManager;
80 property Version: string read FVersion write FVersion;
81 property ReleaseTime: TDateTime read FReleaseTime write FReleaseTime;
82 property Title: string read FTitle write FTitle;
83 property License: string read FLicense write FLicense;
84 property Author: string read FAuthor write FAuthor;
85 property Dependencies: TStringList read FDependencies write FDependencies;
86 property Description: TStringList read FDescription write FDescription;
87 property FileName: string read FFileName write FFileName;
88 property Category: string read FCategory write FCategory;
89 property WebSite: string read FWebSite write FWebSite;
90 // Screenshots, reviews, icon, weak dependencies, ...
91 property OnUpdate: TNotifyEvent read FOnUpdate write FOnUpdate;
92 end;
93
94 { TModules }
95
96 TModules = class(TObjectList<TModule>)
97 public
98 procedure Perform(Actions: array of TModuleAction; Conditions: TModuleConditions = [mcAll]);
99 function FindByName(Name: string): TModule;
100 function GetNames: string;
101 end;
102
103 TModuleManagerOption = (moAutoInstallOnRun, moAuto);
104 TModuleManagerOptions = set of TModuleManagerOption;
105
106 { TModuleManager }
107
108 TModuleManager = class(TComponent)
109 private
110 FAPI: TAPI;
111 FOnUpdate: TNotifyEvent;
112 FUpdateCount: Integer;
113 FOptions: TModuleManagerOptions;
114 procedure SetAPI(AValue: TAPI);
115 procedure DoUpdate(Sender: TObject);
116 public
117 Modules: TModules;
118 function ModuleRunning(Name: string): Boolean;
119 procedure EnumDependenciesCascade(Module: TModule; ModuleList: TModules;
120 Conditions: TModuleConditions = [mcAll]);
121 procedure EnumSuperiorDependenciesCascade(Module: TModule;
122 ModuleList: TModules; Conditions: TModuleConditions = [mcAll]);
123 procedure RegisterModule(Module: TModule);
124 procedure UnregisterModule(Module: TModule);
125 procedure LoadFromRegistry(Context: TRegistryContext);
126 procedure SaveToRegistry(Context: TRegistryContext);
127 constructor Create(AOwner: TComponent); override;
128 destructor Destroy; override;
129 property API: TAPI read FAPI write SetAPI;
130 published
131 property OnUpdate: TNotifyEvent read FOnUpdate write FOnUpdate;
132 property Options: TModuleManagerOptions read FOptions write FOptions;
133 end;
134
135procedure Register;
136
137
138implementation
139
140resourcestring
141 SModuleNotFound = 'Module "%1:s" not found as dependency for module "%0:s"';
142
143procedure Register;
144begin
145 RegisterComponents('ModularSystem', [TModuleManager, TModule]);
146end;
147
148{ TModules }
149
150procedure TModules.Perform(Actions: array of TModuleAction;
151 Conditions: TModuleConditions = [mcAll]);
152var
153 I: Integer;
154 A: Integer;
155begin
156 for I := 0 to Count - 1 do
157 with TModule(Items[I]) do
158 if (mcAll in Conditions) or
159 (Running and (mcRunning in Conditions)) or
160 (not Running and (mcNotRunning in Conditions)) or
161 (Installed and (mcInstalled in Conditions)) or
162 (not Installed and (mcNotInstalled in Conditions)) or
163 (Enabled and (mcEnabled in Conditions)) or
164 (not Enabled and (mcNotEnabled in Conditions)) then
165 for A := 0 to High(Actions) do begin
166 if Actions[A] = maStart then Start;
167 if Actions[A] = maStop then Stop;
168 if Actions[A] = maInstall then Install;
169 if Actions[A] = maUninstall then Uninstall;
170 if Actions[A] = maUpgrade then Upgrade;
171 if Actions[A] = maEnable then Enable;
172 if Actions[A] = maDisable then Disable;
173 end;
174end;
175
176function TModules.FindByName(Name: string): TModule;
177var
178 I: Integer;
179begin
180 I := 0;
181 while (I < Count) and (TModule(Items[I]).Identification <> Name) do Inc(I);
182 if I < Count then Result := TModule(Items[I])
183 else Result := nil;
184end;
185
186function TModules.GetNames: string;
187var
188 I: Integer;
189begin
190 Result := '';
191 for I := 0 to Count - 1 do
192 Result := Result + ', ' + Items[I].Identification;
193 Result := Copy(Result, 3, MaxInt);
194end;
195
196{ TModuleManager }
197
198procedure TModuleManager.SetAPI(AValue: TAPI);
199var
200 I: Integer;
201begin
202 if FAPI = AValue then Exit;
203 FAPI := AValue;
204 for I := 0 to Modules.Count - 1 do
205 TModule(Modules[I]).API := FAPI;
206end;
207
208procedure TModuleManager.DoUpdate(Sender: TObject);
209begin
210 if Assigned(FOnUpdate) then FOnUpdate(Self);
211end;
212
213function TModuleManager.ModuleRunning(Name: string): Boolean;
214var
215 Module: TModule;
216begin
217 Module := Modules.FindByName(Name);
218 if Assigned(Module) then begin
219 Result := Module.Running;
220 end else Result := False;
221end;
222
223procedure TModuleManager.EnumDependenciesCascade(Module: TModule;
224 ModuleList: TModules; Conditions: TModuleConditions = [mcAll]);
225var
226 DepModule: TModule;
227 DepModuleName: string;
228 I: Integer;
229begin
230 for I := 0 to Module.Dependencies.Count - 1 do begin
231 DepModuleName := Module.Dependencies[I];
232 DepModule := Modules.FindByName(DepModuleName);
233 if Assigned(DepModule) then
234 with DepModule do begin
235 if (ModuleList.IndexOf(DepModule) = -1) and
236 ((mcAll in Conditions) or
237 (Running and (mcRunning in Conditions)) or
238 (not Running and (mcNotRunning in Conditions)) or
239 (Installed and (mcInstalled in Conditions)) or
240 (not Installed and (mcNotInstalled in Conditions)) or
241 (Enabled and (mcEnabled in Conditions)) or
242 (not Enabled and (mcNotEnabled in Conditions))) then begin
243 ModuleList.Add(DepModule);
244 Self.EnumDependenciesCascade(DepModule, ModuleList);
245 end;
246 end else raise Exception.CreateFmt(SModuleNotFound, [Module.Dependencies[I], Module.Identification]);
247 end;
248end;
249
250procedure TModuleManager.EnumSuperiorDependenciesCascade(Module: TModule;
251 ModuleList: TModules; Conditions: TModuleConditions = [mcAll]);
252var
253 I: Integer;
254begin
255 for I := 0 to Modules.Count - 1 do
256 with TModule(Modules[I]) do begin
257 if (Dependencies.IndexOf(Module.Identification) <> -1) and
258 (ModuleList.IndexOf(TModule(Modules[I])) = -1) and
259 ((mcAll in Conditions) or
260 (Running and (mcRunning in Conditions)) or
261 (not Running and (mcNotRunning in Conditions)) or
262 (Installed and (mcInstalled in Conditions)) or
263 (not Installed and (mcNotInstalled in Conditions)) or
264 (Enabled and (mcEnabled in Conditions)) or
265 (not Enabled and (mcNotEnabled in Conditions))) then begin
266 ModuleList.Add(TModule(Modules[I]));
267 Self.EnumSuperiorDependenciesCascade(TModule(Modules[I]), ModuleList);
268 end;
269 end;
270end;
271
272procedure TModuleManager.RegisterModule(Module: TModule);
273begin
274 Modules.Add(Module);
275 Module.FManager := Self;
276 Module.API := API;
277 DoUpdate(Self);
278end;
279
280procedure TModuleManager.UnregisterModule(Module: TModule);
281begin
282 Modules.Remove(Module);
283 DoUpdate(Self);
284end;
285
286constructor TModuleManager.Create(AOwner: TComponent);
287begin
288 inherited;
289 Modules := TModules.Create;
290 Modules.OwnsObjects := False;
291end;
292
293destructor TModuleManager.Destroy;
294begin
295 Modules.Perform([maStop]);
296 FreeAndNil(Modules);
297 inherited;
298end;
299
300procedure TModuleManager.LoadFromRegistry(Context: TRegistryContext);
301var
302 I: Integer;
303begin
304 with TRegistryEx.Create do
305 try
306 RootKey := Context.RootKey;
307 for I := 0 to Modules.Count - 1 do
308 with Modules[I] do begin
309 OpenKey(Context.Key + '\' + Identification, True);
310 Installed := ReadBoolWithDefault('Installed', Installed);
311 Enabled := ReadBoolWithDefault('Enabled', Enabled);
312 end;
313 finally
314 Free;
315 end;
316end;
317
318procedure TModuleManager.SaveToRegistry(Context: TRegistryContext);
319var
320 I: Integer;
321begin
322 with TRegistryEx.Create do
323 try
324 RootKey := Context.RootKey;
325 for I := 0 to Modules.Count - 1 do
326 with Modules[I] do begin
327 OpenKey(Context.Key + '\' + Identification, True);
328 WriteBool('Enabled', Enabled);
329 WriteBool('Installed', Installed);
330 end;
331 finally
332 Free;
333 end;
334end;
335
336{ TModule }
337
338procedure TModule.SetRunning(AValue: Boolean);
339begin
340 if FRunning = AValue then Exit;
341 if AValue then Start else Stop;
342end;
343
344procedure TModule.DoStart;
345begin
346end;
347
348procedure TModule.DoStop;
349begin
350end;
351
352procedure TModule.DoInstall;
353begin
354end;
355
356procedure TModule.DoUninstall;
357begin
358end;
359
360procedure TModule.DoUpgrade;
361begin
362end;
363
364procedure TModule.DoUpdate;
365begin
366 if Assigned(FOnUpdate) then FOnUpdate(Self);
367end;
368
369procedure TModule.Enable;
370var
371 List: TModules;
372begin
373 if Enabled then Exit;
374 FEnabled := True;
375 try
376 List := TModules.Create;
377 List.OwnsObjects := False;
378 EnumDependenciesCascade(List, [mcNotEnabled]);
379 List.Perform([maEnable], [mcNotEnabled]);
380 finally
381 List.Free;
382 end;
383 Start; // Auto start enabled modules
384 //Manager.Update;
385end;
386
387procedure TModule.Disable;
388var
389 List: TModules;
390begin
391 if not Enabled then Exit;
392 if FRunning then Stop; // Auto stop running modules
393 FEnabled := False;
394 try
395 List := TModules.Create;
396 List.OwnsObjects := False;
397 EnumSuperiorDependenciesCascade(List, [mcEnabled]);
398 List.Perform([maDisable], [mcEnabled]);
399 finally
400 List.Free;
401 end;
402 DoUpdate;
403end;
404
405procedure TModule.SetInstalled(AValue: Boolean);
406begin
407 if FInstalled = AValue then Exit;
408 if AValue then Install else Uninstall;
409end;
410
411procedure TModule.SetManager(AValue: TModuleManager);
412begin
413 if FManager = AValue then Exit;
414 if Assigned(FManager) then FManager.UnregisterModule(Self);
415 FManager := AValue;
416 if Assigned(FManager) then AValue.RegisterModule(Self);
417end;
418
419procedure TModule.SetEnabled(AValue: Boolean);
420begin
421 if FEnabled = AValue then Exit;
422 if AValue then Enable else Disable;
423end;
424
425procedure TModule.Start;
426var
427 List: TModules;
428 StartTime: TDateTime;
429begin
430 if not Enabled or Running then Exit;
431 if not Installed then Install; // Auto install not installed modules
432 try
433 List := TModules.Create;
434 List.OwnsObjects := False;
435 EnumDependenciesCascade(List, [mcNotRunning]);
436 List.Perform([maStart], [mcNotRunning]);
437 finally
438 List.Free;
439 end;
440 StartTime := Now;
441 DoStart;
442 FStartUpTime := Now - StartTime;
443 FRunning := True;
444 DoUpdate;
445end;
446
447procedure TModule.Stop;
448var
449 List: TModules;
450begin
451 if not Running then Exit;
452 FRunning := False;
453 try
454 List := TModules.Create;
455 List.OwnsObjects := False;
456 EnumSuperiorDependenciesCascade(List, [mcRunning]);
457 List.Perform([maStop], [mcRunning]);
458 finally
459 List.Free;
460 end;
461 DoStop;
462 DoUpdate;
463end;
464
465procedure TModule.Restart;
466begin
467 Stop;
468 Start;
469end;
470
471procedure TModule.Install;
472var
473 List: TModules;
474begin
475 if Installed then Exit;
476 try
477 List := TModules.Create;
478 List.OwnsObjects := False;
479 EnumDependenciesCascade(List, [mcNotInstalled]);
480 List.Perform([maInstall], [mcNotInstalled]);
481 finally
482 List.Free;
483 end;
484 FInstalled := True;
485 DoInstall;
486 Enable; // Auto enable installed module
487 DoUpdate;
488end;
489
490procedure TModule.Uninstall;
491var
492 List: TModules;
493begin
494 if not Installed then Exit;
495 if Enabled then Disable; // Auto disable uninstalled module
496 try
497 List := TModules.Create;
498 List.OwnsObjects := False;
499 EnumSuperiorDependenciesCascade(List, [mcInstalled]);
500 List.Perform([maUninstall], [mcInstalled]);
501 finally
502 List.Free;
503 end;
504 FInstalled := False;
505 DoUninstall;
506 DoUpdate;
507end;
508
509procedure TModule.Reinstall;
510begin
511 Uninstall;
512 Install;
513end;
514
515procedure TModule.Upgrade;
516begin
517 if not Enabled or not Installed then Exit;
518 if Running then
519 try
520 Stop;
521 DoUpgrade;
522 finally
523 Start;
524 end else DoUpgrade;
525 DoUpdate;
526end;
527
528procedure TModule.EnumDependenciesCascade(ModuleList: TModules;
529 Conditions: TModuleConditions = [mcAll]);
530begin
531 ModuleList.Clear;
532 Manager.EnumDependenciesCascade(Self, ModuleList, Conditions);
533end;
534
535procedure TModule.EnumSuperiorDependenciesCascade(ModuleList: TModules;
536 Conditions: TModuleConditions = [mcAll]);
537begin
538 ModuleList.Clear;
539 Manager.EnumSuperiorDependenciesCascade(Self, ModuleList, Conditions);
540end;
541
542procedure TModule.SetInstalledState(Value: Boolean);
543begin
544 FInstalled := Value;
545 DoUpdate;
546end;
547
548constructor TModule.Create(Owner: TComponent);
549begin
550 inherited;
551 Dependencies := TStringList.Create;
552 Description := TStringList.Create;
553end;
554
555destructor TModule.Destroy;
556begin
557 Running := False;
558 FreeAndNil(FDescription);
559 FreeAndNil(FDependencies);
560 inherited;
561end;
562
563end.
564
Note: See TracBrowser for help on using the repository browser.