unitdiff.pp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. {
  2. $Id$
  3. UnitDiff Copyright (C) 2004 by the Free Pascal team
  4. Show differences between unit interfaces.
  5. See the file COPYING, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. }
  11. program MakeSkel;
  12. uses
  13. SysUtils, Classes, Gettext,
  14. dGlobals, PasTree, PParser,PScanner;
  15. resourcestring
  16. SIdentifiersIn = 'Identifiers in file "%s"';
  17. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  18. SErrNoInputFile = 'No input file specified';
  19. SWarnAssumingList = 'Only one input file specified. Assuming --list option.';
  20. SExtraIdentifier = 'Extra identifier in file "%s" : Name: %s';
  21. SExtraTypedIdentifier = 'Extra identifier in file "%s" : Type %s, Name: %s';
  22. SIdenticalUnits = 'Unit interfaces are identical.';
  23. type
  24. TCmdLineAction = (actionHelp, actionDiff,ActionList);
  25. TSkelEngine = class(TFPDocEngine)
  26. public
  27. FList: TStringList;
  28. Constructor Create;
  29. Destructor Destroy;override;
  30. function CreateElement(AClass: TPTreeElement; const AName: String;
  31. AParent: TPasElement; AVisibility :TPasMemberVisibility;
  32. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
  33. end;
  34. Constructor TSkelEngine.Create;
  35. begin
  36. Inherited Create;
  37. FList:=TStringList.Create;
  38. end;
  39. Destructor TSkelEngine.Destroy;
  40. begin
  41. FreeAndNil(FList);
  42. Inherited;
  43. end;
  44. const
  45. CmdLineAction: TCmdLineAction = actionDiff;
  46. OSTarget: String = {$I %FPCTARGETOS%};
  47. CPUTarget: String = {$I %FPCTARGETCPU%};
  48. var
  49. InputFile1,
  50. InputFile2 : String;
  51. DocLang: String;
  52. Engine1,
  53. Engine2: TSkelEngine;
  54. SparseList,
  55. DisableArguments,
  56. DisableProtected,
  57. DisablePrivate,
  58. DisableFunctionResults: Boolean;
  59. OutputName: String;
  60. f: Text;
  61. function TSkelEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  62. AParent: TPasElement; AVisibility : TPasMemberVisibility;
  63. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  64. Function ExamineThisNode(APasElement : TPasElement) : Boolean;
  65. begin
  66. Result:=Assigned(AParent) and (Length(AName) > 0) and
  67. (not DisableArguments or (APasElement.ClassType <> TPasArgument)) and
  68. (not DisableFunctionResults or (APasElement.ClassType <> TPasResultElement)) and
  69. (not DisablePrivate or (AVisibility<>visPrivate)) and
  70. (not DisableProtected or (AVisibility<>visProtected));
  71. end;
  72. begin
  73. Result := AClass.Create(AName, AParent);
  74. if AClass.InheritsFrom(TPasModule) then
  75. CurModule := TPasModule(Result)
  76. else if ExamineThisNode(Result) then
  77. Flist.AddObject(Result.FullName,Result);
  78. end;
  79. Procedure Usage;
  80. begin
  81. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options] file1 file2');
  82. Writeln('Where [options] is one or more of :');
  83. Writeln(' --disable-arguments Do not check function arguments.');
  84. Writeln(' --disable-private Do not check class private fields.');
  85. Writeln(' --disable-protected Do not check class protected fields.');
  86. Writeln(' --help Emit help.');
  87. Writeln(' --input=cmdline Input file to create skeleton for.');
  88. Writeln(' Use options are as for compiler.');
  89. Writeln(' --lang=language Use selected language.');
  90. Writeln(' --list List identifiers instead of making a diff');
  91. Writeln(' --output=filename Send output to file.');
  92. Writeln(' --sparse Sparse list/diff (skip type identification)');
  93. end;
  94. procedure ParseOption(const s: String);
  95. var
  96. i: Integer;
  97. Cmd, Arg: String;
  98. begin
  99. if (s = '-h') or (s = '--help') then
  100. CmdLineAction := actionHelp
  101. else if s = '--disable-arguments' then
  102. DisableArguments := True
  103. else if s = '--disable-private' then
  104. DisablePrivate := True
  105. else if s = '--sparse' then
  106. SparseList := True
  107. else if s = '--disable-protected' then
  108. begin
  109. DisableProtected := True;
  110. DisablePrivate :=True;
  111. end
  112. else
  113. begin
  114. i := Pos('=', s);
  115. if i > 0 then
  116. begin
  117. Cmd := Copy(s, 1, i - 1);
  118. Arg := Copy(s, i + 1, Length(s));
  119. end
  120. else
  121. begin
  122. Cmd := s;
  123. SetLength(Arg, 0);
  124. end;
  125. if (Cmd = '-l') or (Cmd = '--lang') then
  126. DocLang := Arg
  127. else if (Cmd = '-o') or (Cmd = '--output') then
  128. OutputName := Arg
  129. else
  130. if (length(cmd)>0) and (cmd[1]='-') then
  131. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]))
  132. else if (InputFile1='') then
  133. InputFile1:=Cmd
  134. else if (InputFile2='') then
  135. InputFile2:=Cmd
  136. else
  137. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  138. end;
  139. end;
  140. procedure ParseCommandLine;
  141. Const
  142. {$IFDEF Unix}
  143. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  144. {$ELSE}
  145. MoFileTemplate ='intl/makeskel.%s.mo';
  146. {$ENDIF}
  147. var
  148. MOFilename: string;
  149. i: Integer;
  150. begin
  151. CmdLineAction := actionDiff;
  152. DocLang:='';
  153. SparseList:=False;
  154. for i := 1 to ParamCount do
  155. ParseOption(ParamStr(i));
  156. If (DocLang<>'') then
  157. begin
  158. MOFilename:=Format(MOFileTemplate,[DocLang]);
  159. if FileExists(MOFilename) then
  160. gettext.TranslateResourceStrings(MoFileName)
  161. else
  162. writeln('NOTE: unable to find tranlation file ',MOFilename);
  163. // Translate internal documentation strings
  164. TranslateDocStrings(DocLang);
  165. end;
  166. if (cmdLineAction<>ActionHelp) and (InputFile1='') and (InputFile2='') then
  167. Writeln(StdErr,SErrNoInputFile)
  168. else if (InputFile2='') and (CmdLineAction<>ActionList) then
  169. begin
  170. Writeln(StdErr,SWarnAssumingList);
  171. CmdLineAction:=ActionList;
  172. end;
  173. end;
  174. Function GetTypeDescription(El : TPasElement) : String;
  175. begin
  176. If Assigned(El) then
  177. Result:=El.ElementTypeName
  178. else
  179. Result:='(unknown)';
  180. end;
  181. Procedure ListIdentifiers(Fn : String; List : TStrings);
  182. Var
  183. I : Integer;
  184. begin
  185. Writeln(f,Format(SIdentifiersIn,[FN]));
  186. For I:=0 to List.Count-1 do
  187. begin
  188. If Not SparseList then
  189. Write(GetTypeDescription(TPasElement(List.Objects[i])),' : ');
  190. Writeln(List[i]);
  191. end;
  192. end;
  193. Procedure WriteExtra(FN,Id : String; El: TPaselement);
  194. begin
  195. If SparseList then
  196. Writeln(F,Format(SExtraIdentifier,[FN,ID]))
  197. else
  198. Writeln(F,Format(SExtraTypedIdentifier,[FN,GetTypeDescription(El),ID]));
  199. end;
  200. Procedure DoExtra(FN : String; L : TStrings);
  201. Var
  202. I,Len : Integer;
  203. S : String;
  204. begin
  205. I:=0;
  206. While (I<L.Count) do
  207. begin
  208. WriteExtra(FN,L[I],TPasElement(L.Objects[I]));
  209. // Delete possible subelements.
  210. S:=L[I]+'.';
  211. Len:=Length(S);
  212. While (I+1<L.Count) and (CompareText(Copy(L[I+1],1,Len),S)=0) do
  213. L.Delete(I+1);
  214. Inc(I);
  215. end;
  216. end;
  217. Procedure DiffIdentifiers(List1,List2 : TStrings);
  218. Var
  219. L1,L2 : TStrings;
  220. I,J : Integer;
  221. begin
  222. L1:=List1;
  223. L2:=List2;
  224. If List2.Count>List1.Count then
  225. begin
  226. L1:=List2;
  227. L2:=List1;
  228. end;
  229. // Remove all common elements.
  230. For I:=L1.Count-1 downto 0 do
  231. begin
  232. J:=L2.IndexOf(L1[i]);
  233. If (J<>-1) then
  234. begin
  235. L1.Delete(I);
  236. L2.Delete(J);
  237. end;
  238. end;
  239. If (List1.Count=0) and (List2.Count=0) then
  240. Writeln(F,SIdenticalUnits)
  241. else
  242. begin
  243. DoExtra(InputFile1,List1);
  244. DoExtra(InputFile2,List2);
  245. end;
  246. end;
  247. begin
  248. ParseCommandLine;
  249. if CmdLineAction = actionHelp then
  250. Usage
  251. else
  252. begin
  253. Assign(f, OutputName);
  254. Rewrite(f);
  255. Try
  256. Engine1:=TSkelEngine.Create;
  257. Try
  258. Engine1.SetPackageName('diff'); // do not localize
  259. ParseSource(Engine1, InputFile1, OSTarget, CPUTarget);
  260. Engine1.FList.Sorted:=True;
  261. if (InputFile2<>'') then
  262. begin
  263. Engine2:=TSkelEngine.Create;
  264. Try
  265. Engine2.SetPackageName('diff'); // do not localize
  266. ParseSource(Engine2, InputFile2, OSTarget, CPUTarget);
  267. Engine2.FList.Sorted:=True;
  268. If cmdLineAction=ActionList then
  269. begin
  270. ListIdentifiers(InputFile1,Engine1.FList);
  271. ListIdentifiers(InputFile2,Engine2.FList);
  272. end
  273. else
  274. DiffIdentifiers(Engine1.Flist,Engine2.Flist);
  275. finally
  276. Engine2.Free;
  277. end;
  278. end
  279. else
  280. ListIdentifiers(InputFile1,Engine1.FList);
  281. Finally
  282. Engine1.Free;
  283. end;
  284. Finally
  285. Close(f);
  286. end;
  287. end;
  288. end.
  289. {
  290. $Log$
  291. Revision 1.3 2004-11-15 18:03:28 michael
  292. + Faster inserts by sorting after all elements were parsed (suggestion by Mattias Gaertner)
  293. Revision 1.2 2004/11/14 21:20:31 michael
  294. + Changed copyright
  295. Revision 1.1 2004/11/14 21:18:58 michael
  296. + Initial check-in
  297. Revision 1.13 2004/09/13 16:04:52 peter
  298. * fix nested for-loop with same index
  299. Revision 1.12 2004/08/29 15:32:41 michael
  300. + More intelligent handling of nodes. Do not write unused nodes.
  301. Revision 1.11 2004/08/28 18:18:59 michael
  302. + Do not write descr nodes for module when updating
  303. Revision 1.10 2004/08/28 18:15:14 michael
  304. + Check whether outputfile not in inputfilenames
  305. Revision 1.9 2004/08/28 18:04:06 michael
  306. + Added update mode
  307. Revision 1.8 2004/08/25 07:16:43 michael
  308. + Improved translation handling
  309. Revision 1.7 2004/08/24 14:48:25 michael
  310. + Translate now called correctly...
  311. Revision 1.6 2004/05/01 20:13:40 marco
  312. * got fed up with exceptions on file not found. Fileresolver now raises a
  313. EFileNotFound error, and makeskel catches and exists gracefully
  314. Revision 1.5 2003/11/28 12:51:37 sg
  315. * Added support for source references
  316. Revision 1.4 2003/09/02 13:26:47 mattias
  317. MG: makeskel now ignores missing translation file
  318. Revision 1.3 2003/05/07 16:31:32 sg
  319. * Fixed a severe memory corruption problem on termination
  320. Revision 1.2 2003/03/28 13:01:36 michael
  321. + Patch from Charlie/iNQ to work with new scanner/parser
  322. Revision 1.1 2003/03/17 23:03:20 michael
  323. + Initial import in CVS
  324. }