File: testfpcsrcunitrules.lpr

package info (click to toggle)
lazarus 2.0.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 214,460 kB
  • sloc: pascal: 1,862,622; xml: 265,709; cpp: 56,595; sh: 3,008; java: 609; makefile: 535; perl: 297; sql: 222; ansic: 137
file content (447 lines) | stat: -rw-r--r-- 14,736 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
{
 ***************************************************************************
 *                                                                         *
 *   This source is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This code is distributed in the hope that it will be useful, but      *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   General Public License for more details.                              *
 *                                                                         *
 *   A copy of the GNU General Public License is available on the World    *
 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 *   obtain it by writing to the Free Software Foundation,                 *
 *   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.        *
 *                                                                         *
 ***************************************************************************

  Author: Mattias Gaertner

  Abstract:
    Write all duplicate ppu files and all duplicate unit source files.
}
program TestFPCSrcUnitRules;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils, CustApp, Laz_AVL_Tree,
  // LazUtils
  LazFileUtils, AvgLvlTree,
  // CodeTools
  FileProcs, CodeToolManager, DefineTemplates, CodeToolsConfig;

const
  ConfigFilename = 'codetools.config';
type

  { TTestFPCSourceUnitRules }

  TTestFPCSourceUnitRules = class(TCustomApplication)
  private
    FCheckUnitName: string;
  protected
    procedure DoRun; override;
  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
    procedure WriteHelp; virtual;
    procedure Error(Msg: string; DoWriteHelp: Boolean);
    procedure WriteCompilerInfo(ConfigCache: TPCTargetConfigCache);
    procedure WriteNonExistingPPUPaths(ConfigCache: TPCTargetConfigCache);
    procedure WriteDuplicatesInPPUPath(ConfigCache: TPCTargetConfigCache);
    procedure WriteMissingPPUSources(UnitSet: TFPCUnitSetCache);
    procedure WriteDuplicateSources(UnitSet: TFPCUnitSetCache);
    procedure WriteUnitReport(UnitSet: TFPCUnitSetCache; const AnUnitName: string);
    property CheckUnitName: string read FCheckUnitName write FCheckUnitName;
  end;

{ TMyApplication }

procedure TTestFPCSourceUnitRules.DoRun;
var
  ErrorMsg: String;
  CompilerFilename: String;
  TargetOS: String;
  TargetCPU: String;
  FPCSrcDir: String;
  UnitSet: TFPCUnitSetCache;
  ConfigCache: TPCTargetConfigCache;
  Options: TCodeToolsOptions;
begin
  // quick check parameters
  ErrorMsg:=CheckOptions('hc:T:P:F:u:','help compiler: targetos: targetcpu: fpcsrcdir: checkunit:');
  if ErrorMsg<>'' then begin
    ShowException(Exception.Create(ErrorMsg));
    Terminate;
    Exit;
  end;

  // parse parameters
  if HasOption('h','help') then begin
    WriteHelp;
    Halt;
  end;

  if not HasOption('F','fpcsrcdir') then
    Error('fpc source directory missing',true);

  if HasOption('c','compiler') then begin
    CompilerFilename:=GetOptionValue('c','compiler');
    CompilerFilename:=CleanAndExpandFilename(CompilerFilename);
  end else begin
    CompilerFilename:=GetDefaultCompilerFilename;
    CompilerFilename:=SearchFileInPath(CompilerFilename,'',
                    GetEnvironmentVariable('PATH'), PathSeparator,ctsfcDefault);
  end;
  TargetOS:=GetOptionValue('T','targetos');
  TargetCPU:=GetOptionValue('P','targetcpu');
  FPCSrcDir:=GetOptionValue('F','fpcsrcdir');
  FPCSrcDir:=CleanAndExpandDirectory(FPCSrcDir);
  CheckUnitName:=GetOptionValue('u','checkunit');

  if not FileExistsUTF8(CompilerFilename) then
    Error('compiler file not found: '+CompilerFilename,false);
  if not DirPathExists(FPCSrcDir) then
    Error('FPC source directory not found: '+FPCSrcDir,false);

  Options:=TCodeToolsOptions.Create;
  Options.InitWithEnvironmentVariables;
  if FileExistsUTF8(ConfigFilename) then
    Options.LoadFromFile(ConfigFilename);
  Options.FPCPath:=CompilerFilename;
  Options.FPCOptions:='';
  Options.TargetOS:=TargetOS;
  Options.TargetProcessor:=TargetCPU;
  Options.FPCSrcDir:=FPCSrcDir;

  CodeToolBoss.Init(Options);

  UnitSet:=CodeToolBoss.CompilerDefinesCache.FindUnitSet(CompilerFilename,
                                          TargetOS,TargetCPU,'',FPCSrcDir,true);
  UnitSet.Init;

  Options.SaveToFile(ConfigFilename);
  Options.Free;

  ConfigCache:=UnitSet.GetConfigCache(false);
  writeln('FPCSrcDir=',UnitSet.FPCSourceDirectory);
  WriteCompilerInfo(ConfigCache);
  WriteNonExistingPPUPaths(ConfigCache);
  WriteDuplicatesInPPUPath(ConfigCache);
  WriteMissingPPUSources(UnitSet);
  WriteDuplicateSources(UnitSet);
  if CheckUnitName<>'' then
    WriteUnitReport(UnitSet,CheckUnitName);

  // stop program loop
  Terminate;
