unitdiff.pp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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. begin
  168. Writeln(StdErr,SErrNoInputFile);
  169. cmdLineAction := actionHelp;
  170. end else if (InputFile2='') and (CmdLineAction<>ActionList) then
  171. begin
  172. Writeln(StdErr,SWarnAssumingList);
  173. CmdLineAction:=ActionList;
  174. end;
  175. end;
  176. Function GetTypeDescription(El : TPasElement) : String;
  177. begin
  178. If Assigned(El) then
  179. Result:=El.ElementTypeName
  180. else
  181. Result:='(unknown)';
  182. end;
  183. Procedure ListIdentifiers(Fn : String; List : TStrings);
  184. Var
  185. I : Integer;
  186. begin
  187. Writeln(f,Format(SIdentifiersIn,[FN]));
  188. For I:=0 to List.Count-1 do
  189. begin
  190. If Not SparseList then
  191. Write(GetTypeDescription(TPasElement(List.Objects[i])),' : ');
  192. Writeln(List[i]);
  193. end;
  194. end;
  195. Procedure WriteExtra(FN,Id : String; El: TPaselement);
  196. begin
  197. If SparseList then
  198. Writeln(F,Format(SExtraIdentifier,[FN,ID]))
  199. else
  200. Writeln(F,Format(SExtraTypedIdentifier,[FN,GetTypeDescription(El),ID]));
  201. end;
  202. Procedure DoExtra(FN : String; L : TStrings);
  203. Var
  204. I,Len : Integer;
  205. S : String;
  206. begin
  207. I:=0;
  208. While (I<L.Count) do
  209. begin
  210. WriteExtra(FN,L[I],TPasElement(L.Objects[I]));
  211. // Delete possible subelements.
  212. S:=L[I]+'.';
  213. Len:=Length(S);
  214. While (I+1<L.Count) and (CompareText(Copy(L[I+1],1,Len),S)=0) do
  215. L.Delete(I+1);
  216. Inc(I);
  217. end;
  218. end;
  219. Procedure DiffIdentifiers(List1,List2 : TStrings);
  220. Var
  221. L1,L2 : TStrings;
  222. I,J : Integer;
  223. begin
  224. L1:=List1;
  225. L2:=List2;
  226. If List2.Count>List1.Count then
  227. begin
  228. L1:=List2;
  229. L2:=List1;
  230. end;
  231. // Remove all common elements.
  232. For I:=L1.Count-1 downto 0 do
  233. begin
  234. J:=L2.IndexOf(L1[i]);
  235. If (J<>-1) then
  236. begin
  237. L1.Delete(I);
  238. L2.Delete(J);
  239. end;
  240. end;
  241. If (List1.Count=0) and (List2.Count=0) then
  242. Writeln(F,SIdenticalUnits)
  243. else
  244. begin
  245. DoExtra(InputFile1,List1);
  246. DoExtra(InputFile2,List2);
  247. end;
  248. end;
  249. begin
  250. ParseCommandLine;
  251. if CmdLineAction = actionHelp then
  252. Usage
  253. else
  254. begin
  255. Assign(f, OutputName);
  256. Rewrite(f);
  257. Try
  258. Engine1:=TSkelEngine.Create;
  259. Try
  260. Engine1.SetPackageName('diff'); // do not localize
  261. ParseSource(Engine1, InputFile1, OSTarget, CPUTarget);
  262. Engine1.FList.Sorted:=True;
  263. if (InputFile2<>'') then
  264. begin
  265. Engine2:=TSkelEngine.Create;
  266. Try
  267. Engine2.SetPackageName('diff'); // do not localize
  268. ParseSource(Engine2, InputFile2, OSTarget, CPUTarget);
  269. Engine2.FList.Sorted:=True;
  270. If cmdLineAction=ActionList then
  271. begin
  272. ListIdentifiers(InputFile1,Engine1.FList);
  273. ListIdentifiers(InputFile2,Engine2.FList);
  274. end
  275. else
  276. DiffIdentifiers(Engine1.Flist,Engine2.Flist);
  277. finally
  278. Engine2.Free;
  279. end;
  280. end
  281. else
  282. ListIdentifiers(InputFile1,Engine1.FList);
  283. Finally
  284. Engine1.Free;
  285. end;
  286. Finally
  287. Close(f);
  288. end;
  289. end;
  290. end.
  291. {
  292. $Log$
  293. Revision 1.4 2005-01-01 19:56:29 armin
  294. * fixed access violation without file on command line
  295. Revision 1.3 2004/11/15 18:03:28 michael
  296. + Faster inserts by sorting after all elements were parsed (suggestion by Mattias Gaertner)
  297. Revision 1.2 2004/11/14 21:20:31 michael
  298. + Changed copyright
  299. Revision 1.1 2004/11/14 21:18:58 michael
  300. + Initial check-in
  301. Revision 1.13 2004/09/13 16:04:52 peter
  302. * fix nested for-loop with same index
  303. Revision 1.12 2004/08/29 15:32:41 michael
  304. + More intelligent handling of nodes. Do not write unused nodes.
  305. Revision 1.11 2004/08/28 18:18:59 michael
  306. + Do not write descr nodes for module when updating
  307. Revision 1.10 2004/08/28 18:15:14 michael
  308. + Check whether outputfile not in inputfilenames
  309. Revision 1.9 2004/08/28 18:04:06 michael
  310. + Added update mode
  311. Revision 1.8 2004/08/25 07:16:43 michael
  312. + Improved translation handling
  313. Revision 1.7 2004/08/24 14:48:25 michael
  314. + Translate now called correctly...
  315. Revision 1.6 2004/05/01 20:13:40 marco
  316. * got fed up with exceptions on file not found. Fileresolver now raises a
  317. EFileNotFound error, and makeskel catches and exists gracefully
  318. Revision 1.5 2003/11/28 12:51:37 sg
  319. * Added support for source references
  320. Revision 1.4 2003/09/02 13:26:47 mattias
  321. MG: makeskel now ignores missing translation file
  322. Revision 1.3 2003/05/07 16:31:32 sg
  323. * Fixed a severe memory corruption problem on termination
  324. Revision 1.2 2003/03/28 13:01:36 michael
  325. + Patch from Charlie/iNQ to work with new scanner/parser
  326. Revision 1.1 2003/03/17 23:03:20 michael
  327. + Initial import in CVS
  328. }