makeskel.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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. TSkelEngine = class(TFPDocEngine)
  30. FModules : TStringList;
  31. Procedure DoWriteUnReferencedNodes(N : TDocNode; NodePath : String);
  32. public
  33. Destructor Destroy; override;
  34. function FindModule(const AName: String): TPasModule; override;
  35. function CreateElement(AClass: TPTreeElement; const AName: String;
  36. AParent: TPasElement; AVisibility :TPasMemberVisibility;
  37. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
  38. procedure WriteUnReferencedNodes;
  39. end;
  40. const
  41. CmdLineAction: TCmdLineAction = actionConvert;
  42. OSTarget: String = {$I %FPCTARGETOS%};
  43. CPUTarget: String = {$I %FPCTARGETCPU%};
  44. FPCVersion: String = {$I %FPCVERSION%};
  45. FPCDate: String = {$I %FPCDATE%};
  46. var
  47. EmittedList, InputFiles, DescrFiles: TStringList;
  48. DocLang: String;
  49. Engine: TSkelEngine;
  50. UpdateMode,
  51. DisableErrors,
  52. DisableSeealso,
  53. DisableArguments,
  54. DisableProtected,
  55. DisablePrivate,
  56. DisableFunctionResults: Boolean;
  57. EmitClassSeparator: Boolean;
  58. PackageName, OutputName: String;
  59. f: Text;
  60. function TSkelEngine.FindModule(const AName: String): TPasModule;
  61. Var
  62. I : Integer;
  63. begin
  64. Result:=Inherited FindModule(AName);
  65. If (Result=Nil) then
  66. begin // Create dummy list and search in that.
  67. If (FModules=Nil) then
  68. begin
  69. FModules:=TStringList.Create;
  70. FModules.Sorted:=True;
  71. end;
  72. I:=FModules.IndexOf(AName);
  73. IF (I=-1) then
  74. begin
  75. Result:=TPasModule.Create(AName,Nil);
  76. FModules.AddObject(AName,Result);
  77. end
  78. else
  79. Result:=FModules.Objects[i] as TPasModule;
  80. end;
  81. end;
  82. Destructor TSkelEngine.Destroy;
  83. Var
  84. I : Integer;
  85. begin
  86. If Assigned(FModules) then
  87. begin
  88. For I:=0 to FModules.Count-1 do
  89. FModules.Objects[i].Free;
  90. FreeAndNil(FModules);
  91. end;
  92. end;
  93. function TSkelEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  94. AParent: TPasElement; AVisibility : TPasMemberVisibility;
  95. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  96. Function WriteThisNode(APasElement : TPasElement; DocNode : TDocNode) : Boolean;
  97. Var
  98. ParentVisible:Boolean;
  99. PT,PP : TPasElement;
  100. begin
  101. ParentVisible:=True;
  102. If (APasElement is TPasArgument) or (APasElement is TPasResultElement) then
  103. begin
  104. PT:=AParent;
  105. // Skip ProcedureType or PasFunctionType
  106. If (PT<>Nil) then
  107. begin
  108. if (PT is TPasProcedureType) or (PT is TPasFunctionType) then
  109. PT:=PT.Parent;
  110. If (PT<>Nil) and ((PT is TPasProcedure) or (PT is TPasProcedure)) then
  111. PP:=PT.Parent
  112. else
  113. PP:=Nil;
  114. If (PP<>Nil) and (PP is TPasClassType) then
  115. begin
  116. ParentVisible:=((not DisablePrivate or (PT.Visibility<>visPrivate)) and
  117. (not DisableProtected or (PT.Visibility<>visProtected)));
  118. end;
  119. end;
  120. end;
  121. Result:=Assigned(AParent) and (Length(AName) > 0) and
  122. (ParentVisible and (not DisableArguments or (APasElement.ClassType <> TPasArgument))) and
  123. (ParentVisible and (not DisableFunctionResults or (APasElement.ClassType <> TPasResultElement))) and
  124. (not DisablePrivate or (AVisibility<>visPrivate)) and
  125. (not DisableProtected or (AVisibility<>visProtected)) and
  126. (Not Assigned(EmittedList) or (EmittedList.IndexOf(APasElement.FullName)=-1));
  127. If Result and updateMode then
  128. begin
  129. Result:=DocNode=Nil;
  130. If Result then
  131. Writeln(stderr,Format(ScreatingNewNode,[APasElement.PathName]));
  132. end;
  133. end;
  134. Function WriteOnlyShort(APasElement : TPasElement) : Boolean;
  135. begin
  136. Result:=(APasElement.ClassType=TPasArgument) or
  137. (APasElement.ClassType=TPasResultElement) or
  138. (APasElement.ClassType=TPasEnumValue);
  139. end;
  140. Function IsTypeVarConst(APasElement : TPasElement) : Boolean;
  141. begin
  142. With APasElement do
  143. Result:=(InheritsFrom(TPasType) and not InheritsFrom(TPasClassType)) or
  144. (InheritsFrom(TPasResString)) or
  145. (InheritsFrom(TPasVariable));
  146. end;
  147. Var
  148. DN : TDocNode;
  149. begin
  150. Result := AClass.Create(AName, AParent);
  151. If UpdateMode then
  152. begin
  153. DN:=FindDocNode(Result);
  154. If Assigned(DN) then
  155. DN.IncRefCount;
  156. end
  157. else
  158. DN:=Nil;
  159. Result.Visibility:=AVisibility;
  160. if AClass.InheritsFrom(TPasModule) then
  161. CurModule := TPasModule(Result);
  162. if Result.ClassType = TPasModule then
  163. begin
  164. WriteLn(f);
  165. WriteLn(f, '<!--');
  166. WriteLn(f, ' ====================================================================');
  167. WriteLn(f, ' ', Result.Name);
  168. WriteLn(f, ' ====================================================================');
  169. WriteLn(f, '-->');
  170. WriteLn(f);
  171. WriteLn(f, '<module name="', Result.Name, '">');
  172. if not UpdateMode then
  173. begin
  174. WriteLn(f, '<short></short>');
  175. WriteLn(f, '<descr>');
  176. WriteLn(f, '</descr>');
  177. end;
  178. end
  179. else if WriteThisNode(Result,DN) then
  180. begin
  181. EmittedList.Add(Result.FullName); // So we don't emit again.
  182. WriteLn(f);
  183. if EmitClassSeparator and (Result.ClassType = TPasClassType) then
  184. begin
  185. WriteLn(f, '<!--');
  186. WriteLn(f, ' ********************************************************************');
  187. WriteLn(f, ' ', Result.PathName);
  188. WriteLn(f, ' ********************************************************************');
  189. WriteLn(f, '-->');
  190. WriteLn(f);
  191. end;
  192. Writeln(F,'<!-- ', Result.ElementTypeName,' Visibility: ',VisibilityNames[AVisibility], ' -->');
  193. WriteLn(f,'<element name="', Result.FullName, '">');
  194. WriteLn(f, '<short></short>');
  195. if Not WriteOnlyShort(Result) then
  196. begin
  197. WriteLn(f, '<descr>');
  198. WriteLn(f, '</descr>');
  199. if not (DisableErrors or IsTypeVarConst(Result)) then
  200. begin
  201. WriteLn(f, '<errors>');
  202. WriteLn(f, '</errors>');
  203. end;
  204. if not DisableSeealso then
  205. begin
  206. WriteLn(f, '<seealso>');
  207. WriteLn(f, '</seealso>');
  208. end;
  209. end;
  210. WriteLn(f, '</element>');
  211. end;
  212. end;
  213. Procedure TSkelEngine.DoWriteUnReferencedNodes(N : TDocNode; NodePath : String);
  214. begin
  215. If (N<>Nil) then
  216. begin
  217. If (NodePath<>'') then
  218. NodePath:=NodePath+'.';
  219. DoWriteUnReferencedNodes(N.FirstChild,NodePath+N.Name);
  220. While (N<>Nil) do
  221. begin
  222. if (N.RefCount=0) and (N.Node<>Nil) and (Not N.TopicNode) then
  223. Writeln(stderr,Format(SNodeNotReferenced,[NodePath+N.Name]));
  224. N:=N.NextSibling;
  225. end;
  226. end;
  227. end;
  228. procedure TSkelEngine.WriteUnReferencedNodes;
  229. begin
  230. DoWriteUnReferencedNodes(RootDocNode,'');
  231. end;
  232. procedure InitOptions;
  233. begin
  234. InputFiles := TStringList.Create;
  235. DescrFiles := TStringList.Create;
  236. EmittedList:=TStringList.Create;
  237. EmittedList.Sorted:=True;
  238. end;
  239. procedure FreeOptions;
  240. begin
  241. DescrFiles.Free;
  242. InputFiles.Free;
  243. EmittedList.Free;
  244. end;
  245. Procedure Usage;
  246. begin
  247. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options]');
  248. Writeln('Where [options] is one or more of :');
  249. Writeln(' --descr=filename Filename for update.');
  250. Writeln(' --disable-arguments Do not create nodes for function arguments.');
  251. Writeln(' --disable-errors Do not create errors node.');
  252. Writeln(' --disable-function-results');
  253. Writeln(' Do not create nodes for function arguments.');
  254. Writeln(' --disable-private Do not create nodes for class private fields.');
  255. Writeln(' --disable-protected Do not create nodes for class protected fields.');
  256. Writeln(' --disable-seealso Do not create seealso node.');
  257. Writeln(' --emit-class-separator');
  258. Writeln(' Emit descriptive comment between classes.');
  259. Writeln(' --help Emit help.');
  260. Writeln(' --input=cmdline Input file to create skeleton for.');
  261. Writeln(' Use options are as for compiler.');
  262. Writeln(' --lang=language Use selected language.');
  263. Writeln(' --output=filename Send output to file.');
  264. Writeln(' --package=name Specify package name (mandatory).');
  265. Writeln(' --update Update mode. Output only missing nodes.');
  266. end;
  267. procedure ParseOption(const s: String);
  268. procedure AddToFileList(List: TStringList; const FileName: String);
  269. var
  270. f: Text;
  271. s: String;
  272. begin
  273. if Copy(FileName, 1, 1) = '@' then
  274. begin
  275. Assign(f, Copy(FileName, 2, Length(FileName)));
  276. Reset(f);
  277. while not EOF(f) do
  278. begin
  279. ReadLn(f, s);
  280. List.Add(s);
  281. end;
  282. Close(f);
  283. end else
  284. List.Add(FileName);
  285. end;
  286. var
  287. i: Integer;
  288. Cmd, Arg: String;
  289. begin
  290. if (s = '-h') or (s = '--help') then
  291. CmdLineAction := actionHelp
  292. else if s = '--update' then
  293. UpdateMode := True
  294. else if s = '--disable-arguments' then
  295. DisableArguments := True
  296. else if s = '--disable-errors' then
  297. DisableErrors := True
  298. else if s = '--disable-function-results' then
  299. DisableFunctionResults := True
  300. else if s = '--disable-seealso' then
  301. DisableSeealso := True
  302. else if s = '--disable-private' then
  303. DisablePrivate := True
  304. else if s = '--disable-protected' then
  305. begin
  306. DisableProtected := True;
  307. DisablePrivate :=True;
  308. end
  309. else if (s = '--emitclassseparator') or (s='--emit-class-separator') then
  310. EmitClassSeparator := True
  311. else
  312. begin
  313. i := Pos('=', s);
  314. if i > 0 then
  315. begin
  316. Cmd := Copy(s, 1, i - 1);
  317. Arg := Copy(s, i + 1, Length(s));
  318. end else
  319. begin
  320. Cmd := s;
  321. SetLength(Arg, 0);
  322. end;
  323. if (Cmd = '-i') or (Cmd = '--input') then
  324. AddToFileList(InputFiles, Arg)
  325. else if (Cmd = '-l') or (Cmd = '--lang') then
  326. DocLang := Arg
  327. else if (Cmd = '-o') or (Cmd = '--output') then
  328. OutputName := Arg
  329. else if Cmd = '--package' then
  330. PackageName := Arg
  331. else if Cmd = '--descr' then
  332. begin
  333. if FileExists(Arg) then
  334. DescrFiles.Add(Arg);
  335. end
  336. else
  337. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  338. end;
  339. end;
  340. procedure ParseCommandLine;
  341. Const
  342. {$IFDEF Unix}
  343. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  344. {$ELSE}
  345. MoFileTemplate ='intl/makeskel.%s.mo';
  346. {$ENDIF}
  347. var
  348. MOFilename: string;
  349. i: Integer;
  350. begin
  351. DocLang:='';
  352. for i := 1 to ParamCount do
  353. ParseOption(ParamStr(i));
  354. If (DocLang<>'') then
  355. begin
  356. MOFilename:=Format(MOFileTemplate,[DocLang]);
  357. if FileExists(MOFilename) then
  358. gettext.TranslateResourceStrings(MoFileName)
  359. else
  360. writeln('NOTE: unable to find tranlation file ',MOFilename);
  361. // Translate internal documentation strings
  362. TranslateDocStrings(DocLang);
  363. end;
  364. end;
  365. var
  366. i,j: Integer;
  367. Module: TPasModule;
  368. N : TDocNode;
  369. begin
  370. InitOptions;
  371. ParseCommandLine;
  372. WriteLn(STitle);
  373. WriteLn(Format(SVersion, [FPCVersion, FPCDate]));
  374. WriteLn(SCopyright);
  375. WriteLn;
  376. if CmdLineAction = actionHelp then
  377. Usage
  378. else
  379. begin
  380. // Action is to create the XML skeleton
  381. if Length(PackageName) = 0 then
  382. begin
  383. WriteLn(SNoPackageNameProvided);
  384. Halt(2);
  385. end;
  386. if DescrFiles.IndexOf(OutputName)<>-1 then
  387. begin
  388. Writeln(SOutputMustNotBeDescr);
  389. Halt(3)
  390. end;
  391. Assign(f, OutputName);
  392. Rewrite(f);
  393. WriteLn(f, '<?xml version="1.0" encoding="ISO-8859-1"?>');
  394. WriteLn(f, '<fpdoc-descriptions>');
  395. WriteLn(f, '<package name="', PackageName, '">');
  396. // Process all source files
  397. for i := 0 to InputFiles.Count - 1 do
  398. begin
  399. Engine := TSkelEngine.Create;
  400. try
  401. try
  402. Engine.SetPackageName(PackageName);
  403. if UpdateMode then
  404. For J:=0 to DescrFiles.Count-1 do
  405. Engine.AddDocFile(DescrFiles[J]);
  406. Module := ParseSource(Engine, InputFiles[i], OSTarget, CPUTarget);
  407. If UpdateMode then
  408. begin
  409. N:=Engine.FindDocNode(Module);
  410. If Assigned(N) then
  411. N.IncRefCount;
  412. end;
  413. WriteLn(f, '');
  414. WriteLn(f, '</module> <!-- ', Module.Name, ' -->');
  415. WriteLn(f, '');
  416. except
  417. on e:EFileNotFoundError do
  418. begin
  419. Writeln(StdErr,' file ', e.message, ' not found');
  420. close(f);
  421. Halt(1);
  422. end;
  423. on e:EParserError do
  424. begin
  425. Writeln(StdErr,'', e.filename,'(',e.row,',',e.column,') Fatal: ',e.message);
  426. close(f);
  427. Halt(1);
  428. end;
  429. end;
  430. If UpdateMode then
  431. Engine.WriteUnReferencedNodes;
  432. finally
  433. Engine.Free;
  434. end;
  435. end;
  436. WriteLn(f, '</package>');
  437. WriteLn(f, '</fpdoc-descriptions>');
  438. Close(f);
  439. WriteLn(SDone);
  440. end;
  441. FreeOptions;
  442. end.