makeskel.pp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  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. if not(FileExists(AFileName)) then
  312. raise Exception.CreateFmt('Cannot find source file %s to document.',[AFileName]);
  313. FNodeList:=TStringList.Create;
  314. Try
  315. FEmittedList:=TStringList.Create;
  316. FEmittedList.Sorted:=True;
  317. try
  318. Module:=ParseSource(Self,AFileName,ATarget,ACPU);
  319. If UpdateMode then
  320. begin
  321. N:=FindDocNode(Module);
  322. If Assigned(N) then
  323. N.IncRefCount;
  324. end;
  325. If SortNodes then
  326. FNodelist.Sorted:=True;
  327. WriteNodes(F,Module,FNodeList);
  328. If UpdateMode then
  329. WriteUnReferencedNodes;
  330. Finally
  331. FEmittedList.Free;
  332. end;
  333. Finally
  334. For I:=0 to FNodeList.Count-1 do
  335. FNodeList.Objects[i].Free;
  336. FNodeList.Free;
  337. end;
  338. end;
  339. { ---------------------------------------------------------------------
  340. Main program. Document all units.
  341. ---------------------------------------------------------------------}
  342. Function DocumentPackage(Const APackageName,AOutputName : String; InputFiles,DescrFiles : TStrings) : String;
  343. Var
  344. F : Text;
  345. I,J : Integer;
  346. Engine: TSkelEngine;
  347. begin
  348. Result:='';
  349. Assign(f, AOutputName);
  350. Rewrite(f);
  351. Try
  352. WriteLn(f, '<?xml version="1.0" encoding="ISO-8859-1"?>');
  353. WriteLn(f, '<fpdoc-descriptions>');
  354. WriteLn(f, '<package name="', APackageName, '">');
  355. Try
  356. I:=0;
  357. While (Result='') And (I<InputFiles.Count) do
  358. begin
  359. Engine := TSkelEngine.Create;
  360. Try
  361. Engine.SetPackageName(APackageName);
  362. if UpdateMode then
  363. For J:=0 to DescrFiles.Count-1 do
  364. Engine.AddDocFile(DescrFiles[J]);
  365. Try
  366. Engine.DocumentFile(F,InputFiles[I],OSTarget,CPUTarget);
  367. except
  368. on E:Exception do
  369. begin
  370. WriteLn('Error while documenting: '+E.message);
  371. Result:='Error while documenting: '+E.message;
  372. end;
  373. end;
  374. Finally
  375. Engine.Free;
  376. end;
  377. Inc(I);
  378. end;
  379. Finally
  380. WriteLn(f, '</package>');
  381. WriteLn(f, '</fpdoc-descriptions>');
  382. end;
  383. finally
  384. Close(f);
  385. end;
  386. end;
  387. { ---------------------------------------------------------------------
  388. Option management
  389. ---------------------------------------------------------------------}
  390. var
  391. InputFiles,
  392. DescrFiles : TStringList;
  393. DocLang : String;
  394. PackageName,
  395. OutputName: String;
  396. procedure InitOptions;
  397. begin
  398. InputFiles := TStringList.Create;
  399. DescrFiles := TStringList.Create;
  400. end;
  401. procedure FreeOptions;
  402. begin
  403. DescrFiles.Free;
  404. InputFiles.Free;
  405. end;
  406. procedure ParseOption(const s: String);
  407. procedure AddToFileList(List: TStringList; const FileName: String);
  408. var
  409. f: Text;
  410. s: String;
  411. begin
  412. if Copy(FileName, 1, 1) = '@' then
  413. begin
  414. Assign(f, Copy(FileName, 2, Length(FileName)));
  415. Reset(f);
  416. while not EOF(f) do
  417. begin
  418. ReadLn(f, s);
  419. List.Add(s);
  420. end;
  421. Close(f);
  422. end else
  423. List.Add(FileName);
  424. end;
  425. var
  426. i: Integer;
  427. Cmd, Arg: String;
  428. begin
  429. if (s = '-h') or (s = '--help') then
  430. CmdLineAction := actionHelp
  431. else if s = '--update' then
  432. UpdateMode := True
  433. else if s = '--disable-arguments' then
  434. DisableArguments := True
  435. else if s = '--disable-errors' then
  436. DisableErrors := True
  437. else if s = '--disable-function-results' then
  438. DisableFunctionResults := True
  439. else if s = '--disable-seealso' then
  440. DisableSeealso := True
  441. else if s = '--disable-private' then
  442. DisablePrivate := True
  443. else if s = '--disable-override' then
  444. DisableOverride := True
  445. else if s = '--disable-protected' then
  446. begin
  447. DisableProtected := True;
  448. DisablePrivate :=True;
  449. end
  450. else if (s = '--emitclassseparator') or (s='--emit-class-separator') then
  451. EmitClassSeparator := True
  452. else if (s = '--emit-declaration') then
  453. WriteDeclaration := True
  454. else if (s = '--sort-nodes') then
  455. SortNodes := True
  456. else
  457. begin
  458. i := Pos('=', s);
  459. if i > 0 then
  460. begin
  461. Cmd := Copy(s, 1, i - 1);
  462. Arg := Copy(s, i + 1, Length(s));
  463. end else
  464. begin
  465. Cmd := s;
  466. SetLength(Arg, 0);
  467. end;
  468. if (Cmd = '-i') or (Cmd = '--input') then
  469. AddToFileList(InputFiles, Arg)
  470. else if (Cmd = '-l') or (Cmd = '--lang') then
  471. DocLang := Arg
  472. else if (Cmd = '-o') or (Cmd = '--output') then
  473. OutputName := Arg
  474. else if Cmd = '--package' then
  475. PackageName := Arg
  476. else if Cmd = '--descr' then
  477. begin
  478. if FileExists(Arg) then
  479. DescrFiles.Add(Arg);
  480. end
  481. else
  482. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  483. end;
  484. end;
  485. Function ParseCommandLine : Integer;
  486. Const
  487. {$IFDEF Unix}
  488. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  489. {$ELSE}
  490. MoFileTemplate ='intl/makeskel.%s.mo';
  491. {$ENDIF}
  492. var
  493. MOFilename: string;
  494. i: Integer;
  495. begin
  496. Result:=0;
  497. DocLang:='';
  498. for i := 1 to ParamCount do
  499. ParseOption(ParamStr(i));
  500. If (DocLang<>'') then
  501. begin
  502. MOFilename:=Format(MOFileTemplate,[DocLang]);
  503. if FileExists(MOFilename) then
  504. gettext.TranslateResourceStrings(MoFileName)
  505. else
  506. writeln('NOTE: unable to find tranlation file ',MOFilename);
  507. // Translate internal documentation strings
  508. TranslateDocStrings(DocLang);
  509. end;
  510. // Action is to create the XML skeleton
  511. if (Length(PackageName) = 0) and (CmdLineAction<>ActionHelp) then
  512. begin
  513. WriteLn(SNoPackageNameProvided);
  514. Result:=2;
  515. end;
  516. if DescrFiles.IndexOf(OutputName)<>-1 then
  517. begin
  518. Writeln(SOutputMustNotBeDescr);
  519. Result:=3;
  520. end;
  521. end;
  522. { ---------------------------------------------------------------------
  523. Usage
  524. ---------------------------------------------------------------------}
  525. Procedure Usage;
  526. begin
  527. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options]');
  528. Writeln('Where [options] is one or more of :');
  529. Writeln(' --descr=filename Filename for update.');
  530. Writeln(' --disable-arguments Do not create nodes for function arguments.');
  531. Writeln(' --disable-errors Do not create errors node.');
  532. Writeln(' --disable-function-results');
  533. Writeln(' Do not create nodes for function arguments.');
  534. Writeln(' --disable-override Do not create nodes for override methods.');
  535. Writeln(' --disable-private Do not create nodes for class private fields.');
  536. Writeln(' --disable-protected Do not create nodes for class protected fields.');
  537. Writeln(' --disable-seealso Do not create seealso node.');
  538. Writeln(' --emit-class-separator');
  539. Writeln(' Emit descriptive comment between classes.');
  540. Writeln(' --emit-declaration Emit declaration for elements.');
  541. Writeln(' --help Emit help.');
  542. Writeln(' --input=cmdline Input file to create skeleton for.');
  543. Writeln(' Use options are as for compiler.');
  544. Writeln(' --lang=language Use selected language.');
  545. Writeln(' --output=filename Send output to file.');
  546. Writeln(' --package=name Specify package name (mandatory).');
  547. Writeln(' --sort-nodes Sort element nodes (not modules)');
  548. Writeln(' --update Update mode. Output only missing nodes.');
  549. end;
  550. { ---------------------------------------------------------------------
  551. Main Program
  552. ---------------------------------------------------------------------}
  553. Procedure Run;
  554. var
  555. E: Integer;
  556. begin
  557. WriteLn(STitle);
  558. WriteLn(Format(SVersion, [FPCVersion, FPCDate]));
  559. WriteLn(SCopyright1);
  560. WriteLn(SCopyright2);
  561. InitOptions;
  562. Try
  563. E:=ParseCommandLine;
  564. If E<>0 then
  565. Halt(E);
  566. WriteLn;
  567. if CmdLineAction = actionHelp then
  568. Usage
  569. else
  570. begin
  571. DocumentPackage(PackageName,OutputName,InputFiles,DescrFiles);
  572. WriteLn(SDone);
  573. end;
  574. Finally
  575. FreeOptions;
  576. end;
  577. end;
  578. Begin
  579. Run;
  580. end.