makeskel.pp 18 KB

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