makeskel.pp 17 KB

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