JclUnitVersioning.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. {**************************************************************************************************}
  2. { }
  3. { Project JEDI Code Library (JCL) }
  4. { }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the }
  7. { License at http://www.mozilla.org/MPL/ }
  8. { }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights }
  11. { and limitations under the License. }
  12. { }
  13. { The Original Code is JclUnitVersioning.pas. }
  14. { }
  15. { The Initial Developer of the Original Code is Andreas Hausladen. }
  16. { Portions created by Andreas Hausladen are Copyright (C) Andreas Hausladen. All rights reserved. }
  17. { }
  18. { Contributor(s): }
  19. { Andreas Hausladen (ahuser) }
  20. { }
  21. {**************************************************************************************************}
  22. { }
  23. { A unit version information system. It collects information from prepared units by each module. }
  24. { It also works with units in DLLs. }
  25. { }
  26. {**************************************************************************************************}
  27. { }
  28. { Last modified: $Date:: $ }
  29. { Revision: $Rev:: $ }
  30. { Author: $Author:: $ }
  31. { }
  32. {**************************************************************************************************}
  33. unit JclUnitVersioning;
  34. {$I jcl.inc}
  35. interface
  36. uses
  37. {$IFDEF HAS_UNIT_LIBC}
  38. Libc,
  39. {$ENDIF HAS_UNIT_LIBC}
  40. {$IFDEF HAS_UNITSCOPE}
  41. {$IFDEF MSWINDOWS}
  42. Winapi.Windows,
  43. {$ENDIF MSWINDOWS}
  44. System.SysUtils, System.Contnrs;
  45. {$ELSE ~HAS_UNITSCOPE}
  46. {$IFDEF MSWINDOWS}
  47. Windows,
  48. {$ENDIF MSWINDOWS}
  49. SysUtils, Contnrs;
  50. {$ENDIF ~HAS_UNITSCOPE}
  51. type
  52. PUnitVersionInfo = ^TUnitVersionInfo;
  53. TUnitVersionInfo = record
  54. RCSfile: PChar; // $'RCSfile$
  55. Revision: PChar; // $'Revision$
  56. Date: PChar; // $'Date$ in UTC (GMT)
  57. LogPath: PChar; // logical file path
  58. Extra: PChar; // user defined string
  59. Data: Pointer; // user data
  60. end;
  61. TUnitVersion = class(TObject)
  62. private
  63. FInfo: PUnitVersionInfo;
  64. public
  65. constructor Create(AInfo: PUnitVersionInfo);
  66. function RCSfile: string;
  67. function Revision: string;
  68. function Date: string;
  69. function Extra: string;
  70. function LogPath: string;
  71. function Data: Pointer;
  72. function DateTime: TDateTime;
  73. function Summary: string;
  74. end;
  75. TUnitVersioningModule = class(TObject)
  76. private
  77. FInstance: THandle;
  78. FItems: TObjectList;
  79. function GetItems(Index: Integer): TUnitVersion;
  80. function GetCount: Integer;
  81. procedure Add(Info: PUnitVersionInfo);
  82. function IndexOfInfo(Info: PUnitVersionInfo): Integer;
  83. public
  84. constructor Create(AInstance: THandle);
  85. destructor Destroy; override;
  86. function IndexOf(const RCSfile: string; const LogPath: string = '*'): Integer;
  87. function FindUnit(const RCSfile: string; const LogPath: string = '*'): TUnitVersion;
  88. property Instance: THandle read FInstance;
  89. property Count: Integer read GetCount;
  90. property Items[Index: Integer]: TUnitVersion read GetItems; default;
  91. end;
  92. TCustomUnitVersioningProvider = class(TObject)
  93. public
  94. constructor Create; virtual;
  95. procedure LoadModuleUnitVersioningInfo(Instance: THandle); virtual;
  96. procedure ReleaseModuleUnitVersioningInfo(Instance: THandle); virtual;
  97. end;
  98. TUnitVersioningProviderClass = class of TCustomUnitVersioningProvider;
  99. TUnitVersioning = class(TObject)
  100. private
  101. FModules: TObjectList;
  102. FProviders: TObjectList;
  103. function GetItem(Index: Integer): TUnitVersion;
  104. function GetCount: Integer;
  105. function GetModuleCount: Integer;
  106. function GetModule(Index: Integer): TUnitVersioningModule;
  107. procedure UnregisterModule(Module: TUnitVersioningModule); overload;
  108. procedure ValidateModules;
  109. // These two methods must be virtual because they can be invoked by a DLL.
  110. // Static linking would mean that the DLL's TUnitVersioning methods handle
  111. // the call which leads to an access violation.
  112. procedure Add(Instance: THandle; Info: PUnitVersionInfo); virtual;
  113. procedure UnregisterModule(Instance: THandle); overload; virtual;
  114. public
  115. constructor Create;
  116. destructor Destroy; override;
  117. procedure RegisterProvider(AProviderClass: TUnitVersioningProviderClass);
  118. procedure LoadModuleUnitVersioningInfo(Instance: THandle);
  119. function IndexOf(const RCSfile: string; const LogPath: string = '*'): Integer;
  120. function FindUnit(const RCSfile: string; const LogPath: string = '*'): TUnitVersion;
  121. // units by modules
  122. property ModuleCount: Integer read GetModuleCount;
  123. property Modules[Index: Integer]: TUnitVersioningModule read GetModule;
  124. // all units
  125. property Count: Integer read GetCount;
  126. property Items[Index: Integer]: TUnitVersion read GetItem; default;
  127. end;
  128. procedure RegisterUnitVersion(Instance: THandle; const Info: TUnitVersionInfo);
  129. procedure UnregisterUnitVersion(Instance: THandle);
  130. function GetUnitVersioning: TUnitVersioning;
  131. procedure ExportUnitVersioningToFile(iFileName : string);
  132. const
  133. UnitVersioning: TUnitVersionInfo = (
  134. RCSfile: '$URL$';
  135. Revision: '$Revision$';
  136. Date: '$Date$';
  137. LogPath: 'JCL\source\common';
  138. Extra: '';
  139. Data: nil
  140. );
  141. implementation
  142. uses
  143. // make TObjectList functions inlined
  144. {$IFDEF HAS_UNITSCOPE}
  145. System.Types, // inlining of TObjectList.Remove
  146. System.Classes,
  147. {$ELSE ~HAS_UNITSCOPE}
  148. Classes,
  149. {$ENDIF ~HAS_UNITSCOPE}
  150. JclSysUtils, JclSynch;
  151. // Delphi 5 does not know this function //(usc) D6/7 Per does have StartsWith
  152. // a fast version of Pos(SubStr, S) = 1
  153. function StartsWith(const SubStr, S: string): Boolean;
  154. var
  155. I, Len: Integer;
  156. begin
  157. Result := False;
  158. Len := Length(SubStr);
  159. if Len <= Length(S) then
  160. begin
  161. for I := 1 to Len do
  162. if S[I] <> SubStr[I] then
  163. Exit;
  164. Result := True;
  165. end;
  166. end;
  167. function CompareFilenames(const Fn1, Fn2: string): Integer;
  168. begin
  169. {$IFDEF MSWINDOWS}
  170. Result := CompareText(Fn1, Fn2);
  171. {$ENDIF MSWINDOWS}
  172. {$IFDEF UNIX}
  173. Result := CompareStr(Fn1, Fn2);
  174. {$ENDIF UNIX}
  175. end;
  176. //=== { TUnitVersion } =======================================================
  177. constructor TUnitVersion.Create(AInfo: PUnitVersionInfo);
  178. begin
  179. inherited Create;
  180. FInfo := AInfo;
  181. end;
  182. function TUnitVersion.RCSfile: string;
  183. var
  184. I, P: Integer;
  185. begin
  186. Result := Trim(FInfo.RCSfile);
  187. // the + is to have CVS not touch the string
  188. if StartsWith('$' + 'RCSfile: ', Result) then // a CVS command
  189. begin
  190. Delete(Result, 1, 10);
  191. Delete(Result, Length(Result) - 1, 2);
  192. for I := Length(Result) downto 1 do
  193. if Result[I] = ',' then
  194. begin
  195. Delete(Result, I, MaxInt);
  196. Break;
  197. end;
  198. end;
  199. // the + is to have SVN not touch the string
  200. if StartsWith('$' + 'URL: ', Result) then // a SVN command
  201. begin
  202. Delete(Result, 1, 6);
  203. Delete(Result, Length(Result) - 1, 2);
  204. { TODO -oUSc : Is there any need for a function that returns the URL? }
  205. P := Pos('/', Result);
  206. while P > 0 do
  207. begin
  208. Delete(Result, 1, P);
  209. P := Pos('/', Result);
  210. end;
  211. end;
  212. end;
  213. function TUnitVersion.Revision: string;
  214. begin
  215. Result := Trim(FInfo.Revision);
  216. if StartsWith('$' + 'Revision: ', Result) then // a CVS command
  217. Result := Copy(Result, 12, Length(Result) - 11 - 2);
  218. end;
  219. function TUnitVersion.Date: string;
  220. begin
  221. Result := Trim(FInfo.Date);
  222. if StartsWith('$' + 'Date: ', Result) then // a CVS command
  223. begin
  224. Delete(Result, 1, 7);
  225. Delete(Result, Length(Result) - 1, 2);
  226. end;
  227. end;
  228. function TUnitVersion.Data: Pointer;
  229. begin
  230. Result := FInfo.Data;
  231. end;
  232. function TUnitVersion.Extra: string;
  233. begin
  234. Result := Trim(FInfo.Extra);
  235. end;
  236. function TUnitVersion.LogPath: string;
  237. begin
  238. Result := Trim(FInfo.LogPath);
  239. end;
  240. function TUnitVersion.DateTime: TDateTime;
  241. var
  242. Ps: Integer;
  243. S: string;
  244. Error: Integer;
  245. Year, Month, Day, Hour, Minute, Second: Word;
  246. TimeSep: Char;
  247. begin
  248. Result := 0;
  249. S := Date;
  250. // date: yyyy/mm/dd | yyyy-mm-dd | mm/dd/yyyy | mm-dd-yyyy | dd.mm.yyyy
  251. Ps := Pos('/', S);
  252. if Ps = 0 then
  253. Ps := Pos('-', S);
  254. if Ps <> 0 then
  255. begin
  256. if Ps = 5 then
  257. begin
  258. // yyyy/mm/dd | yyyy-mm-dd
  259. Val(Copy(S, 1, 4), Year, Error);
  260. Val(Copy(S, 6, 2), Month, Error);
  261. Val(Copy(S, 9, 2), Day, Error);
  262. end
  263. else
  264. begin
  265. // mm/dd/yyyy | mm-dd-yyyy
  266. Val(Copy(S, 1, 2), Month, Error);
  267. Val(Copy(S, 4, 2), Day, Error);
  268. Val(Copy(S, 7, 4), Year, Error);
  269. end;
  270. end
  271. else
  272. begin
  273. Ps := Pos('.', S);
  274. if Ps <> 0 then
  275. begin
  276. // dd.mm.yyyy
  277. Val(Copy(S, 1, 2), Day, Error);
  278. Val(Copy(S, 4, 2), Month, Error);
  279. Val(Copy(S, 7, 4), Year, Error);
  280. end
  281. else
  282. Exit;
  283. end;
  284. // time: hh:mm:ss | hh/mm/ss
  285. Ps := Pos(' ', S);
  286. S := Trim(Copy(S, Ps + 1, MaxInt));
  287. Ps := Pos(':', S);
  288. if Ps <> 0 then
  289. TimeSep := ':'
  290. else
  291. begin
  292. Ps := Pos('/', S);
  293. TimeSep := '/';
  294. end;
  295. Val(Copy(S, 1, Ps - 1), Hour, Error);
  296. Delete(S, 1, Ps);
  297. Ps := Pos(TimeSep, S);
  298. Val(Copy(S, 1, Ps - 1), Minute, Error);
  299. Delete(S, 1, Ps);
  300. Ps := Pos(TimeSep, S);
  301. if Ps = 0 then
  302. Ps := Length(S) + 1;
  303. Val(Copy(S, 1, Ps - 1), Second, Error);
  304. Result := EncodeDate(Year, Month, Day) + EncodeTime(Hour, Minute, Second, 0);
  305. end;
  306. function TUnitVersion.Summary: string;
  307. begin
  308. Result := LogPath + #9 + RCSFile + #9 + Revision + #9 + Date;
  309. if Extra <> '' then
  310. Result := Result + #9 + Extra;
  311. end;
  312. //=== { TUnitVersioningModule } ==============================================
  313. constructor TUnitVersioningModule.Create(AInstance: THandle);
  314. begin
  315. inherited Create;
  316. FInstance := AInstance;
  317. FItems := TObjectList.Create;
  318. end;
  319. destructor TUnitVersioningModule.Destroy;
  320. begin
  321. FItems.Free;
  322. inherited Destroy;
  323. end;
  324. function TUnitVersioningModule.GetCount: Integer;
  325. begin
  326. Result := FItems.Count;
  327. end;
  328. function TUnitVersioningModule.GetItems(Index: Integer): TUnitVersion;
  329. begin
  330. Result := TUnitVersion(FItems[Index]);
  331. end;
  332. procedure TUnitVersioningModule.Add(Info: PUnitVersionInfo);
  333. begin
  334. FItems.Add(TUnitVersion.Create(Info));
  335. end;
  336. function TUnitVersioningModule.IndexOfInfo(Info: PUnitVersionInfo): Integer;
  337. begin
  338. for Result := 0 to FItems.Count - 1 do
  339. if Items[Result].FInfo = Info then
  340. Exit;
  341. Result := -1;
  342. end;
  343. function TUnitVersioningModule.FindUnit(const RCSfile: string; const LogPath: string): TUnitVersion;
  344. var
  345. Index: Integer;
  346. begin
  347. Index := IndexOf(RCSfile, LogPath);
  348. if Index <> -1 then
  349. Result := Items[Index]
  350. else
  351. Result := nil;
  352. end;
  353. function TUnitVersioningModule.IndexOf(const RCSfile: string; const LogPath: string): Integer;
  354. var
  355. Item: TUnitVersion;
  356. begin
  357. for Result := 0 to FItems.Count - 1 do
  358. begin
  359. Item := Items[Result];
  360. if CompareFilenames(Item.RCSfile, RCSfile) = 0 then
  361. if LogPath = '*' then
  362. Exit
  363. else
  364. if CompareFilenames(LogPath, Trim(Item.LogPath)) = 0 then
  365. Exit;
  366. end;
  367. Result := -1;
  368. end;
  369. //=== { TCustomUnitVersioningProvider } ======================================
  370. constructor TCustomUnitVersioningProvider.Create;
  371. begin
  372. inherited Create;
  373. end;
  374. procedure TCustomUnitVersioningProvider.LoadModuleUnitVersioningInfo(Instance: THandle);
  375. begin
  376. //
  377. end;
  378. procedure TCustomUnitVersioningProvider.ReleaseModuleUnitVersioningInfo(Instance: THandle);
  379. begin
  380. //
  381. end;
  382. //=== { TUnitVersioning } ====================================================
  383. constructor TUnitVersioning.Create;
  384. begin
  385. inherited Create;
  386. FModules := TObjectList.Create;
  387. FProviders := TObjectList.Create;
  388. end;
  389. destructor TUnitVersioning.Destroy;
  390. begin
  391. FProviders.Free;
  392. FModules.Free;
  393. inherited Destroy;
  394. end;
  395. procedure TUnitVersioning.Add(Instance: THandle; Info: PUnitVersionInfo);
  396. var
  397. I: Integer;
  398. Module: TUnitVersioningModule;
  399. begin
  400. for I := 0 to FModules.Count - 1 do
  401. begin
  402. Module := Modules[I];
  403. if Module.Instance = Instance then
  404. begin
  405. if Module.IndexOfInfo(Info) = -1 then
  406. Module.Add(Info);
  407. Exit;
  408. end;
  409. end;
  410. // create a new module entry
  411. Module := TUnitVersioningModule.Create(Instance);
  412. FModules.Add(Module);
  413. Module.Add(Info);
  414. end;
  415. procedure TUnitVersioning.UnregisterModule(Instance: THandle);
  416. var
  417. I: Integer;
  418. begin
  419. for I := FModules.Count - 1 downto 0 do
  420. if Modules[I].Instance = Instance then
  421. begin
  422. FModules.Delete(I);
  423. Break;
  424. end;
  425. for I := 0 to FProviders.Count -1 do
  426. TCustomUnitVersioningProvider(FProviders[I]).ReleaseModuleUnitVersioningInfo(Instance);
  427. end;
  428. procedure TUnitVersioning.UnregisterModule(Module: TUnitVersioningModule);
  429. begin
  430. FModules.Remove(Module);
  431. end;
  432. function TUnitVersioning.GetCount: Integer;
  433. var
  434. I: Integer;
  435. begin
  436. Result := 0;
  437. ValidateModules;
  438. for I := 0 to FModules.Count - 1 do
  439. Inc(Result, Modules[I].Count);
  440. end;
  441. function TUnitVersioning.GetItem(Index: Integer): TUnitVersion;
  442. var
  443. Cnt, I: Integer;
  444. Module: TUnitVersioningModule;
  445. begin
  446. Result := nil;
  447. ValidateModules;
  448. Cnt := 0;
  449. for I := 0 to FModules.Count - 1 do
  450. begin
  451. Module := Modules[I];
  452. if Index < Cnt + Module.Count then
  453. begin
  454. Result := Module.Items[Index - Cnt];
  455. Break;
  456. end;
  457. Inc(Cnt, Module.Count);
  458. end;
  459. end;
  460. function TUnitVersioning.GetModuleCount: Integer;
  461. begin
  462. ValidateModules;
  463. Result := FModules.Count;
  464. end;
  465. function TUnitVersioning.GetModule(Index: Integer): TUnitVersioningModule;
  466. begin
  467. Result := TUnitVersioningModule(FModules[Index]);
  468. end;
  469. {$UNDEF FPCUNIX} // Temporary, will move to .inc's in time.
  470. {$IFDEF FPC}
  471. {$IFDEF UNIX}
  472. {$DEFIN FPCUNIX}
  473. {$ENDIF}
  474. {$ENDIF}
  475. procedure TUnitVersioning.ValidateModules;
  476. var
  477. I: Integer;
  478. {$IFNDEF FPCUNIX}
  479. Buffer: string;
  480. {$ENDIF ~FPCUNIX}
  481. Module: TUnitVersioningModule;
  482. begin
  483. {$IFNDEF FPCUNIX}
  484. SetLength(Buffer, 1024);
  485. {$ENDIF ~FPCUNIX}
  486. for I := FModules.Count - 1 downto 0 do
  487. begin
  488. Module := Modules[I];
  489. {$IFDEF FPCUNIX}
  490. if dlsym(Pointer(Module.Instance), '_init') = nil then
  491. {$ELSE ~FPCUNIX}
  492. if GetModuleFileName(Module.Instance, PChar(Buffer), 1024) = 0 then
  493. {$ENDIF ~FPCUNIX}
  494. // This module is no more in memory but has not unregistered itself so
  495. // unregister it here.
  496. UnregisterModule(Module);
  497. end;
  498. end;
  499. function TUnitVersioning.FindUnit(const RCSfile: string; const LogPath: string): TUnitVersion;
  500. var
  501. I: Integer;
  502. begin
  503. for I := 0 to FModules.Count - 1 do
  504. begin
  505. Result := Modules[I].FindUnit(RCSfile, LogPath);
  506. if Result <> nil then
  507. Exit;
  508. end;
  509. Result := nil;
  510. end;
  511. function TUnitVersioning.IndexOf(const RCSfile: string; const LogPath: string): Integer;
  512. var
  513. I, Cnt, Index: Integer;
  514. Module: TUnitVersioningModule;
  515. begin
  516. Result := -1;
  517. Cnt := 0;
  518. for I := 0 to FModules.Count - 1 do
  519. begin
  520. Module := Modules[I];
  521. Index := Module.IndexOf(RCSfile, LogPath);
  522. if Index <> -1 then
  523. begin
  524. Result := Cnt + Index;
  525. Break;
  526. end;
  527. Inc(Cnt, Module.Count);
  528. end;
  529. end;
  530. procedure TUnitVersioning.RegisterProvider(AProviderClass: TUnitVersioningProviderClass);
  531. var
  532. I, Idx: Integer;
  533. begin
  534. Idx := -1;
  535. for I := 0 to FProviders.Count - 1 do
  536. if TObject(FProviders[I]).ClassType = AProviderClass then
  537. begin
  538. Idx := I;
  539. Break;
  540. end;
  541. if Idx = -1 then
  542. FProviders.Add(AProviderClass.Create);
  543. end;
  544. procedure TUnitVersioning.LoadModuleUnitVersioningInfo(Instance: THandle);
  545. var
  546. I: Integer;
  547. begin
  548. for I := 0 to FProviders.Count - 1 do
  549. TCustomUnitVersioningProvider(FProviders[I]).LoadModuleUnitVersioningInfo(Instance);
  550. end;
  551. type
  552. PUnitVersioning = ^TUnitVersioning;
  553. var
  554. UnitVersioningOwner: Boolean = False;
  555. GlobalUnitVersioning: TUnitVersioning = nil;
  556. UnitVersioningNPA: PUnitVersioning = nil;
  557. UnitVersioningMutex: TJclMutex;
  558. UnitVersioningFinalized: Boolean = False;
  559. function GetUnitVersioning: TUnitVersioning;
  560. begin
  561. if UnitVersioningFinalized then
  562. begin
  563. Result := nil;
  564. Exit;
  565. end;
  566. if UnitVersioningMutex = nil then
  567. UnitVersioningMutex := TJclMutex.Create(nil, False, 'MutexNPA_UnitVersioning_' + IntToStr(GetCurrentProcessId));
  568. if GlobalUnitVersioning = nil then
  569. begin
  570. UnitVersioningMutex.WaitFor(INFINITE);
  571. try
  572. if UnitVersioningNPA = nil then
  573. SharedGetMem(UnitVersioningNPA, 'ShmNPA_UnitVersioning_' + IntToStr(GetCurrentProcessId), SizeOf(TUnitVersioning));
  574. if UnitVersioningNPA <> nil then
  575. begin
  576. GlobalUnitVersioning := UnitVersioningNPA^;
  577. if GlobalUnitVersioning = nil then
  578. begin
  579. GlobalUnitVersioning := TUnitVersioning.Create;
  580. UnitVersioningNPA^ := GlobalUnitVersioning;
  581. UnitVersioningOwner := True;
  582. end;
  583. end
  584. else
  585. begin
  586. GlobalUnitVersioning := TUnitVersioning.Create;
  587. UnitVersioningOwner := True;
  588. end;
  589. finally
  590. UnitVersioningMutex.Release;
  591. end;
  592. end
  593. else
  594. if UnitVersioningNPA <> nil then
  595. begin
  596. UnitVersioningMutex.WaitFor(INFINITE);
  597. try
  598. GlobalUnitVersioning := UnitVersioningNPA^; // update (maybe the owner has destroyed the instance)
  599. finally
  600. UnitVersioningMutex.Release;
  601. end;
  602. end;
  603. Result := GlobalUnitVersioning;
  604. end;
  605. procedure FinalizeUnitVersioning;
  606. begin
  607. UnitVersioningFinalized := True;
  608. try
  609. if UnitVersioningNPA <> nil then
  610. SharedCloseMem(UnitVersioningNPA);
  611. if (GlobalUnitVersioning <> nil) and UnitVersioningOwner then
  612. FreeAndNil(GlobalUnitVersioning)
  613. else
  614. GlobalUnitVersioning := nil;
  615. except
  616. // ignore - should never happen
  617. end;
  618. FreeAndNil(UnitVersioningMutex);
  619. end;
  620. procedure RegisterUnitVersion(Instance: THandle; const Info: TUnitVersionInfo);
  621. var
  622. UnitVersioning: TUnitVersioning;
  623. begin
  624. UnitVersioning := GetUnitVersioning;
  625. if Assigned(UnitVersioning) then
  626. UnitVersioning.Add(Instance, @Info);
  627. end;
  628. procedure UnregisterUnitVersion(Instance: THandle);
  629. var
  630. UnitVersioning: TUnitVersioning;
  631. begin
  632. UnitVersioning := GetUnitVersioning;
  633. if Assigned(UnitVersioning) then
  634. UnitVersioning.UnregisterModule(Instance);
  635. end;
  636. procedure ExportUnitVersioningToFile(iFileName : string);
  637. var
  638. I: Integer;
  639. sl: TStringList;
  640. begin
  641. sl := TStringList.Create;
  642. try
  643. for I := 0 to GetUnitVersioning.Count - 1 do
  644. sl.Add(GetUnitVersioning.Items[I].Summary);
  645. sl.Sort;
  646. sl.SaveToFile(iFileName);
  647. finally
  648. sl.Free;
  649. end;
  650. end;
  651. initialization
  652. {$IFDEF UNITVERSIONING}
  653. RegisterUnitVersion(HInstance, UnitVersioning);
  654. {$ENDIF UNITVERSIONING}
  655. finalization
  656. {$IFDEF UNITVERSIONING}
  657. UnregisterUnitVersion(HInstance);
  658. {$ENDIF UNITVERSIONING}
  659. FinalizeUnitVersioning;
  660. end.