end;

constructor TTestFPCSourceUnitRules.Create(TheOwner: TComponent);
begin
  inherited Create(TheOwner);
  StopOnException:=True;
end;

destructor TTestFPCSourceUnitRules.Destroy;
begin
  inherited Destroy;
end;

procedure TTestFPCSourceUnitRules.WriteHelp;
begin
  writeln('Usage: ',ExeName,' -h');
  writeln;
  writeln('  -c <compiler file name>, --compiler=<compiler file name>');
  writeln('         Default is to use environment variable PP.');
  writeln('         If this is not set, search for '+GetDefaultCompilerFilename);
  writeln;
  writeln('  -T <target OS>, --targetos=<target OS>');
  writeln('         Default is to use environment variable FPCTARGET.');
  writeln('         If this is not set, use the default of the compiler.');
  writeln;
  writeln('  -P <target CPU>, --targetcpu=<target CPU>');
  writeln('         Default is to use environment variable FPCTARGETCPU.');
  writeln('         If this is not set, use the default of the compiler.');
  writeln;
  writeln('  -F <FPC source directory>, --fpcsrcdir=<FPC source directory>');
  writeln('         Default is to use environment variable FPCDIR.');
  writeln('         There is no default.');
  writeln;
  writeln('  -u <unit name>, --checkunit=<unit name>');
  writeln('         Write a detailed report about this unit.');
end;

procedure TTestFPCSourceUnitRules.Error(Msg: string; DoWriteHelp: Boolean);
begin
  writeln('Error: ',Msg);
  if DoWriteHelp then begin
    writeln;
    WriteHelp;
  end;
  Halt;
end;

procedure TTestFPCSourceUnitRules.WriteCompilerInfo(
  ConfigCache: TPCTargetConfigCache);
var
  i: Integer;
  CfgFile: TPCConfigFileState;
begin
  writeln('Compiler=',ConfigCache.Compiler);
  writeln('TargetOS=',ConfigCache.TargetOS);
  writeln('TargetCPU=',ConfigCache.TargetCPU);
  writeln('Options=',ConfigCache.CompilerOptions);
  writeln('RealCompiler=',ConfigCache.RealCompiler);
  writeln('RealTargetOS=',ConfigCache.RealTargetOS);
  writeln('RealTargetCPU=',ConfigCache.RealTargetCPU);
  writeln('RealCompilerInPATH=',ConfigCache.RealCompilerInPath);
  if ConfigCache.ConfigFiles<>nil then begin
    for i:=0 to ConfigCache.ConfigFiles.Count-1 do begin
      CfgFile:=ConfigCache.ConfigFiles[i];
      writeln('Config=',CfgFile.Filename,' Exists=',CfgFile.FileExists);
    end;
  end;
  if (ConfigCache.UnitPaths=nil) or (ConfigCache.UnitPaths.Count=0) then
    writeln('WARNING: no ppu search paths')
  else
    writeln('Number of PPU search paths=',ConfigCache.UnitPaths.Count);
end;

procedure TTestFPCSourceUnitRules.WriteNonExistingPPUPaths(
  ConfigCache: TPCTargetConfigCache);
var
  SearchPaths: TStrings;
  i: Integer;
  Dir: String;
