makeskel.pp 12 KB

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