makeskel.pp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. 2005-2012 by
  6. various FPC contributors
  7. * Skeleton XML description file generator.
  8. This generator scans Pascal source code for identifiers and emits XML files
  9. suitable for further processing with the fpdoc documentation system:
  10. users can edit the XML file and add (help) description.
  11. See the file COPYING, included in this distribution,
  12. for details about the copyright.
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. }
  17. {%RunCommand $MakeExe($(EdFile)) --package=fpvectorial --input=/home/felipe/Programas/fpctrunk/packages/fpvectorial/src/fpvectorial.pas}
  18. program MakeSkel;
  19. {$mode objfpc}
  20. {$h+}
  21. uses
  22. SysUtils, Classes, Gettext,
  23. dGlobals, PasTree, PParser,PScanner;
  24. resourcestring
  25. STitle = 'MakeSkel - FPDoc skeleton XML description file generator';
  26. SVersion = 'Version %s [%s]';
  27. SCmdLineHelp = 'See documentation for usage.';
  28. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  29. SNoPackageNameProvided = 'Please specify a package name with --package=<name>';
  30. SOutputMustNotBeDescr = 'Output file must be different from description filenames.';
  31. SCreatingNewNode = 'Creating documentation for new node : %s';
  32. SNodeNotReferenced = 'Documentation node "%s" no longer used';
  33. SDone = 'Done.';
  34. type
  35. TCmdLineAction = (actionHelp, actionConvert);
  36. TNodePair = Class(TObject)
  37. Private
  38. FEl : TPasElement;
  39. FNode : TDocNode;
  40. Public
  41. Constructor Create(AnElement : TPasElement; ADocNode : TDocNode);
  42. Property Element : TPasElement Read FEl;
  43. Property DocNode : TDocNode Read FNode;
  44. end;
  45. TSkelEngine = class(TFPDocEngine)
  46. Private
  47. FEmittedList,
  48. FNodeList,
  49. FModules : TStringList;
  50. Procedure DoWriteUnReferencedNodes(N : TDocNode; NodePath : String);
  51. public
  52. Destructor Destroy; override;
  53. Function MustWriteElement(El : TPasElement; Full : Boolean) : Boolean;
  54. Function WriteElement(Var F : Text; El : TPasElement; ADocNode : TDocNode) : Boolean;
  55. function FindModule(const AName: String): TPasModule; override;
  56. function CreateElement(AClass: TPTreeElement; const AName: String;
  57. AParent: TPasElement; AVisibility :TPasMemberVisibility;
  58. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
  59. procedure WriteUnReferencedNodes;
  60. Procedure WriteNodes(Var F : Text; AModule : TPasModule; List : TStrings);
  61. Procedure DocumentFile(Var F : Text; Const AFileName,ATarget,ACPU : String);
  62. Property NodeList : TStringList Read FNodeList;
  63. Property EmittedList : TStringList Read FEmittedList;
  64. end;
  65. const
  66. CmdLineAction: TCmdLineAction = actionConvert;
  67. OSTarget: String = {$I %FPCTARGETOS%};
  68. CPUTarget: String = {$I %FPCTARGETCPU%};
  69. FPCVersion: String = {$I %FPCVERSION%};
  70. FPCDate: String = {$I %FPCDATE%};
  71. var
  72. WriteDeclaration,
  73. UpdateMode,
  74. SortNodes,
  75. DisableOverride,
  76. DisableErrors,
  77. DisableSeealso,
  78. DisableArguments,
  79. DisableProtected,
  80. DisablePrivate,
  81. DisableFunctionResults: Boolean;
  82. EmitClassSeparator: Boolean;
  83. Constructor TNodePair.Create(AnElement : TPasElement; ADocNode : TDocNode);
  84. begin
  85. Fel:=Anelement;
  86. FNode:=ADocNode;
  87. end;
  88. function TSkelEngine.FindModule(const AName: String): TPasModule;
  89. Var
  90. I : Integer;
  91. begin
  92. Result:=Inherited FindModule(AName);
  93. If (Result=Nil) then
  94. begin // Create dummy list and search in that.
  95. If (FModules=Nil) then
  96. begin
  97. FModules:=TStringList.Create;
  98. FModules.Sorted:=True;
  99. end;
  100. I:=FModules.IndexOf(AName);
  101. IF (I=-1) then
  102. begin
  103. Result:=TPasModule.Create(AName,Nil);
  104. FModules.AddObject(AName,Result);
  105. end
  106. else
  107. Result:=FModules.Objects[i] as TPasModule;
  108. end;
  109. end;
  110. Destructor TSkelEngine.Destroy;
  111. Var
  112. I : Integer;
  113. begin
  114. If Assigned(FModules) then
  115. begin
  116. For I:=0 to FModules.Count-1 do
  117. FModules.Objects[i].Free;
  118. FreeAndNil(FModules);
  119. end;
  120. end;
  121. Function TSkelEngine.MustWriteElement(El : TPasElement; Full : Boolean) : Boolean;
  122. Var
  123. ParentVisible:Boolean;
  124. PT,PP : TPasElement;
  125. begin
  126. ParentVisible:=True;
  127. If (El is TPasArgument) or (El is TPasResultElement) then
  128. begin
  129. PT:=El.Parent;
  130. // Skip ProcedureType or PasFunctionType
  131. If (PT<>Nil) then
  132. begin
  133. if (PT is TPasProcedureType) or (PT is TPasFunctionType) then
  134. PT:=PT.Parent;
  135. If (PT<>Nil) and ((PT is TPasProcedure) or (PT is TPasProcedure)) then
  136. PP:=PT.Parent
  137. else
  138. PP:=Nil;
  139. If (PP<>Nil) and (PP is TPasClassType) then
  140. begin
  141. ParentVisible:=((not DisablePrivate or (PT.Visibility<>visPrivate)) and
  142. (not DisableProtected or (PT.Visibility<>visProtected)));
  143. end;
  144. end;
  145. end;
  146. Result:=Assigned(El.Parent) and (Length(El.Name) > 0) and
  147. (ParentVisible and (not DisableArguments or (El.ClassType <> TPasArgument))) and
  148. (ParentVisible and (not DisableFunctionResults or (El.ClassType <> TPasResultElement))) and
  149. (not DisablePrivate or (el.Visibility<>visPrivate)) and
  150. (not DisableProtected or (el.Visibility<>visProtected));
  151. If Result and Full then
  152. begin
  153. Result:=(Not Assigned(FEmittedList) or (FEmittedList.IndexOf(El.FullName)=-1));
  154. If DisableOverride and (El is TPasProcedure) then
  155. Result:=Not TPasProcedure(El).IsOverride;
  156. end;
  157. end;
  158. function TSkelEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  159. AParent: TPasElement; AVisibility : TPasMemberVisibility;
  160. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  161. Var
  162. DN : TDocNode;
  163. begin
  164. Result := AClass.Create(AName, AParent);
  165. Result.Visibility:=AVisibility;
  166. // Let function/procedure arguments and function results
  167. // inherit visibility from their parents if visDefault visibility is
  168. // specified.
  169. // This allows easier text searches on visibility in the resulting XML
  170. if (AVisibility=visDefault) and
  171. ((Result is TPasArgument) or (Result is TPasResultElement)) then
  172. Result.Visibility:=AParent.Visibility;
  173. if AClass.InheritsFrom(TPasModule) then
  174. CurModule := TPasModule(Result);
  175. // Track this element
  176. If UpdateMode then
  177. begin
  178. DN:=FindDocNode(Result);
  179. If Assigned(DN) then
  180. DN.IncRefCount;
  181. end
  182. else
  183. DN:=Nil;
  184. // See if we need to write documentation for it
  185. If MustWriteElement(Result,False) then
  186. FNodeList.AddObject(Result.PathName,TNodePair.Create(Result,DN));
  187. end;
  188. Function TSkelEngine.WriteElement(Var F : Text;El : TPasElement; ADocNode : TDocNode) : Boolean;
  189. Function WriteOnlyShort(APasElement : TPasElement) : Boolean;
  190. begin
  191. Result:=(APasElement.ClassType=TPasArgument) or
  192. (APasElement.ClassType=TPasResultElement) or
  193. (APasElement.ClassType=TPasEnumValue);
  194. end;
  195. Function IsTypeVarConst(APasElement : TPasElement) : Boolean;
  196. begin
  197. With APasElement do
  198. Result:=(InheritsFrom(TPasType) and not InheritsFrom(TPasClassType)) or
  199. (InheritsFrom(TPasResString)) or
  200. (InheritsFrom(TPasVariable));
  201. end;
  202. Function NeedDeclaration(El : TPasElement) : boolean;
  203. begin
  204. Result:=IsTypeVarConst(El)
  205. or WriteOnlyShort(El)
  206. or EL.InheritsFrom(TPasProcedure)
  207. end;
  208. begin
  209. // Check again, this time with full declaration.
  210. Result:=MustWriteElement(El,True);
  211. If Result and UpdateMode then
  212. Result:=(ADocNode=Nil);
  213. If Not Result Then
  214. Exit;
  215. If UpdateMode then
  216. Writeln(stderr,Format(ScreatingNewNode,[el.PathName]));
  217. FEmittedList.Add(El.FullName); // So we don't emit again.
  218. WriteLn(f);
  219. if EmitClassSeparator and (El.ClassType = TPasClassType) then
  220. begin
  221. WriteLn(f, '<!--');
  222. WriteLn(f, ' ********************************************************************');
  223. WriteLn(f, ' ', El.PathName);
  224. WriteLn(f, ' ********************************************************************');
  225. WriteLn(f, '-->');
  226. WriteLn(f);
  227. end;
  228. If Not (WriteDeclaration and NeedDeclaration(El)) then
  229. Writeln(F,'<!-- ', El.ElementTypeName,' Visibility: ',VisibilityNames[El.Visibility], ' -->')
  230. else
  231. begin
  232. Writeln(F,'<!-- ',El.ElementTypeName,' Visibility: ',VisibilityNames[El.Visibility]);
  233. Writeln(F,' Declaration: ',El.GetDeclaration(True),' -->');
  234. end;
  235. WriteLn(f,'<element name="', El.FullName, '">');
  236. WriteLn(f, '<short></short>');
  237. if Not WriteOnlyShort(El) then
  238. begin
  239. WriteLn(f, '<descr>');
  240. WriteLn(f, '</descr>');
  241. if not (DisableErrors or IsTypeVarConst(El)) then
  242. begin
  243. WriteLn(f, '<errors>');
  244. WriteLn(f, '</errors>');
  245. end;
  246. if not DisableSeealso then
  247. begin
  248. WriteLn(f, '<seealso>');
  249. WriteLn(f, '</seealso>');
  250. end;
  251. end;
  252. WriteLn(f, '</element>');
  253. end;
  254. Procedure TSkelEngine.DoWriteUnReferencedNodes(N : TDocNode; NodePath : String);
  255. begin
  256. If (N<>Nil) then
  257. begin
  258. If (NodePath<>'') then
  259. NodePath:=NodePath+'.';
  260. DoWriteUnReferencedNodes(N.FirstChild,NodePath+N.Name);
  261. While (N<>Nil) do
  262. begin
  263. if (N.RefCount=0) and (N.Node<>Nil) and (Not N.TopicNode) then
  264. Writeln(stderr,Format(SNodeNotReferenced,[NodePath+N.Name]));
  265. N:=N.NextSibling;
  266. end;
  267. end;
  268. end;
  269. procedure TSkelEngine.WriteUnReferencedNodes;
  270. begin
  271. DoWriteUnReferencedNodes(RootDocNode,'');
  272. end;
  273. Procedure TSkelEngine.WriteNodes(Var F : Text; AModule : TPasModule; List : TStrings);
  274. Var
  275. P : TNodePair;
  276. I : integer;
  277. begin
  278. WriteLn(f);
  279. WriteLn(f, '<!--');
  280. WriteLn(f, ' ====================================================================');
  281. WriteLn(f, ' ', Amodule.Name);
  282. WriteLn(f, ' ====================================================================');
  283. WriteLn(f, '-->');
  284. WriteLn(f);
  285. WriteLn(f, '<module name="', AModule.Name, '">');
  286. if not UpdateMode then
  287. begin
  288. WriteLn(f, '<short></short>');
  289. WriteLn(f, '<descr>');
  290. WriteLn(f, '</descr>');
  291. end;
  292. Try
  293. For I:=0 to List.Count-1 do
  294. begin
  295. P:=List.Objects[i] as TNodePair;
  296. If (P.Element<>AModule) then
  297. WriteElement(F,P.Element,P.DocNode);
  298. end;
  299. Finally
  300. WriteLn(f, '');
  301. WriteLn(f, '</module> <!-- ', AModule.Name, ' -->');
  302. WriteLn(f, '');
  303. end;
  304. end;
  305. Procedure TSkelEngine.DocumentFile(Var F : Text; Const AFileName,ATarget,ACPU : String);
  306. Var
  307. Module : TPasModule;
  308. I : Integer;
  309. N : TDocNode;
  310. begin
  311. // wrong because afilename is a cmdline with other options. Straight testing filename is therefore wrong.
  312. // if not(FileExists(AFileName)) then
  313. // raise Exception.CreateFmt('Cannot find source file %s to document.',[AFileName]);
  314. FNodeList:=TStringList.Create;
  315. Try
  316. FEmittedList:=TStringList.Create;
  317. FEmittedList.Sorted:=True;
  318. try
  319. Module:=ParseSource(Self,AFileName,ATarget,ACPU);
  320. If UpdateMode then
  321. begin
  322. N:=FindDocNode(Module);
  323. If Assigned(N) then
  324. N.IncRefCount;
  325. end;
  326. If SortNodes then
  327. FNodelist.Sorted:=True;
  328. WriteNodes(F,Module,FNodeList);
  329. If UpdateMode then
  330. WriteUnReferencedNodes;
  331. Finally
  332. FEmittedList.Free;
  333. end;
  334. Finally
  335. For I:=0 to FNodeList.Count-1 do
  336. FNodeList.Objects[i].Free;
  337. FNodeList.Free;
  338. end;
  339. end;
  340. { ---------------------------------------------------------------------
  341. Main program. Document all units.
  342. ---------------------------------------------------------------------}
  343. Function DocumentPackage(Const APackageName,AOutputName : String; InputFiles,DescrFiles : TStrings) : String;
  344. Var
  345. F : Text;
  346. I,J : Integer;
  347. Engine: TSkelEngine;
  348. begin
  349. Result:='';
  350. Assign(f, AOutputName);
  351. Rewrite(f);
  352. Try
  353. WriteLn(f, '<?xml version="1.0" encoding="ISO-8859-1"?>');
  354. WriteLn(f, '<fpdoc-descriptions>');
  355. WriteLn(f, '<package name="', APackageName, '">');
  356. Try
  357. I:=0;
  358. While (Result='') And (I<InputFiles.Count) do
  359. begin
  360. Engine := TSkelEngine.Create;
  361. Try
  362. Engine.SetPackageName(APackageName);
  363. if UpdateMode then
  364. For J:=0 to DescrFiles.Count-1 do
  365. Engine.AddDocFile(DescrFiles[J]);
  366. Try
  367. Engine.DocumentFile(F,InputFiles[I],OSTarget,CPUTarget);
  368. except
  369. on E:Exception do
  370. begin
  371. WriteLn('Error while documenting: '+E.message);
  372. Result:='Error while documenting: '+E.message;
  373. end;
  374. end;
  375. Finally
  376. Engine.Free;
  377. end;
  378. Inc(I);
  379. end;
  380. Finally
  381. WriteLn(f, '</package>');
  382. WriteLn(f, '</fpdoc-descriptions>');
  383. end;
  384. finally
  385. Close(f);
  386. end;
  387. end;
  388. { ---------------------------------------------------------------------
  389. Option management
  390. ---------------------------------------------------------------------}
  391. var
  392. InputFiles,
  393. DescrFiles : TStringList;
  394. DocLang : String;
  395. PackageName,
  396. OutputName: String;
  397. procedure InitOptions;
  398. begin
  399. InputFiles := TStringList.Create;
  400. DescrFiles := TStringList.Create;
  401. end;
  402. procedure FreeOptions;
  403. begin
  404. DescrFiles.Free;
  405. InputFiles.Free;
  406. end;
  407. procedure ParseOption(const s: String);
  408. procedure AddToFileList(List: TStringList; const FileName: String);
  409. var
  410. f: Text;
  411. s: String;
  412. begin
  413. if Copy(FileName, 1, 1) = '@' then
  414. begin
  415. Assign(f, Copy(FileName, 2, Length(FileName)));
  416. Reset(f);
  417. while not EOF(f) do
  418. begin
  419. ReadLn(f, s);
  420. List.Add(s);
  421. end;
  422. Close(f);
  423. end else
  424. List.Add(FileName);
  425. end;
  426. var
  427. i: Integer;
  428. Cmd, Arg: String;
  429. begin
  430. if (s = '-h') or (s = '--help') then
  431. CmdLineAction := actionHelp
  432. else if s = '--update' then
  433. UpdateMode := True
  434. else if s = '--disable-arguments' then
  435. DisableArguments := True
  436. else if s = '--disable-errors' then
  437. DisableErrors := True
  438. else if s = '--disable-function-results' then
  439. DisableFunctionResults := True
  440. else if s = '--disable-seealso' then
  441. DisableSeealso := True
  442. else if s = '--disable-private' then
  443. DisablePrivate := True
  444. else if s = '--disable-override' then
  445. DisableOverride := True
  446. else if s = '--disable-protected' then
  447. begin
  448. DisableProtected := True;
  449. DisablePrivate :=True;
  450. end
  451. else if (s = '--emitclassseparator') or (s='--emit-class-separator') then
  452. EmitClassSeparator := True
  453. else if (s = '--emit-declaration') then
  454. WriteDeclaration := True
  455. else if (s = '--sort-nodes') then
  456. SortNodes := True
  457. else
  458. begin
  459. i := Pos('=', s);
  460. if i > 0 then
  461. begin
  462. Cmd := Copy(s, 1, i - 1);
  463. Arg := Copy(s, i + 1, Length(s));
  464. end else
  465. begin
  466. Cmd := s;
  467. SetLength(Arg, 0);
  468. end;
  469. if (Cmd = '-i') or (Cmd = '--input') then
  470. AddToFileList(InputFiles, Arg)
  471. else if (Cmd = '-l') or (Cmd = '--lang') then
  472. DocLang := Arg
  473. else if (Cmd = '-o') or (Cmd = '--output') then
  474. OutputName := Arg
  475. else if Cmd = '--package' then
  476. PackageName := Arg
  477. else if Cmd = '--descr' then
  478. begin
  479. if FileExists(Arg) then
  480. DescrFiles.Add(Arg);
  481. end
  482. else
  483. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  484. end;
  485. end;
  486. Function ParseCommandLine : Integer;
  487. Const
  488. {$IFDEF Unix}
  489. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  490. {$ELSE}
  491. MoFileTemplate ='intl/makeskel.%s.mo';
  492. {$ENDIF}
  493. var
  494. MOFilename: string;
  495. i: Integer;
  496. begin
  497. Result:=0;
  498. DocLang:='';
  499. for i := 1 to ParamCount do
  500. ParseOption(ParamStr(i));
  501. If (DocLang<>'') then
  502. begin
  503. MOFilename:=Format(MOFileTemplate,[DocLang]);
  504. if FileExists(MOFilename) then
  505. gettext.TranslateResourceStrings(MoFileName)
  506. else
  507. writeln('NOTE: unable to find tranlation file ',MOFilename);
  508. // Translate internal documentation strings
  509. TranslateDocStrings(DocLang);
  510. end;
  511. // Action is to create the XML skeleton
  512. if (Length(PackageName) = 0) and (CmdLineAction<>ActionHelp) then
  513. begin
  514. WriteLn(SNoPackageNameProvided);
  515. Result:=2;
  516. end;
  517. if DescrFiles.IndexOf(OutputName)<>-1 then
  518. begin
  519. Writeln(SOutputMustNotBeDescr);
  520. Result:=3;
  521. end;
  522. end;
  523. { ---------------------------------------------------------------------
  524. Usage
  525. ---------------------------------------------------------------------}
  526. Procedure Usage;
  527. begin
  528. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options]');
  529. Writeln('Where [options] is one or more of :');
  530. Writeln(' --descr=filename Filename for update.');
  531. Writeln(' --disable-arguments Do not create nodes for function arguments.');
  532. Writeln(' --disable-errors Do not create errors node.');
  533. Writeln(' --disable-function-results');
  534. Writeln(' Do not create nodes for function arguments.');
  535. Writeln(' --disable-override Do not create nodes for override methods.');
  536. Writeln(' --disable-private Do not create nodes for class private fields.');
  537. Writeln(' --disable-protected Do not create nodes for class protected fields.');
  538. Writeln(' --disable-seealso Do not create seealso node.');
  539. Writeln(' --emit-class-separator');
  540. Writeln(' Emit descriptive comment between classes.');
  541. Writeln(' --emit-declaration Emit declaration for elements.');
  542. Writeln(' --help Emit help.');
  543. Writeln(' --input=cmdline Input file to create skeleton for.');
  544. Writeln(' Use options are as for compiler.');
  545. Writeln(' --lang=language Use selected language.');
  546. Writeln(' --output=filename Send output to file.');
  547. Writeln(' --package=name Specify package name (mandatory).');
  548. Writeln(' --sort-nodes Sort element nodes (not modules)');
  549. Writeln(' --update Update mode. Output only missing nodes.');
  550. end;
  551. { ---------------------------------------------------------------------
  552. Main Program
  553. ---------------------------------------------------------------------}
  554. Procedure Run;
  555. var
  556. E: Integer;
  557. begin
  558. WriteLn(STitle);
  559. WriteLn(Format(SVersion, [FPCVersion, FPCDate]));
  560. WriteLn(SCopyright1);
  561. WriteLn(SCopyright2);
  562. InitOptions;
  563. Try
  564. E:=ParseCommandLine;
  565. If E<>0 then
  566. Halt(E);
  567. WriteLn;
  568. if CmdLineAction = actionHelp then
  569. Usage
  570. else
  571. begin
  572. DocumentPackage(PackageName,OutputName,InputFiles,DescrFiles);
  573. WriteLn(SDone);
  574. end;
  575. Finally
  576. FreeOptions;
  577. end;
  578. end;
  579. Begin
  580. Run;
  581. end.