2
0

unitdiff.pp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. {
  2. UnitDiff Copyright (C) 2004 by the Free Pascal team
  3. Show differences between unit interfaces.
  4. See the file COPYING, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. }
  10. program unitdiff;
  11. uses
  12. SysUtils, Classes, Gettext,
  13. dGlobals, PasTree, PParser,PScanner;
  14. resourcestring
  15. SIdentifiersIn = 'Identifiers in file "%s"';
  16. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  17. SErrNoInputFile = 'No input file specified';
  18. SWarnAssumingList = 'Only one input file specified. Assuming --list option.';
  19. SExtraIdentifier = 'Extra identifier in file "%s" : Name: %s';
  20. SExtraTypedIdentifier = 'Extra identifier in file "%s" : Type %s, Name: %s';
  21. SIdenticalUnits = 'Unit interfaces are identical.';
  22. type
  23. TCmdLineAction = (actionHelp, actionDiff,ActionList);
  24. TSkelEngine = class(TFPDocEngine)
  25. public
  26. FList: TStringList;
  27. Constructor Create;
  28. Destructor Destroy;override;
  29. function CreateElement(AClass: TPTreeElement; const AName: String;
  30. AParent: TPasElement; AVisibility :TPasMemberVisibility;
  31. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
  32. end;
  33. Constructor TSkelEngine.Create;
  34. begin
  35. Inherited Create;
  36. FList:=TStringList.Create;
  37. end;
  38. Destructor TSkelEngine.Destroy;
  39. begin
  40. FreeAndNil(FList);
  41. Inherited;
  42. end;
  43. const
  44. CmdLineAction: TCmdLineAction = actionDiff;
  45. OSTarget: String = {$I %FPCTARGETOS%};
  46. CPUTarget: String = {$I %FPCTARGETCPU%};
  47. var
  48. InputFile1,
  49. InputFile2 : String;
  50. DocLang: String;
  51. Engine1,
  52. Engine2: TSkelEngine;
  53. SparseList,
  54. DisableArguments,
  55. DisableProtected,
  56. DisablePrivate,
  57. DisableFunctionResults: Boolean;
  58. OutputName: String;
  59. f: Text;
  60. function TSkelEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  61. AParent: TPasElement; AVisibility : TPasMemberVisibility;
  62. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  63. Function ExamineThisNode(APasElement : TPasElement) : Boolean;
  64. begin
  65. Result:=Assigned(AParent) and (Length(AName) > 0) and
  66. (not DisableArguments or (APasElement.ClassType <> TPasArgument)) and
  67. (not DisableFunctionResults or (APasElement.ClassType <> TPasResultElement)) and
  68. (not DisablePrivate or (AVisibility<>visPrivate)) and
  69. (not DisableProtected or (AVisibility<>visProtected));
  70. end;
  71. begin
  72. Result := AClass.Create(AName, AParent);
  73. if AClass.InheritsFrom(TPasModule) then
  74. CurModule := TPasModule(Result)
  75. else if ExamineThisNode(Result) then
  76. Flist.AddObject(Result.FullName,Result);
  77. end;
  78. Procedure Usage;
  79. begin
  80. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options] file1 file2');
  81. Writeln('Where [options] is one or more of :');
  82. Writeln(' --disable-arguments Do not check function arguments.');
  83. Writeln(' --disable-private Do not check class private fields.');
  84. Writeln(' --disable-protected Do not check class protected fields.');
  85. Writeln(' --help Emit help.');
  86. Writeln(' --input=cmdline Input file to create skeleton for.');
  87. Writeln(' Use options are as for compiler.');
  88. Writeln(' --lang=language Use selected language.');
  89. Writeln(' --list List identifiers instead of making a diff');
  90. Writeln(' --output=filename Send output to file.');
  91. Writeln(' --sparse Sparse list/diff (skip type identification)');
  92. end;
  93. procedure ParseOption(const s: String);
  94. var
  95. i: Integer;
  96. Cmd, Arg: String;
  97. begin
  98. if (s = '-h') or (s = '--help') then
  99. CmdLineAction := actionHelp
  100. else if s = '--disable-arguments' then
  101. DisableArguments := True
  102. else if s = '--disable-private' then
  103. DisablePrivate := True
  104. else if s = '--sparse' then
  105. SparseList := True
  106. else if s = '--disable-protected' then
  107. begin
  108. DisableProtected := True;
  109. DisablePrivate :=True;
  110. end
  111. else
  112. begin
  113. i := Pos('=', s);
  114. if i > 0 then
  115. begin
  116. Cmd := Copy(s, 1, i - 1);
  117. Arg := Copy(s, i + 1, Length(s));
  118. end
  119. else
  120. begin
  121. Cmd := s;
  122. SetLength(Arg, 0);
  123. end;
  124. if (Cmd = '-l') or (Cmd = '--lang') then
  125. DocLang := Arg
  126. else if (Cmd = '-o') or (Cmd = '--output') then
  127. OutputName := Arg
  128. else
  129. if (length(cmd)>0) and (cmd[1]='-') then
  130. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]))
  131. else if (InputFile1='') then
  132. InputFile1:=Cmd
  133. else if (InputFile2='') then
  134. InputFile2:=Cmd
  135. else
  136. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  137. end;
  138. end;
  139. procedure ParseCommandLine;
  140. Const
  141. {$IFDEF Unix}
  142. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  143. {$ELSE}
  144. MoFileTemplate ='intl/makeskel.%s.mo';
  145. {$ENDIF}
  146. var
  147. MOFilename: string;
  148. i: Integer;
  149. begin
  150. CmdLineAction := actionDiff;
  151. DocLang:='';
  152. SparseList:=False;
  153. for i := 1 to ParamCount do
  154. ParseOption(ParamStr(i));
  155. If (DocLang<>'') then
  156. begin
  157. MOFilename:=Format(MOFileTemplate,[DocLang]);
  158. if FileExists(MOFilename) then
  159. gettext.TranslateResourceStrings(MoFileName)
  160. else
  161. writeln('NOTE: unable to find tranlation file ',MOFilename);
  162. // Translate internal documentation strings
  163. TranslateDocStrings(DocLang);
  164. end;
  165. if (cmdLineAction<>ActionHelp) and (InputFile1='') and (InputFile2='') then
  166. begin
  167. Writeln(StdErr,SErrNoInputFile);
  168. cmdLineAction := actionHelp;
  169. end else if (InputFile2='') and (CmdLineAction<>ActionList) then
  170. begin
  171. Writeln(StdErr,SWarnAssumingList);
  172. CmdLineAction:=ActionList;
  173. end;
  174. end;
  175. Function GetTypeDescription(El : TPasElement) : String;
  176. begin
  177. If Assigned(El) then
  178. Result:=El.ElementTypeName
  179. else
  180. Result:='(unknown)';
  181. end;
  182. Procedure ListIdentifiers(Fn : String; List : TStrings);
  183. Var
  184. I : Integer;
  185. begin
  186. Writeln(f,Format(SIdentifiersIn,[FN]));
  187. For I:=0 to List.Count-1 do
  188. begin
  189. If Not SparseList then
  190. Write(GetTypeDescription(TPasElement(List.Objects[i])),' : ');
  191. Writeln(List[i]);
  192. end;
  193. end;
  194. Procedure WriteExtra(FN,Id : String; El: TPaselement);
  195. begin
  196. If SparseList then
  197. Writeln(F,Format(SExtraIdentifier,[FN,ID]))
  198. else
  199. Writeln(F,Format(SExtraTypedIdentifier,[FN,GetTypeDescription(El),ID]));
  200. end;
  201. Procedure DoExtra(FN : String; L : TStrings);
  202. Var
  203. I,Len : Integer;
  204. S : String;
  205. begin
  206. I:=0;
  207. While (I<L.Count) do
  208. begin
  209. WriteExtra(FN,L[I],TPasElement(L.Objects[I]));
  210. // Delete possible subelements.
  211. S:=L[I]+'.';
  212. Len:=Length(S);
  213. While (I+1<L.Count) and (CompareText(Copy(L[I+1],1,Len),S)=0) do
  214. L.Delete(I+1);
  215. Inc(I);
  216. end;
  217. end;
  218. Procedure DiffIdentifiers(List1,List2 : TStrings);
  219. Var
  220. L1,L2 : TStrings;
  221. I,J : Integer;
  222. begin
  223. L1:=List1;
  224. L2:=List2;
  225. If List2.Count>List1.Count then
  226. begin
  227. L1:=List2;
  228. L2:=List1;
  229. end;
  230. // Remove all common elements.
  231. For I:=L1.Count-1 downto 0 do
  232. begin
  233. J:=L2.IndexOf(L1[i]);
  234. If (J<>-1) then
  235. begin
  236. L1.Delete(I);
  237. L2.Delete(J);
  238. end;
  239. end;
  240. If (List1.Count=0) and (List2.Count=0) then
  241. Writeln(F,SIdenticalUnits)
  242. else
  243. begin
  244. DoExtra(InputFile1,List1);
  245. DoExtra(InputFile2,List2);
  246. end;
  247. end;
  248. begin
  249. ParseCommandLine;
  250. if CmdLineAction = actionHelp then
  251. Usage
  252. else
  253. begin
  254. Assign(f, OutputName);
  255. Rewrite(f);
  256. Try
  257. Engine1:=TSkelEngine.Create;
  258. Try
  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. except
  284. on e: eparsererror do
  285. writeln(format('%s(%d,%d): Error: %s',[e.Filename,e.Row,e.Column,e.Message]));
  286. end;
  287. Finally
  288. Engine1.Free;
  289. end;
  290. Finally
  291. Close(f);
  292. end;
  293. end;
  294. end.