mkfpdoc.pp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. unit mkfpdoc;
  2. {$mode objfpc}{$H+}
  3. {$WARN 5024 off : Parameter "$1" not used}
  4. interface
  5. uses
  6. Classes, SysUtils, dglobals, DOM, fpdocxmlopts, dwriter, pscanner, pparser, fpdocproj;
  7. const
  8. DefOSTarget = {$I %FPCTARGETOS%};
  9. DefCPUTarget = {$I %FPCTARGETCPU%};
  10. DefFPCVersion = {$I %FPCVERSION%};
  11. DefFPCDate = {$I %FPCDATE%};
  12. {$IFDEF FPC_BIG_ENDIAN}
  13. DefEndianNess = 'FPC_BIG_ENDIAN';
  14. {$ELSE}
  15. DefEndianNess = 'FPC_LITTLE_ENDIAN';
  16. {$ENDIF}
  17. Type
  18. { TFPDocCreator }
  19. TFPDocCreator = Class(TComponent)
  20. Private
  21. FBaseDescrDir: String;
  22. FBaseInputDir: String;
  23. FCurPackage : TFPDocPackage;
  24. FDefines: TStrings;
  25. FExamplesPath: String;
  26. FProcessedUnits : TStrings;
  27. FOnLog: TPasParserLogHandler;
  28. FPParserLogEvents: TPParserLogEvents;
  29. FProject : TFPDocProject;
  30. FProjectMacros: TStrings;
  31. FScannerLogEvents: TPScannerLogEvents;
  32. FVerbose: Boolean;
  33. function GetLogLevels: TFPDocLogLevels;
  34. function GetOptions: TEngineOptions;
  35. function GetPackages: TFPDocPackages;
  36. procedure SetBaseDescrDir(AValue: String);
  37. procedure SetBaseInputDir(AValue: String);
  38. procedure SetDefines(const aValue: TStrings);
  39. procedure SetExamplesPath(AValue: String);
  40. procedure SetProjectMacros(AValue: TStrings);
  41. Protected
  42. Function FixInputFile(Const AFileName : String) : String;
  43. Function FixDescrFile(Const AFileName : String) : String;
  44. Procedure DoBeforeEmitNote(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean); virtual;
  45. procedure HandleOnParseUnit(Sender: TObject; const AUnitName: String; out AInputFile, OSTarget, CPUTarget: String);
  46. procedure SetVerbose(AValue: Boolean); virtual;
  47. Procedure DoLog(Const Msg : String);
  48. procedure DoLog(Const Fmt : String; Args : Array of Const);
  49. Procedure DoLogSender(Sender : TObject; Const Msg : String);
  50. // Create documentation by specified Writer class
  51. procedure CreateOutput(APackage: TFPDocPackage; Engine: TFPDocEngine); virtual;
  52. Public
  53. Constructor Create(AOwner : TComponent); override;
  54. Destructor Destroy; override;
  55. Procedure CreateDocumentation(APackage : TFPDocPackage; ParseOnly : Boolean); virtual; //Writes out documentation in selected format
  56. Procedure CreateProjectFile(Const AFileName : string); //Writes out project file with the chosen options
  57. Procedure LoadProjectFile(Const AFileName: string);
  58. Property Project : TFPDocProject Read FProject;
  59. Property ScannerLogEvents : TPScannerLogEvents Read FScannerLogEvents Write FScannerLogEvents;
  60. Property ParserLogEvents : TPParserLogEvents Read FPParserLogEvents Write FPParserLogEvents;
  61. Property Verbose : Boolean Read FVerbose Write SetVerbose;
  62. Property OnLog : TPasParserLogHandler Read FOnLog Write FOnLog;
  63. // Easy access
  64. Property Options : TEngineOptions Read GetOptions;
  65. Property Packages : TFPDocPackages Read GetPackages;
  66. // When set, they will be prepended to non-absolute filenames.
  67. Property BaseInputDir : String Read FBaseInputDir Write SetBaseInputDir;
  68. Property BaseDescrDir : String Read FBaseDescrDir Write SetBaseDescrDir;
  69. Property ExamplesPath : String Read FExamplesPath Write SetExamplesPath;
  70. // Macros used when loading the project file
  71. Property ProjectMacros : TStrings Read FProjectMacros Write SetProjectMacros;
  72. Property Defines : TStrings Read FDefines Write SetDefines;
  73. end;
  74. implementation
  75. uses fpdocstrs;
  76. { TFPDocCreator }
  77. procedure TFPDocCreator.SetVerbose(AValue: Boolean);
  78. begin
  79. if FVerbose=AValue then Exit;
  80. FVerbose:=AValue;
  81. if FVerbose then
  82. begin
  83. ScannerLogEvents:=[sleFile];
  84. ParserLogEvents:=[];
  85. Options.InfoUsedFile:= true;
  86. Options.WarnDocumentationEmpty:= true;
  87. Options.WarnXCT:= true;
  88. end
  89. else
  90. begin
  91. ScannerLogEvents:=[];
  92. ParserLogEvents:=[];
  93. end;
  94. end;
  95. procedure TFPDocCreator.DoLog(const Msg: String);
  96. begin
  97. If Assigned(OnLog) then
  98. OnLog(Self,Msg);
  99. end;
  100. procedure TFPDocCreator.DoLog(const Fmt: String; Args: array of const);
  101. begin
  102. DoLog(Format(Fmt,Args));
  103. end;
  104. procedure TFPDocCreator.DoLogSender ( Sender: TObject; const Msg: String ) ;
  105. begin
  106. if Assigned(Sender) then
  107. DoLog(Format('%s - Sender: %s', [Msg, Sender.ClassName]))
  108. else
  109. DoLog(Msg);
  110. end;
  111. procedure TFPDocCreator.HandleOnParseUnit(Sender: TObject;
  112. const AUnitName: String; out AInputFile, OSTarget, CPUTarget: String);
  113. Var
  114. I : Integer;
  115. S,un,opts : String;
  116. begin
  117. AInputFile:='';
  118. OSTarget:='';
  119. CPUTarget:='';
  120. if Assigned(FCurPackage) then
  121. begin
  122. I:=0;
  123. While (AInputFIle='') and (I<FCurPackage.Inputs.Count) do
  124. begin
  125. S:=FCurPackage.Inputs[i];
  126. SplitInputFIleOption(S,UN,Opts);
  127. if CompareText(ChangeFileExt(ExtractFileName(Un),''),AUnitName)=0 then
  128. begin
  129. AInputFile:=FixInputFile(UN)+' '+Opts+' -d'+Options.EndianNess;
  130. OSTarget:=FProject.Options.OSTarget;
  131. CPUTarget:=FProject.Options.CPUTarget;
  132. FProcessedUnits.Add(UN);
  133. end;
  134. Inc(I);
  135. end;
  136. end;
  137. end;
  138. function TFPDocCreator.GetOptions: TEngineOptions;
  139. begin
  140. Result:=FProject.Options;
  141. end;
  142. function TFPDocCreator.GetPackages: TFPDocPackages;
  143. begin
  144. Result:=FProject.Packages;
  145. end;
  146. function TFPDocCreator.FixInputFile(const AFileName: String): String;
  147. begin
  148. Result:=AFileName;
  149. If Result='' then exit;
  150. if (ExtractFileDrive(Result)='') and (Result[1]<>PathDelim) then
  151. Result:=BaseInputDir+Result;
  152. end;
  153. function TFPDocCreator.FixDescrFile(const AFileName: String): String;
  154. begin
  155. Result:=AFileName;
  156. If Result='' then exit;
  157. if (ExtractFileDrive(Result)='') and (Result[1]<>PathDelim) then
  158. Result:=BaseDescrDir+Result;
  159. end;
  160. procedure TFPDocCreator.SetBaseDescrDir(AValue: String);
  161. begin
  162. if FBaseDescrDir=AValue then Exit;
  163. FBaseDescrDir:=AValue;
  164. If FBaseDescrDir<>'' then
  165. FBaseDescrDir:=IncludeTrailingPathDelimiter(FBaseDescrDir);
  166. end;
  167. procedure TFPDocCreator.SetBaseInputDir(AValue: String);
  168. begin
  169. if FBaseInputDir=AValue then Exit;
  170. FBaseInputDir:=AValue;
  171. If FBaseInputDir<>'' then
  172. FBaseInputDir:=IncludeTrailingPathDelimiter(FBaseInputDir);
  173. end;
  174. procedure TFPDocCreator.SetDefines(const aValue: TStrings);
  175. begin
  176. if FDefines=aValue then Exit;
  177. FDefines.Assign(aValue);
  178. end;
  179. procedure TFPDocCreator.SetExamplesPath(AValue: String);
  180. begin
  181. if FExamplesPath=AValue then Exit;
  182. FExamplesPath:=AValue;
  183. If FExamplesPath<>'' then
  184. FExamplesPath:=IncludeTrailingPathDelimiter(FExamplesPath);
  185. end;
  186. procedure TFPDocCreator.SetProjectMacros(AValue: TStrings);
  187. begin
  188. if FProjectMacros=AValue then Exit;
  189. FProjectMacros.Assign(AValue);
  190. end;
  191. procedure TFPDocCreator.DoBeforeEmitNote(Sender: TObject; Note: TDomElement;
  192. var EmitNote: Boolean);
  193. begin
  194. EmitNote:=True;
  195. end;
  196. constructor TFPDocCreator.Create(AOwner: TComponent);
  197. begin
  198. inherited Create(AOwner);
  199. FProject:=TFPDocProject.Create(Self);
  200. FProject.Options.StopOnParseError:=False;
  201. FProject.Options.CPUTarget:=DefCPUTarget;
  202. FProject.Options.OSTarget:=DefOSTarget;
  203. FProject.Options.EndianNess:=DefEndianNess;
  204. FProcessedUnits:=TStringList.Create;
  205. FProjectMacros:=TStringList.Create;
  206. FDefines:=TStringList.Create;
  207. end;
  208. destructor TFPDocCreator.Destroy;
  209. begin
  210. FreeAndNil(FDefines);
  211. FreeAndNil(FProcessedUnits);
  212. FreeAndNil(FProject);
  213. FreeAndNil(FProjectMacros);
  214. inherited Destroy;
  215. end;
  216. procedure TFPDocCreator.CreateOutput(APackage: TFPDocPackage;Engine : TFPDocEngine);
  217. Var
  218. WriterClass : TFPDocWriterClass;
  219. Writer : TFPDocWriter;
  220. I : Integer;
  221. Cmd,Arg : String;
  222. begin
  223. // Now is used the specified writer
  224. WriterClass:=GetWriterClass(Options.Backend);
  225. // ALL CONTENT CREATED HERE
  226. Writer:=WriterClass.Create(Engine.Package,Engine);
  227. With Writer do
  228. Try
  229. If FVerbose then
  230. DoLog('Writing documentation');
  231. OnLog:=Self.OnLog;
  232. BeforeEmitNote:[email protected];
  233. EmitNotes:=Options.EmitNotes;
  234. If Options.BackendOptions.Count>0 then
  235. for I:=0 to ((Options.BackendOptions.Count-1) div 2) do
  236. begin
  237. Cmd:=Options.BackendOptions[I*2];
  238. Arg:=Options.BackendOptions[I*2+1];
  239. If not InterPretOption(Cmd,Arg) then
  240. DoLog(SCmdLineInvalidOption,[Cmd+'='+Arg]);
  241. end;
  242. // Create documentation by writer
  243. WriteDocumentation();
  244. Finally
  245. Free;
  246. end;
  247. // Output content files
  248. if FVerbose then
  249. DoLog('Content file : '+APackage.ContentFile);
  250. if Length(APackage.ContentFile) > 0 then
  251. Engine.WriteContentFile(APackage.ContentFile);
  252. end;
  253. function TFPDocCreator.GetLogLevels: TFPDocLogLevels;
  254. Procedure DoOpt(doSet : Boolean; aLevel: TFPDocLogLevel);
  255. begin
  256. if DoSet then
  257. Result:=Result+[aLevel];
  258. end;
  259. begin
  260. Result:=[];
  261. DoOpt(Options.WarnNoNode,dleWarnNoNode);
  262. DoOpt(Options.InfoUsedFile,dleWarnUsedFile);
  263. DoOpt(Options.WarnDocumentationEmpty,dleDocumentationEmpty);
  264. DoOpt(Options.WarnXCT,dleXCT);
  265. end;
  266. procedure TFPDocCreator.CreateDocumentation(APackage: TFPDocPackage;
  267. ParseOnly: Boolean);
  268. var
  269. i,j: Integer;
  270. Engine : TFPDocEngine;
  271. Cmd,Arg : String;
  272. WriterClass: TFPDocWriterClass;
  273. eMsg: String;
  274. begin
  275. Cmd:='';
  276. FCurPackage:=APackage;
  277. Engine:=TFPDocEngine.Create;
  278. try
  279. Engine.OnLog:= @DoLogSender;
  280. Engine.ExamplesPath:=Self.ExamplesPath;
  281. // get documentation Writer html, latex, and other
  282. WriterClass:=GetWriterClass(Options.Backend);
  283. For J:=0 to Apackage.Imports.Count-1 do
  284. begin
  285. Arg:=Apackage.Imports[j];
  286. // conversion import FilePaths
  287. WriterClass.SplitImport(Arg,Cmd);
  288. // create tree of imported objects
  289. Engine.ReadContentFile(Arg, Cmd);
  290. end;
  291. for i := 0 to APackage.Descriptions.Count - 1 do
  292. Engine.AddDocFile(FixDescrFile(APackage.Descriptions[i]),Options.donttrim);
  293. // set engine options
  294. Engine.SetPackageName(APackage.Name);
  295. Engine.Output:=APackage.Output;
  296. Engine.OnLog:=Self.OnLog;
  297. Engine.ScannerLogEvents:=Self.ScannerLogEvents;
  298. Engine.ParserLogEvents:=Self.ParserLogEvents;
  299. Engine.HideProtected:=Options.HideProtected;
  300. Engine.HidePrivate:=Not Options.ShowPrivate;
  301. Engine.OnParseUnit:=@HandleOnParseUnit;
  302. Engine.DocLogLevels:=GetLogLevels;
  303. Engine.FalbackSeeAlsoLinks:= Options.FallBackSeeAlsoLinks;
  304. if Length(Options.Language) > 0 then
  305. TranslateDocStrings(Options.Language);
  306. // scan the input source files
  307. for i := 0 to APackage.Inputs.Count - 1 do
  308. try
  309. try
  310. eMsg:='';
  311. // get options from input packages
  312. SplitInputFileOption(APackage.Inputs[i],Cmd,Arg);
  313. arg:=Arg+' -d'+Options.EndianNess;
  314. // make absolute filepath
  315. Cmd:=FixInputFile(Cmd);
  316. if FProcessedUnits.IndexOf(Cmd)=-1 then
  317. begin
  318. FProcessedUnits.Add(Cmd);
  319. // Parse sources for OS Target
  320. //WriteLn(Format('Parsing unit: %s', [ExtractFilenameOnly(Cmd)]));
  321. ParseSource(Engine,Cmd+' '+Arg, Options.OSTarget, Options.CPUTarget,[poUseStreams]); // poSkipDefaultDefs
  322. end;
  323. //else WriteLn(Format('Processed unit: %s', [ExtractFilenameOnly(Cmd)]));
  324. except
  325. on E: EParserError do
  326. begin
  327. eMsg:= Format('Parser error: %s (%d,%d): %s',[E.Filename, E.Row, E.Column, E.Message]);
  328. If Options.StopOnParseError then Raise;
  329. end;
  330. on E: EFileNotFoundError do
  331. begin
  332. eMsg:= Format('Error: file not found - %s', [E.Message]);
  333. If Options.StopOnParseError then Raise;
  334. end;
  335. on E: Exception do
  336. begin
  337. eMsg:= Format('Error: %s', [E.Message]);
  338. If Options.StopOnParseError then Raise;
  339. end;
  340. end; // try except
  341. finally
  342. if eMsg <> '' then
  343. begin
  344. DoLog(eMsg);
  345. If not Options.StopOnParseError then
  346. DoLog('Ignoring error, continuing with next unit (if any).');
  347. end;
  348. end; // try finally
  349. if Not ParseOnly then
  350. begin
  351. Engine.StartDocumenting;
  352. // Create documentation
  353. CreateOutput(APackage,Engine);
  354. end;
  355. finally
  356. FreeAndNil(Engine);
  357. FCurPackage:=Nil;
  358. end;
  359. end;
  360. procedure TFPDocCreator.CreateProjectFile(const AFileName: string);
  361. begin
  362. With TXMLFPDocOptions.Create(Self) do
  363. try
  364. SaveOptionsToFile(FProject,AFileName);
  365. finally
  366. Free;
  367. end;
  368. end;
  369. procedure TFPDocCreator.LoadProjectFile(const AFileName: string);
  370. begin
  371. With TXMLFPDocOptions.Create(self) do
  372. try
  373. SetDefines(Self.Defines);
  374. if (ProjectMacros.Count>0) then
  375. LoadOptionsFromFile(FProject,AFileName,ProjectMacros)
  376. else
  377. LoadOptionsFromFile(FProject,AFileName,Nil);
  378. finally
  379. Free;
  380. end;
  381. end;
  382. end.