1 {
2 /***************************************************************************
3 lpkcache.pas
4 ------------
5
6
7 ***************************************************************************/
8
9 ***************************************************************************
10 * *
11 * This source is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This code is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
19 * General Public License for more details. *
20 * *
21 * A copy of the GNU General Public License is available on the World *
22 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
23 * obtain it by writing to the Free Software Foundation, *
24 * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
25 * *
26 ***************************************************************************
27
28 Author: Mattias Gaertner
29
30 Abstract:
31 Multithreaded scanner for lpk files to gather information about all
32 available lpk files.
33
34 Why this unit is needed:
35 The *loaded* packages are handled by the PackageGraph (unit packagesystem).
36 The IDE remembers all lpk files (file name and version) of the users
37 disk in $(PrimaryConfigPath)/packagefiles.xml.
38 The lpk files are often scattered on the disk, might be outdated,
39 broken, wrong version or on slow network shares, so scanning them is
40 expensive. That's why this is done in another thread.
41
42 Usage:
43 LPKInfoCache.StartLPKReaderWithAllAvailable;
44 or LPKInfoCache.StartLPKReader(ListOfLPKFiles)
45
46 }
47 unit LPKCache;
48
49 {$mode objfpc}{$H+}
50
51 interface
52
53 uses
54 Classes, SysUtils, Laz_AVL_Tree,
55 // LazUtils
56 LazFileUtils, Laz2_XMLCfg, LazLoggerBase, LazTracer, LazMethodList,
57 // IdeIntf
58 PackageDependencyIntf, PackageIntf, PackageLinkIntf,
59 // IDE
60 EnvironmentOpts, PackageLinks, PackageDefs, PackageSystem;
61
62 type
63 TLPKInfoState = (
64 lpkiNotParsed,
65 lpkiParsing,
66 lpkiParsedError,
67 lpkiParsed
68 );
69
70 { TLPKInfo }
71
72 TLPKInfo = class
73 public
74 ID: TLazPackageID; // name and version
75 LPKFilename: string;
76 InLazSrc: boolean; // lpk is in lazarus source directory
77 Installed: TPackageInstallType;
78 Base: boolean; // is base package, can not be uninstalled
79
80 LPKParsed: TLPKInfoState;
81 LPKError: string;
82
83 // the below is only valid if TLPKInfoState=lpkiParsed
84 Author: string;
85 Description: string;
86 License: string;
87 PkgType: TLazPackageType; // design, runtime
88
89 procedure Assign(Source: TObject);
90 constructor Create(TheID: TLazPackageID);
91 destructor Destroy; override;
92 end;
93
94 TLPKInfoCache = class;
95
96 { TIPSLPKReader }
97
98 TIPSLPKReader = class(TThread)
99 protected
100 procedure SynChangePkgVersion;
101 procedure SynQueueEmpty;
102 procedure Log(Msg: string);
103 procedure Execute; override;
104 public
105 Cache: TLPKInfoCache;
106 NewVersion: TPkgVersion;
107 Info: TLPKInfo; // currently processed info
108 Abort: boolean;
109 FilenameQueue: TStrings; // list of file names to parse by the lpkreader thread
110 destructor Destroy; override;
111 end;
112
113 TLPKInfoCacheEvent = (
114 liceOnBeforeVersionChange,
115 liceOnAfterVersionChange,
116 liceOnQueueEmpty
117 );
118 TOnLPKInfoBeforeVersionChange = procedure(PkgInfo: TLPKInfo; NewID: TPkgVersion) of object;
119 TOnLPKInfoAfterVersionChange = procedure(PkgInfo: TLPKInfo; OldID: string) of object;
120
121 { TLPKInfoCache }
122
123 TLPKInfoCache = class
124 private
125 FCritSec: TRTLCriticalSection;
126 FLPKReader: TIPSLPKReader;
127 fLPKByFilename: TAvlTree; // tree of TLPKInfo sorted for LPKFilename
128 fLPKByID: TAvlTree; // tree of TLPKInfo sorted for ID
129 fEvents: array[TLPKInfoCacheEvent] of TMethodList;
130 fAvailableFiles: TStrings; // used by OnIterateAvailablePackages
131 procedure QueueEmpty;
132 procedure OnIterateAvailablePackages(APackage: TLazPackageID);
133 public
134 constructor Create;
135 destructor Destroy; override;
136
137 // call by main thread only
138 procedure StartLPKReaderWithAllAvailable;
139 procedure StartLPKReader(Filenames: TStrings);
140 procedure EndLPKReader;
141 procedure ParseLPKInfoInMainThread(Info: TLPKInfo);
142 procedure AddOnBeforeVersionChange(const OnBefore: TOnLPKInfoBeforeVersionChange;
143 AsLast: boolean = true);
144 procedure RemoveOnBeforeVersionChange(const OnBefore: TOnLPKInfoBeforeVersionChange);
145 procedure AddOnAfterVersionChange(const OnAfter: TOnLPKInfoAfterVersionChange;
146 AsLast: boolean = true);
147 procedure RemoveOnAfterVersionChange(const OnAfter: TOnLPKInfoAfterVersionChange);
148 procedure AddOnQueueEmpty(const OnEmpty: TNotifyEvent; AsLast: boolean = true);
149 procedure RemoveOnQueueEmpty(const OnEmpty: TNotifyEvent);
150 procedure ChangePkgVersion(PkgInfo: TLPKInfo; NewVersion: TPkgVersion);
151
152 // requires critical section
153 procedure EnterCritSection;
154 procedure LeaveCritSection;
FindPkgInfoWithFilenamenull155 function FindPkgInfoWithFilename(aFilename: string): TLPKInfo; // requires crit sec
FindPkgInfoWithIDnull156 function FindPkgInfoWithID(PkgID: TLazPackageID): TLPKInfo; // requires crit sec
FindPkgInfoWithIDAsStringnull157 function FindPkgInfoWithIDAsString(PkgID: string): TLPKInfo; // requires crit sec
158 property LPKByFilename: TAvlTree read fLPKByFilename; // tree of TLPKInfo sorted for LPKFilename
159 property LPKByID: TAvlTree read fLPKByID; // tree of TLPKInfo sorted for ID
160
161 // thread safe
IsValidLPKFilenamenull162 function IsValidLPKFilename(LPKFilename: string): boolean;
163 procedure ParseLPK(LPKFilename: string;
164 out ErrorMsg, Author, License, Description: string;
165 out PkgType: TLazPackageType;
166 var Version: TPkgVersion); // called by main and helper thread
167 procedure ParseLPKInfo(Info: TLPKInfo; var NewVersion: TPkgVersion);
168 end;
169
170 var
171 LPKInfoCache: TLPKInfoCache = nil; // set by main.pp
172
CompareIPSPkgInfosnull173 function CompareIPSPkgInfos(PkgInfo1, PkgInfo2: Pointer): integer;
ComparePkgIDWithIPSPkgInfonull174 function ComparePkgIDWithIPSPkgInfo(PkgID, PkgInfo: Pointer): integer;
CompareIPSPkgInfosWithFilenamenull175 function CompareIPSPkgInfosWithFilename(PkgInfo1, PkgInfo2: Pointer): integer;
CompareFilenameWithIPSPkgInfonull176 function CompareFilenameWithIPSPkgInfo(Filename, PkgInfo: Pointer): integer;
177
178 implementation
179
CompareIPSPkgInfosnull180 function CompareIPSPkgInfos(PkgInfo1, PkgInfo2: Pointer): integer;
181 var
182 Info1: TLPKInfo absolute PkgInfo1;
183 Info2: TLPKInfo absolute PkgInfo2;
184 begin
185 Result:=CompareLazPackageIDNames(Info1.ID,Info2.ID);
186 end;
187
ComparePkgIDWithIPSPkgInfonull188 function ComparePkgIDWithIPSPkgInfo(PkgID, PkgInfo: Pointer): integer;
189 var
190 ID: TLazPackageID absolute PkgID;
191 Info: TLPKInfo absolute PkgInfo;
192 begin
193 Result:=CompareLazPackageIDNames(ID,Info.ID);
194 end;
195
CompareIPSPkgInfosWithFilenamenull196 function CompareIPSPkgInfosWithFilename(PkgInfo1, PkgInfo2: Pointer): integer;
197 var
198 Info1: TLPKInfo absolute PkgInfo1;
199 Info2: TLPKInfo absolute PkgInfo2;
200 begin
201 Result:=CompareFilenames(Info1.LPKFilename,Info2.LPKFilename);
202 end;
203
CompareFilenameWithIPSPkgInfonull204 function CompareFilenameWithIPSPkgInfo(Filename, PkgInfo: Pointer): integer;
205 var
206 Info: TLPKInfo absolute PkgInfo;
207 begin
208 Result:=CompareFilenames(AnsiString(Filename),Info.LPKFilename);
209 end;
210
211 { TLPKInfoCache }
212
213 procedure TLPKInfoCache.StartLPKReader(Filenames: TStrings);
214 var
215 i: Integer;
216 CurFilename: String;
217 Info: TLPKInfo;
218 ID: TLazPackageID;
219 NeedsStart: Boolean;
220 Pkg: TLazPackage;
221 begin
222 if (Filenames=nil) or (Filenames.Count=0) then begin
223 QueueEmpty;
224 exit;
225 end;
226 NeedsStart:=false;
227 EnterCritSection;
228 try
229 for i:=Filenames.Count-1 downto 0 do
230 begin
231 CurFilename:=Filenames[i];
232 if not IsValidLPKFilename(CurFilename) then continue;
233 Info:=FindPkgInfoWithFilename(CurFilename);
234 if Info<>nil then begin
235 // info is known
236 if Info.LPKParsed<>lpkiNotParsed then continue;
237 end else begin
238 // new info
239 ID:=TLazPackageID.Create;
240 ID.Name:=ExtractFileNameOnly(CurFilename);
241 Info:=TLPKInfo.Create(ID);
242 Info.LPKFilename:=CurFilename;
243 Info.InLazSrc:=FileIsInPath(Info.LPKFilename,
244 EnvironmentOptions.GetParsedLazarusDirectory);
245 Info.Base:=Info.InLazSrc and PackageGraph.IsStaticBasePackage(Info.ID.Name);
246 Pkg:=PackageGraph.FindPackageWithFilename(Info.LPKFilename);
247 if Pkg<>nil then
248 Info.Installed:=Pkg.Installed;
249 fLPKByFilename.Add(Info);
250 fLPKByID.Add(Info);
251 end;
252 if FLPKReader=nil then begin
253 // create thread
254 FLPKReader:=TIPSLPKReader.Create(true);
255 FLPKReader.Cache:=Self;
256 FLPKReader.FreeOnTerminate:=true;
257 FLPKReader.FilenameQueue:=TStringList.Create;
258 end;
259 FLPKReader.FilenameQueue.Add(Info.LPKFilename);
260 NeedsStart:=true;
261 end;
262 finally
263 LeaveCritSection;
264 end;
265
266 if NeedsStart then
267 FLPKReader.Start
268 else
269 QueueEmpty;
270 end;
271
272 procedure TLPKInfoCache.EndLPKReader;
273 var
274 i: Integer;
275 begin
276 EnterCritSection;
277 try
278 if FLPKReader=nil then exit;
279 FLPKReader.Abort:=true;
280 finally
281 LeaveCritSection;
282 end;
283 i:=0;
284 while FLPKReader<>nil do begin
285 Sleep(10);
286 inc(i,10);
287 if i>=1000 then begin
288 debugln(['TLPKInfoCache.EndLPKReader still waiting for lpk reader to end ...']);
289 i:=0;
290 end;
291 end;
292 end;
293
294 procedure TLPKInfoCache.ParseLPKInfoInMainThread(Info: TLPKInfo);
295 var
296 NewVersion: TPkgVersion;
297 begin
298 NewVersion:=nil;
299 try
300 ParseLPKInfo(Info,NewVersion);
301 ChangePkgVersion(Info,NewVersion);
302 finally
303 NewVersion.Free;
304 end;
305 end;
306
TLPKInfoCache.IsValidLPKFilenamenull307 function TLPKInfoCache.IsValidLPKFilename(LPKFilename: string): boolean;
308 var
309 PkgName: String;
310 begin
311 Result:=false;
312 if not FilenameIsAbsolute(LPKFilename) then exit;
313 if not FilenameExtIs(LPKFilename,'.lpk') then exit;
314 PkgName:=ExtractFileNameOnly(LPKFilename);
315 if not IsValidPkgName(PkgName) then exit;
316 Result:=true;
317 end;
318
319 procedure TLPKInfoCache.AddOnBeforeVersionChange(
320 const OnBefore: TOnLPKInfoBeforeVersionChange; AsLast: boolean);
321 begin
322 fEvents[liceOnBeforeVersionChange].Add(TMethod(OnBefore),AsLast);
323 end;
324
325 procedure TLPKInfoCache.RemoveOnBeforeVersionChange(
326 const OnBefore: TOnLPKInfoBeforeVersionChange);
327 begin
328 fEvents[liceOnBeforeVersionChange].Remove(TMethod(OnBefore));
329 end;
330
331 procedure TLPKInfoCache.AddOnAfterVersionChange(
332 const OnAfter: TOnLPKInfoAfterVersionChange; AsLast: boolean);
333 begin
334 fEvents[liceOnAfterVersionChange].Add(TMethod(OnAfter),AsLast);
335 end;
336
337 procedure TLPKInfoCache.RemoveOnAfterVersionChange(
338 const OnAfter: TOnLPKInfoAfterVersionChange);
339 begin
340 fEvents[liceOnAfterVersionChange].Remove(TMethod(OnAfter));
341 end;
342
343 procedure TLPKInfoCache.AddOnQueueEmpty(const OnEmpty: TNotifyEvent;
344 AsLast: boolean);
345 begin
346 fEvents[liceOnQueueEmpty].Add(TMethod(OnEmpty),AsLast);
347 end;
348
349 procedure TLPKInfoCache.RemoveOnQueueEmpty(const OnEmpty: TNotifyEvent);
350 begin
351 fEvents[liceOnQueueEmpty].Remove(TMethod(OnEmpty));
352 end;
353
354 procedure TLPKInfoCache.OnIterateAvailablePackages(APackage: TLazPackageID);
355 begin
356 if APackage is TLazPackage then
357 fAvailableFiles.Add(TLazPackage(APackage).Filename)
358 else if APackage is TLazPackageLink then begin
359 {if (OPMInterface<>nil) and (TLazPackageLink(APackage).Origin=ploOnline) and
360 (not OPMInterface.IsPackageAvailable(TLazPackageLink(APackage), 2)) then
361 fAvailableFiles.Add(TLazPackageLink(APackage).OPMFileName)
362 else}
363 fAvailableFiles.Add(TLazPackageLink(APackage).LPKFilename);
364 end;
365 end;
366
367 procedure TLPKInfoCache.QueueEmpty;
368 begin
369 fEvents[liceOnQueueEmpty].CallNotifyEvents(Self);
370 end;
371
372 procedure TLPKInfoCache.ChangePkgVersion(PkgInfo: TLPKInfo;
373 NewVersion: TPkgVersion);
374 var
375 OldID: String;
376 i: Integer;
377 begin
378 if PkgInfo.ID.Version.Compare(NewVersion)=0 then exit;
379 // notify before
380 i:=fEvents[liceOnBeforeVersionChange].Count;
381 while fEvents[liceOnBeforeVersionChange].NextDownIndex(i) do
382 TOnLPKInfoBeforeVersionChange(fEvents[liceOnBeforeVersionChange].Items[i])(PkgInfo,NewVersion);
383 // change
384 fLPKByID.Remove(PkgInfo);
385 OldID:=PkgInfo.ID.IDAsString;
386 PkgInfo.ID.Version.Assign(NewVersion);
387 fLPKByID.Add(PkgInfo);
388 // notify after
389 i:=fEvents[liceOnAfterVersionChange].Count;
390 while fEvents[liceOnAfterVersionChange].NextDownIndex(i) do
391 TOnLPKInfoAfterVersionChange(fEvents[liceOnAfterVersionChange].Items[i])(PkgInfo,OldID);
392 end;
393
FindPkgInfoWithFilenamenull394 function TLPKInfoCache.FindPkgInfoWithFilename(aFilename: string): TLPKInfo;
395 var
396 Node: TAvlTreeNode;
397 begin
398 Node:=fLPKByFilename.FindKey(Pointer(aFilename),@CompareFilenameWithIPSPkgInfo);
399 if Node<>nil then
400 Result:=TLPKInfo(Node.Data)
401 else
402 Result:=nil;
403 end;
404
TLPKInfoCache.FindPkgInfoWithIDnull405 function TLPKInfoCache.FindPkgInfoWithID(PkgID: TLazPackageID): TLPKInfo;
406 var
407 Node: TAvlTreeNode;
408 begin
409 Node:=fLPKByID.FindKey(Pointer(PkgID),@ComparePkgIDWithIPSPkgInfo);
410 if Node<>nil then
411 Result:=TLPKInfo(Node.Data)
412 else
413 Result:=nil;
414 end;
415
TLPKInfoCache.FindPkgInfoWithIDAsStringnull416 function TLPKInfoCache.FindPkgInfoWithIDAsString(PkgID: string): TLPKInfo;
417 var
418 ID: TLazPackageID;
419 begin
420 Result:=nil;
421 ID:=TLazPackageID.Create;
422 try
423 if not ID.StringToID(PkgID) then exit;
424 Result:=FindPkgInfoWithID(ID);
425 finally
426 ID.Free;
427 end;
428 end;
429
430 procedure TLPKInfoCache.ParseLPK(LPKFilename: string; out ErrorMsg, Author,
431 License, Description: string; out PkgType: TLazPackageType;
432 var Version: TPkgVersion);
433 var
434 Path: String;
435 XMLConfig: TXMLConfig;
436 FileVersion: Integer;
437 begin
438 ErrorMsg:='';
439 Author:='';
440 License:='';
441 Description:='';
442 PkgType:=lptRunAndDesignTime;
443 Version.Clear;
444 if FilenameIsAbsolute(LPKFilename) and FileExistsUTF8(LPKFilename) then begin
445 // load the package file
446 try
447 XMLConfig:=TXMLConfig.Create(LPKFilename);
448 try
449 Path:='Package/';
450 FileVersion:=XMLConfig.GetValue(Path+'Version',0);
451 Author:=XMLConfig.GetValue(Path+'Author/Value','');
452 Description:=XMLConfig.GetValue(Path+'Description/Value','');
453 License:=XMLConfig.GetValue(Path+'License/Value','');
454 PkgType:=LazPackageTypeIdentToType(XMLConfig.GetValue(Path+'Type/Value',
455 LazPackageTypeIdents[lptRunTime]));
456 PkgVersionLoadFromXMLConfig(Version,XMLConfig,Path+'Version/',FileVersion);
457 finally
458 XMLConfig.Free;
459 end;
460 except
461 on E: Exception do begin
462 ErrorMsg:='file="'+LPKFilename+'": '+E.Message;
463 DebugLn('TLPKInfoCache.ParseLPK ERROR: '+ErrorMsg);
464 end;
465 end;
466 end else begin
467 ErrorMsg:='file not found "'+LPKFilename+'"';
468 end;
469 end;
470
471 procedure TLPKInfoCache.ParseLPKInfo(Info: TLPKInfo;
472 var NewVersion: TPkgVersion);
473 // if not done, parse the lpk and update LPKError, LPKParsed,
474 // Author, Description, License, PkgType
475 // Version is not changed, but returned in NewVersion
476 var
477 ErrorMsg: string;
478 Author: string;
479 License: string;
480 Description: string;
481 PkgType: TLazPackageType;
482 begin
483 // check if alread parsed
484 EnterCritSection;
485 try
486 if NewVersion=nil then begin
487 NewVersion:=TPkgVersion.Create;
488 NewVersion.Assign(Info.ID.Version);
489 end;
490 if Info.LPKParsed<>lpkiNotParsed then exit;
491 Info.LPKParsed:=lpkiParsing;
492 finally
493 LeaveCritSection;
494 end;
495
496 // parse
497 ParseLPK(Info.LPKFilename,ErrorMsg,Author,License,Description,PkgType,NewVersion);
498
499 // change info
500 // Note: the version is not changed
501 EnterCritSection;
502 try
503 if Info.LPKParsed<>lpkiParsing then exit;
504 if ErrorMsg<>'' then begin
505 Info.LPKError:=ErrorMsg;
506 Info.LPKParsed:=lpkiParsedError;
507 end else begin
508 Info.LPKError:='';
509 Info.LPKParsed:=lpkiParsed;
510 Info.Author:=Author;
511 Info.Description:=Description;
512 Info.License:=License;
513 Info.PkgType:=PkgType;
514 end;
515 finally
516 LeaveCritSection;
517 end;
518 end;
519
520 constructor TLPKInfoCache.Create;
521 var
522 e: TLPKInfoCacheEvent;
523 begin
524 InitCriticalSection(FCritSec);
525 fLPKByFilename:=TAvlTree.Create(@CompareIPSPkgInfosWithFilename);
526 fLPKByID:=TAvlTree.Create(@CompareIPSPkgInfos);
527 for e:=Low(TLPKInfoCacheEvent) to high(TLPKInfoCacheEvent) do
528 fEvents[e]:=TMethodList.Create;
529 end;
530
531 destructor TLPKInfoCache.Destroy;
532 var
533 e: TLPKInfoCacheEvent;
534 begin
535 EndLPKReader;
536 FreeAndNil(fLPKByID);
537 fLPKByFilename.FreeAndClear;
538 FreeAndNil(fLPKByFilename);
539 for e:=Low(TLPKInfoCacheEvent) to high(TLPKInfoCacheEvent) do
540 FreeAndNil(fEvents[e]);
541 inherited Destroy;
542 DoneCriticalsection(FCritSec);
543 end;
544
545 procedure TLPKInfoCache.StartLPKReaderWithAllAvailable;
546 begin
547 fAvailableFiles:=TStringList.Create;
548 try
549 PackageGraph.IteratePackages(fpfSearchAllExisting,@OnIterateAvailablePackages);
550 StartLPKReader(fAvailableFiles);
551 finally
552 FreeAndNil(fAvailableFiles);
553 end;
554 end;
555
556 procedure TLPKInfoCache.EnterCritSection;
557 begin
558 EnterCriticalsection(FCritSec);
559 end;
560
561 procedure TLPKInfoCache.LeaveCritSection;
562 begin
563 LeaveCriticalsection(FCritSec);
564 end;
565
566 { TIPSLPKReader }
567
568 procedure TIPSLPKReader.Execute;
569 begin
570 try
571 while not Abort do begin
572 // get next lpk to parse
573 Cache.EnterCritSection;
574 try
575 Info:=nil;
576 while FilenameQueue.Count>0 do begin
577 Info:=Cache.FindPkgInfoWithFilename(FilenameQueue[FilenameQueue.Count-1]);
578 FilenameQueue.Delete(FilenameQueue.Count-1);
579 if Info=nil then continue;
580 if Info.LPKParsed=lpkiNotParsed then
581 break
582 else
583 Info:=nil;
584 end;
585 if Info=nil then break;
586 finally
587 Cache.LeaveCritSection;
588 end;
589 Cache.ParseLPKInfo(Info,NewVersion);
590 if NewVersion.Compare(Info.ID.Version)<>0 then begin
591 Synchronize(@SynChangePkgVersion);
592 end;
593 Info:=nil;
594 end;
595 except
596 on E: Exception do begin
597 Log('ERROR: TIPSLPKReader.Execute: '+E.Message);
598 end;
599 end;
600
601 Synchronize(@SynQueueEmpty);
602
603 Cache.EnterCritSection;
604 try
605 Cache.FLPKReader:=nil;
606 finally
607 Cache.LeaveCritSection;
608 end;
609 end;
610
611 procedure TIPSLPKReader.SynChangePkgVersion;
612 begin
613 Cache.ChangePkgVersion(Info,NewVersion);
614 end;
615
616 procedure TIPSLPKReader.SynQueueEmpty;
617 begin
618 Cache.QueueEmpty;
619 end;
620
621 procedure TIPSLPKReader.Log(Msg: string);
622 begin
623 debugln(['TIPSLPKReader.Log: ',Msg]);
624 end;
625
626 destructor TIPSLPKReader.Destroy;
627 begin
628 FreeAndNil(FilenameQueue);
629 FreeAndNil(NewVersion);
630 inherited Destroy;
631 end;
632
633 { TLPKInfo }
634
635 constructor TLPKInfo.Create(TheID: TLazPackageID);
636 begin
637 ID:=TheID;
638 end;
639
640 procedure TLPKInfo.Assign(Source: TObject);
641 var
642 SrcInfo: TLPKInfo;
643 SrcID: TLazPackageID;
644 begin
645 if Source is TLPKInfo then
646 begin
647 SrcInfo:=TLPKInfo(Source);
648 PkgType:=SrcInfo.PkgType;
649 LPKParsed:=SrcInfo.LPKParsed;
650 LPKFilename:=SrcInfo.LPKFilename;
651 LPKError:=SrcInfo.LPKError;
652 License:=SrcInfo.License;
653 Installed:=SrcInfo.Installed;
654 InLazSrc:=SrcInfo.InLazSrc;
655 ID.AssignID(SrcInfo.ID);
656 Description:=SrcInfo.Description;
657 Base:=SrcInfo.Base;
658 Author:=SrcInfo.Author;
659 end else if Source is TLazPackageID then begin
660 SrcID:=TLazPackageID(Source);
661 ID.AssignID(SrcID);
662 end else
663 RaiseGDBException('');
664 end;
665
666 destructor TLPKInfo.Destroy;
667 begin
668 FreeAndNil(ID);
669 inherited Destroy;
670 end;
671
672 end.
673
674