dpkinfo.pp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. {
  2. This file is part of the Free Component Library
  3. Copyright (c) 2024 by Michael Van Canneyt ([email protected])
  4. Unit to parse and keep info about a package file.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit dpkinfo;
  13. {$ENDIF}
  14. {$mode ObjFPC}{$H+}
  15. interface
  16. uses
  17. {$IFDEF FPC_DOTTEDUNITS}
  18. System.Classes, System.SysUtils, Pascal.Tree, Pascal.Parser, Xml.Dom, Xml.Writer;
  19. {$ELSE}
  20. Classes, SysUtils, pastree, pparser, dom, XMLWrite;
  21. {$ENDIF}
  22. Type
  23. { TPackageContainer }
  24. TInfoKind = (ikUnknown,ikRequires,ikFiles,ikPaths);
  25. TPackageContainer = class(TPasTreeContainer)
  26. Public
  27. function FindElement(const AName: String): TPasElement; override;
  28. function CreateElement(AClass: TPTreeElement; const AName: String;
  29. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  30. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;overload; override;
  31. end;
  32. { TPackageInfo }
  33. TPackageInfo = class(TComponent)
  34. private
  35. FDefines: TStrings;
  36. FKnownPackages: TStrings;
  37. FOutput: TStrings;
  38. FOutputFile: String;
  39. FUseAbsolute: Boolean;
  40. FPackageDir : String;
  41. class function IsAbsoluteWindowsFile(aFile: String): Boolean;
  42. procedure WriteFiles(Pack: TPasDynamicPackage);
  43. procedure WritePaths(Pack: TPasDynamicPackage);
  44. procedure WriteRequires(Pack: TPasDynamicPackage);
  45. Public
  46. Constructor Create(aOwner : TComponent); override;
  47. Destructor Destroy; override;
  48. Procedure ShowInfo(const aInputFile: String; aKind : TInfoKind);
  49. Property KnownPackages : TStrings Read FKnownPackages;
  50. Property Output : TStrings Read FOutput;
  51. Property Defines : TStrings Read FDefines;
  52. Property UseAbsolute : Boolean Read FUseAbsolute Write FUseAbsolute;
  53. end;
  54. { TSimpleParser }
  55. TSimpleParser = Class
  56. function ParseSource(AEngine: TPasTreeContainer;
  57. const FPCCommandLine : Array of String;
  58. Defines : TStrings): TPasModule;
  59. private
  60. procedure DoIt(Sender: TObject; const aFileName: String; aOptions: TStrings);
  61. end;
  62. implementation
  63. {$IFDEF FPC_DOTTEDUNITS}
  64. uses Pascal.Scanner;
  65. {$ELSE}
  66. uses pscanner;
  67. {$ENDIF}
  68. function TSimpleParser.ParseSource(AEngine: TPasTreeContainer;
  69. const FPCCommandLine : Array of String;
  70. Defines : TStrings): TPasModule;
  71. var
  72. FileResolver: TBaseFileResolver;
  73. Parser: TPasParser;
  74. Filename: String;
  75. Scanner: TPascalScanner;
  76. procedure ProcessCmdLinePart(S : String);
  77. var
  78. l,Len: Integer;
  79. begin
  80. if (S='') then
  81. exit;
  82. Len:=Length(S);
  83. if (s[1] = '-') and (len>1) then
  84. begin
  85. case s[2] of
  86. 'd': // -d define
  87. Scanner.AddDefine(UpperCase(Copy(s, 3, Len)));
  88. 'u': // -u undefine
  89. Scanner.RemoveDefine(UpperCase(Copy(s, 3, Len)));
  90. 'F': // -F
  91. if (len>2) and (s[3] = 'i') then // -Fi include path
  92. FileResolver.AddIncludePath(Copy(s, 4, Len));
  93. 'I': // -I include path
  94. FileResolver.AddIncludePath(Copy(s, 3, Len));
  95. 'S': // -S mode
  96. if (len>2) then
  97. begin
  98. l:=3;
  99. While L<=Len do
  100. begin
  101. case S[l] of
  102. 'c' : Scanner.Options:=Scanner.Options+[po_cassignments];
  103. 'd' : Scanner.SetCompilerMode('DELPHI');
  104. '2' : Scanner.SetCompilerMode('OBJFPC');
  105. 'h' : ; // do nothing
  106. end;
  107. inc(l);
  108. end;
  109. end;
  110. 'M' :
  111. begin
  112. delete(S,1,2);
  113. Scanner.SetCompilerMode(S);
  114. end;
  115. end;
  116. end else
  117. if Filename <> '' then
  118. raise ENotSupportedException.Create(SErrMultipleSourceFiles)
  119. else
  120. Filename := s;
  121. end;
  122. var
  123. S: String;
  124. begin
  125. if DefaultFileResolverClass=Nil then
  126. raise ENotImplemented.Create(SErrFileSystemNotSupported);
  127. Result := nil;
  128. FileResolver := nil;
  129. Scanner := nil;
  130. Parser := nil;
  131. try
  132. FileResolver := DefaultFileResolverClass.Create;
  133. {$ifdef HasStreams}
  134. if FileResolver is TFileResolver then
  135. TFileResolver(FileResolver).UseStreams:=poUseStreams in Options;
  136. {$endif}
  137. Scanner := TPascalScanner.Create(FileResolver);
  138. Scanner.LogEvents:=AEngine.ScannerLogEvents;
  139. Scanner.OnLog:=AEngine.OnLog;
  140. Scanner.RegisterResourceHandler(['res'],@DoIt);
  141. For S in Defines do
  142. Scanner.AddDefine(S);
  143. Parser := TPasParser.Create(Scanner, FileResolver, AEngine);
  144. Parser.ImplicitUses.Clear;
  145. Filename := '';
  146. Parser.LogEvents:=AEngine.ParserLogEvents;
  147. Parser.OnLog:=AEngine.OnLog;
  148. For S in FPCCommandLine do
  149. ProcessCmdLinePart(S);
  150. if Filename = '' then
  151. raise Exception.Create(SErrNoSourceGiven);
  152. {$IFDEF HASFS}
  153. FileResolver.AddIncludePath(ExtractFilePath(FileName));
  154. {$ENDIF}
  155. Scanner.OpenFile(Filename);
  156. Parser.ParseMain(Result);
  157. finally
  158. Parser.Free;
  159. Scanner.Free;
  160. FileResolver.Free;
  161. end;
  162. end;
  163. procedure TSimpleParser.DoIt(Sender: TObject; const aFileName: String; aOptions: TStrings);
  164. begin
  165. // Do nothing
  166. end;
  167. { TPackageInfo }
  168. constructor TPackageInfo.Create(aOwner: TComponent);
  169. begin
  170. inherited Create(aOwner);
  171. FKnownPackages:=TStringList.Create;
  172. Foutput:=TStringList.Create;
  173. FDefines:=TStringList.Create;
  174. end;
  175. destructor TPackageInfo.Destroy;
  176. begin
  177. FreeAndNil(FKnownPackages);
  178. FreeAndNil(FOutput);
  179. FreeAndNil(FDefines);
  180. inherited Destroy;
  181. end;
  182. procedure TPackageInfo.WriteRequires(Pack : TPasDynamicPackage);
  183. var
  184. I : Integer;
  185. aPack : TPasRequiredPackage;
  186. begin
  187. For I:=0 to Pack.PackageSection.Requires.Count-1 do
  188. begin
  189. aPack:=TPasRequiredPackage(Pack.PackageSection.Requires[i]);
  190. if FKnownPackages.IndexOf(aPack.Name)=-1 then
  191. FOutput.Add(aPack.Name);
  192. end;
  193. end;
  194. class function TPackageInfo.IsAbsoluteWindowsFile(aFile: String): Boolean;
  195. begin
  196. Result:=(aFile<>'') and (aFile[2]=':') and (aFile[3]='\');
  197. end;
  198. procedure TPackageInfo.WriteFiles(Pack : TPasDynamicPackage);
  199. var
  200. aUsed : TPasUsesUnit;
  201. aName,aFileName : String;
  202. isAbsolute : Boolean;
  203. begin
  204. For aUsed in Pack.PackageSection.UsesClause do
  205. begin
  206. aName:=aUsed.Name;
  207. if (aName='') then
  208. continue;
  209. if assigned(aUsed.InFileName) then
  210. begin
  211. aFileName:=aUsed.InFilename.Value;
  212. aFileName:=StringReplace(aFileName,'''','',[rfReplaceAll]);
  213. if IsAbsoluteWindowsFile(aFileName) then
  214. isAbsolute:=True
  215. else
  216. begin
  217. aFileName:=StringReplace(aFilename,'\','/',[rfReplaceAll]);
  218. isAbsolute:=aFileName[1]='/';
  219. end
  220. end
  221. else
  222. begin
  223. aFileName:=aName+'.pas'; // Should not happen
  224. isAbsolute:=False;
  225. end;
  226. if (not IsAbsolute) and UseAbsolute then
  227. aFileName:=FPackageDir+aFileName;
  228. FOutput.Add(aFileName);
  229. end;
  230. end;
  231. procedure TPackageInfo.WritePaths(Pack : TPasDynamicPackage);
  232. var
  233. aUsed : TPasUsesUnit;
  234. aName,aFileName : String;
  235. isAbsolute : Boolean;
  236. Paths : TStrings;
  237. begin
  238. Paths:=TStringList.Create;
  239. For aUsed in Pack.PackageSection.UsesClause do
  240. begin
  241. aName:=aUsed.Name;
  242. if (aName='') then
  243. continue;
  244. if assigned(aUsed.InFileName) then
  245. begin
  246. aFileName:=aUsed.InFilename.Value;
  247. aFileName:=StringReplace(aFileName,'''','',[rfReplaceAll]);
  248. if IsAbsoluteWindowsFile(aFileName) then
  249. isAbsolute:=True
  250. else
  251. begin
  252. aFileName:=ExtractFilePath(StringReplace(aFilename,'\','/',[rfReplaceAll]));
  253. isAbsolute:=(aFileName<>'') and (aFileName[1]='/');
  254. end
  255. end
  256. else
  257. begin
  258. aFileName:=''; // Should not happen
  259. isAbsolute:=False;
  260. end;
  261. if (not IsAbsolute) and UseAbsolute then
  262. aFileName:=FPackageDir+aFileName;
  263. if (aFileName<>'') and (Paths.IndexOf(aFileName)=-1) then
  264. begin
  265. FOutput.Add(aFileName);
  266. Paths.Add(aFileName);
  267. end;
  268. end;
  269. end;
  270. procedure TPackageInfo.ShowInfo(const aInputFile: String; aKind: TInfoKind);
  271. Var
  272. El : TPasElement;
  273. Pack : TPasDynamicPackage absolute El;
  274. C : TPackageContainer;
  275. Parser : TSimpleParser;
  276. begin
  277. Foutput.Clear;
  278. FPackageDir:=ExtractFilePath(ExpandFileName(aInputFile));
  279. Parser:=nil;
  280. C:=TPackageContainer.Create;
  281. try
  282. Parser:=TSimpleParser.Create;
  283. El:=Parser.ParseSource(C,['-Sd',aInputFile],Defines);
  284. if not (El is TPasDynamicPackage) then
  285. Raise EPasTree.CreateFmt('%s is not a package source file. Got a %s instead.',[aInputFile,Pack.ClassName]);
  286. Case aKind of
  287. ikRequires : WriteRequires(Pack);
  288. ikPaths : WritePaths(Pack);
  289. ikFiles : WriteFiles(Pack);
  290. end;
  291. finally
  292. Parser.Free;
  293. El.Free;
  294. C.Free;
  295. end;
  296. end;
  297. { TPackageContainer }
  298. function TPackageContainer.FindElement(const AName: String): TPasElement;
  299. begin
  300. Result:=Nil;
  301. end;
  302. function TPackageContainer.CreateElement(AClass: TPTreeElement; const AName: String; AParent: TPasElement;
  303. AVisibility: TPasMemberVisibility; const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  304. begin
  305. Result:=aClass.Create(aName,aParent);
  306. Result.Visibility:=AVisibility;
  307. // ASourceFilename, ASourceLinenumber ?
  308. end;
  309. end.