unitdiff.pp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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. FList.Sorted:=True;
  39. end;
  40. Destructor TSkelEngine.Destroy;
  41. begin
  42. FreeAndNil(FList);
  43. Inherited;
  44. end;
  45. const
  46. CmdLineAction: TCmdLineAction = actionDiff;
  47. OSTarget: String = {$I %FPCTARGETOS%};
  48. CPUTarget: String = {$I %FPCTARGETCPU%};
  49. var
  50. InputFile1,
  51. InputFile2 : String;
  52. DocLang: String;
  53. Engine1,
  54. Engine2: TSkelEngine;
  55. SparseList,
  56. DisableArguments,
  57. DisableProtected,
  58. DisablePrivate,
  59. DisableFunctionResults: Boolean;
  60. OutputName: String;
  61. f: Text;
  62. function TSkelEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  63. AParent: TPasElement; AVisibility : TPasMemberVisibility;
  64. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  65. Function ExamineThisNode(APasElement : TPasElement) : Boolean;
  66. begin
  67. Result:=Assigned(AParent) and (Length(AName) > 0) and
  68. (not DisableArguments or (APasElement.ClassType <> TPasArgument)) and
  69. (not DisableFunctionResults or (APasElement.ClassType <> TPasResultElement)) and
  70. (not DisablePrivate or (AVisibility<>visPrivate)) and
  71. (not DisableProtected or (AVisibility<>visProtected));
  72. end;
  73. begin
  74. Result := AClass.Create(AName, AParent);
  75. if AClass.InheritsFrom(TPasModule) then
  76. CurModule := TPasModule(Result)
  77. else if ExamineThisNode(Result) then
  78. Flist.AddObject(Result.FullName,Result);
  79. end;
  80. Procedure Usage;
  81. begin
  82. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options] file1 file2');
  83. Writeln('Where [options] is one or more of :');
  84. Writeln(' --disable-arguments Do not check function arguments.');
  85. Writeln(' --disable-private Do not check class private fields.');
  86. Writeln(' --disable-protected Do not check class protected fields.');
  87. Writeln(' --help Emit help.');
  88. Writeln(' --input=cmdline Input file to create skeleton for.');
  89. Writeln(' Use options are as for compiler.');
  90. Writeln(' --lang=language Use selected language.');
  91. Writeln(' --list List identifiers instead of making a diff');
  92. Writeln(' --output=filename Send output to file.');
  93. Writeln(' --sparse Sparse list/diff (skip type identification)');
  94. end;
  95. procedure ParseOption(const s: String);
  96. var
  97. i: Integer;
  98. Cmd, Arg: String;
  99. begin
  100. if (s = '-h') or (s = '--help') then
  101. CmdLineAction := actionHelp
  102. else if s = '--disable-arguments' then
  103. DisableArguments := True
  104. else if s = '--disable-private' then
  105. DisablePrivate := True
  106. else if s = '--sparse' then
  107. SparseList := True
  108. else if s = '--disable-protected' then
  109. begin
  110. DisableProtected := True;
  111. DisablePrivate :=True;
  112. end
  113. else
  114. begin
  115. i := Pos('=', s);
  116. if i > 0 then
  117. begin
  118. Cmd := Copy(s, 1, i - 1);
  119. Arg := Copy(s, i + 1, Length(s));
  120. end
  121. else
  122. begin
  123. Cmd := s;
  124. SetLength(Arg, 0);
  125. end;
  126. if (Cmd = '-l') or (Cmd = '--lang') then
  127. DocLang := Arg
  128. else if (Cmd = '-o') or (Cmd = '--output') then
  129. OutputName := Arg
  130. else
  131. if (length(cmd)>0) and (cmd[1]='-') then
  132. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]))
  133. else if (InputFile1='') then
  134. InputFile1:=Cmd
  135. else if (InputFile2='') then
  136. InputFile2:=Cmd
  137. else
  138. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  139. end;
  140. end;
  141. procedure ParseCommandLine;
  142. Const
  143. {$IFDEF Unix}
  144. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  145. {$ELSE}
  146. MoFileTemplate ='intl/makeskel.%s.mo';
  147. {$ENDIF}
  148. var
  149. MOFilename: string;
  150. i: Integer;
  151. begin
  152. CmdLineAction := actionDiff;
  153. DocLang:='';
  154. SparseList:=False;
  155. for i := 1 to ParamCount do
  156. ParseOption(ParamStr(i));
  157. If (DocLang<>'') then
  158. begin
  159. MOFilename:=Format(MOFileTemplate,[DocLang]);
  160. if FileExists(MOFilename) then
  161. gettext.TranslateResourceStrings(MoFileName)
  162. else
  163. writeln('NOTE: unable to find tranlation file ',MOFilename);
  164. // Translate internal documentation strings
  165. TranslateDocStrings(DocLang);
  166. end;
  167. if (cmdLineAction<>ActionHelp) and (InputFile1='') and (InputFile2='') then
  168. Writeln(StdErr,SErrNoInputFile)
  169. 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. Engine1.SetPackageName('diff'); // do not localize
  260. ParseSource(Engine1, InputFile1, OSTarget, CPUTarget);
  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. If cmdLineAction=ActionList then
  268. begin
  269. ListIdentifiers(InputFile1,Engine1.FList);
  270. ListIdentifiers(InputFile2,Engine2.FList);
  271. end
  272. else
  273. DiffIdentifiers(Engine1.Flist,Engine2.Flist);
  274. finally
  275. Engine2.Free;
  276. end;
  277. end
  278. else
  279. ListIdentifiers(InputFile1,Engine1.FList);
  280. Finally
  281. Engine1.Free;
  282. end;
  283. Finally
  284. Close(f);
  285. end;
  286. end;
  287. end.
  288. {
  289. $Log$
  290. Revision 1.2 2004-11-14 21:20:31 michael
  291. + Changed copyright
  292. Revision 1.1 2004/11/14 21:18:58 michael
  293. + Initial check-in
  294. Revision 1.13 2004/09/13 16:04:52 peter
  295. * fix nested for-loop with same index
  296. Revision 1.12 2004/08/29 15:32:41 michael
  297. + More intelligent handling of nodes. Do not write unused nodes.
  298. Revision 1.11 2004/08/28 18:18:59 michael
  299. + Do not write descr nodes for module when updating
  300. Revision 1.10 2004/08/28 18:15:14 michael
  301. + Check whether outputfile not in inputfilenames
  302. Revision 1.9 2004/08/28 18:04:06 michael
  303. + Added update mode
  304. Revision 1.8 2004/08/25 07:16:43 michael
  305. + Improved translation handling
  306. Revision 1.7 2004/08/24 14:48:25 michael
  307. + Translate now called correctly...
  308. Revision 1.6 2004/05/01 20:13:40 marco
  309. * got fed up with exceptions on file not found. Fileresolver now raises a
  310. EFileNotFound error, and makeskel catches and exists gracefully
  311. Revision 1.5 2003/11/28 12:51:37 sg
  312. * Added support for source references
  313. Revision 1.4 2003/09/02 13:26:47 mattias
  314. MG: makeskel now ignores missing translation file
  315. Revision 1.3 2003/05/07 16:31:32 sg
  316. * Fixed a severe memory corruption problem on termination
  317. Revision 1.2 2003/03/28 13:01:36 michael
  318. + Patch from Charlie/iNQ to work with new scanner/parser
  319. Revision 1.1 2003/03/17 23:03:20 michael
  320. + Initial import in CVS
  321. }