begin
  SearchPaths:=ConfigCache.UnitPaths;
  if SearchPaths=nil then exit;
  for i:=0 to SearchPaths.Count-1 do begin
    Dir:=CleanAndExpandDirectory(SearchPaths[i]);
    if not DirPathExists(Dir) then begin
      writeln('WARNING: ppu search path does not exist: ',SearchPaths[i]);
    end;
  end;
end;

procedure TTestFPCSourceUnitRules.WriteDuplicatesInPPUPath(
  ConfigCache: TPCTargetConfigCache);
var
  i: Integer;
  Directory: String;
  FileInfo: TSearchRec;
  ShortFilename: String;
  Filename: String;
  Ext: String;
  LowerUnitname: String;
  SearchPaths: TStrings;
  IsSource: Boolean;
  IsPPU: Boolean;
  SourceFiles: TStringList;
  Units: TStringToStringTree;
  Item: PStringToStringItem;
  Node: TAVLTreeNode;
begin
  SearchPaths:=ConfigCache.UnitPaths;
  if SearchPaths=nil then exit;
  SourceFiles:=TStringList.Create;
  Units:=TStringToStringTree.Create(false);
  for i:=SearchPaths.Count-1 downto 0 do begin
    Directory:=CleanAndExpandDirectory(SearchPaths[i]);
    if FindFirstUTF8(Directory+FileMask,faAnyFile,FileInfo)=0 then begin
      repeat
        ShortFilename:=FileInfo.Name;
        if (ShortFilename='') or (ShortFilename='.') or (ShortFilename='..') then
          continue;
        Filename:=Directory+ShortFilename;
        Ext:=LowerCase(ExtractFileExt(ShortFilename));
        IsSource:=(Ext='.pas') or (Ext='.pp') or (Ext='.p');
        IsPPU:=(Ext='.ppu');
        if IsSource then
          SourceFiles.Add(Filename);
        if IsSource or IsPPU then begin
          LowerUnitname:=lowercase(ExtractFileNameOnly(Filename));
          if Units.Contains(LowerUnitname) then
            Units[LowerUnitname]:=Units[LowerUnitname]+';'+Filename
          else
            Units[LowerUnitname]:=Filename;
        end;
      until FindNextUTF8(FileInfo)<>0;
    end;
    FindCloseUTF8(FileInfo);
  end;
  if SourceFiles.Count<>0 then begin
    // source files in PPU search path
    writeln;
    writeln('WARNING: source files found in PPU search paths:');
    writeln(SourceFiles.Text);
    writeln;
  end;
  Node:=Units.Tree.FindLowest;
  i:=0;
  while Node<>nil do begin
    Item:=PStringToStringItem(Node.Data);
    Filename:=Item^.Value;
    if System.Pos(';',Filename)>0 then begin
      // duplicate units
      if i=0 then writeln;
      inc(i);
      writeln('HINT: duplicate unit in PPU path: '+Filename);
    end;
    Node:=Units.Tree.FindSuccessor(Node);
  end;
  if i>0 then writeln;
  Units.Free;
  SourceFiles.Free;
end;

procedure TTestFPCSourceUnitRules.WriteMissingPPUSources(
  UnitSet: TFPCUnitSetCache);
var
  UnitToSrc: TStringToStringTree;
  Node: TAVLTreeNode;
  Item: PStringToStringItem;
  ConfigCache: TPCTargetConfigCache;
  aUnitName: String;
  Cnt: Integer;
  Filename: String;
  SourceCache: TFPCSourceCache;
  i: Integer;
  SrcRules: TFPCSourceRules;
  aTree: TStringToStringTree;
begin
  UnitToSrc:=UnitSet.GetUnitToSourceTree(false);
  ConfigCache:=UnitSet.GetConfigCache(false);
  SourceCache:=UnitSet.GetSourceCache(false);
  if ConfigCache.Units<>nil then begin
    Cnt:=0;
    Node:=ConfigCache.Units.Tree.FindLowest;
    while Node<>nil do begin
      Item:=PStringToStringItem(Node.Data);
      aUnitName:=Item^.Name;
      Filename:=Item^.Value;
      if CompareFileExt(Filename,'ppu',false)=0 then begin
        // a ppu in the PPU search path
        if UnitToSrc[aUnitName]='' then begin
          inc(Cnt);
          if Cnt=1 then writeln;
          writeln('WARNING: no source found for PPU file: '+Filename);
          for i:=0 to SourceCache.Files.Count-1 do begin
            if SysUtils.CompareText(ExtractFileNameOnly(SourceCache.Files[i]),aUnitName)=0
            then begin
              writeln('      Candidate: ',SourceCache.Files[i]);
              SrcRules:=UnitSet.GetSourceRules(false);
              aTree:=GatherUnitsInFPCSources(SourceCache.Files,
                ConfigCache.RealTargetOS,ConfigCache.RealTargetCPU,nil,
                SrcRules,aUnitName);
              aTree.Free;
            end;
          end;
        end;
      end;
      Node:=ConfigCache.Units.Tree.FindSuccessor(Node);
    end;
    if Cnt>0 then writeln;
  end;
