makeskel.pp 13 KB

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