end;

procedure TTestFPCSourceUnitRules.WriteDuplicateSources(
  UnitSet: TFPCUnitSetCache);
var
  SrcDuplicates: TStringToStringTree;
  Node: TAVLTreeNode;
  Cnt: Integer;
  Item: PStringToStringItem;
  aUnitName: String;
  Files: String;
  Units: TStringToStringTree;
  PPUFile: string;
begin
  SrcDuplicates:=UnitSet.GetSourceDuplicates(false);
  if SrcDuplicates=nil then exit;
  Units:=UnitSet.GetConfigCache(false).Units;

  // first list all duplicates with a ppu file (important)
  if Units<>nil then begin
    Cnt:=0;
    Node:=SrcDuplicates.Tree.FindLowest;
    while Node<>nil do begin
      Item:=PStringToStringItem(Node.Data);
      aUnitName:=Item^.Name;
      Files:=Item^.Value;
      PPUFile:=Units[aUnitName];
      if CompareFileExt(PPUFile,'ppu',false)=0 then begin
        if Cnt=0 then writeln;
        inc(Cnt);
        writeln('WARNING: duplicate source file for ppu ',aUnitName,' files=',Files);
      end;
      Node:=SrcDuplicates.Tree.FindSuccessor(Node);
    end;
    if Cnt>0 then writeln;
  end;

  // then list all duplicates without a ppu file (unimportant)
  Cnt:=0;
  Node:=SrcDuplicates.Tree.FindLowest;
  while Node<>nil do begin
    Item:=PStringToStringItem(Node.Data);
    aUnitName:=Item^.Name;
    Files:=Item^.Value;
    if (Units=nil) or (Units[aUnitName]='') then begin
      if Cnt=0 then writeln;
      inc(Cnt);
      writeln('HINT: duplicate source files: unit=',aUnitName,' files=',Files);
    end;
    Node:=SrcDuplicates.Tree.FindSuccessor(Node);
  end;
  if Cnt>0 then writeln;
end;

procedure TTestFPCSourceUnitRules.WriteUnitReport(UnitSet: TFPCUnitSetCache;
  const AnUnitName: string);
var
  ConfigCache: TPCTargetConfigCache;
  PPUFile: String;
  SourceCache: TFPCSourceCache;
  aTree: TStringToStringTree;
  SrcRules: TFPCSourceRules;
begin
  writeln;
  writeln('Unit report for ',AnUnitName);
  ConfigCache:=UnitSet.GetConfigCache(false);

  // in ppu search path
  PPUFile:='';
  if ConfigCache.Units<>nil then
    PPUFile:=ConfigCache.Units[AnUnitName];
  if PPUFile='' then
    writeln('  WARNING: ',AnUnitName,' is not in PPU search path')
  else if CompareFileExt(PPUFile,'ppu',false)<>0 then
    writeln('  WARNING: fpc ppu search path has a source and not a ppu for ',AnUnitName,': ',PPUFile)
  else
    writeln('  in PPU search path: ',PPUFile);

  SourceCache:=UnitSet.GetSourceCache(false);
  SrcRules:=UnitSet.GetSourceRules(false);
  if SourceCache.Files<>nil then begin
    aTree:=GatherUnitsInFPCSources(SourceCache.Files,
      ConfigCache.RealTargetOS,ConfigCache.RealTargetCPU,nil,
      SrcRules,AnUnitName);
    aTree.Free;
  end;
end;

var
  Application: TTestFPCSourceUnitRules;
begin
  Application:=TTestFPCSourceUnitRules.Create(nil);
  Application.Title:='TestFPCSrcUnitRules';
  Application.Run;
  Application.Free;
end.