Solved

Finding unused units in Delphi

Posted on 2000-04-17
35
1,808 Views
Last Modified: 2011-09-20
I need a tool to find unused units in the uses clauses of a large Delphi 4 app. I have all the source.

I have tried SourceCoder, but without luck.
0
Comment
Question by:miv
  • 9
  • 7
  • 6
  • +7
35 Comments
 
LVL 3

Expert Comment

by:Alisher_N
Comment Utility
I think Delphi removes unused entries by itself...

why do you think there is something extra in the list ?
0
 
LVL 2

Expert Comment

by:craig_capel
Comment Utility
Yeah i agree, what would be nice is if the compilers would just take buts out of the units and not the whole lot..... (i may be wrong?)
0
 
LVL 3

Expert Comment

by:Alisher_N
Comment Utility
unit consists of:

1)public variables declared in units
2)public functions & procs
3)init part
4)local variables
5)local functions & procs

if you use at least one function from unit Delphi includes to your project
1) 3) and ONLY functions you actually use

if you don't use any functions - nothing nothing included (it makes no sence)

0
 
LVL 1

Author Comment

by:miv
Comment Utility
Alisher_N:

This is a 400.000 line project, with apx. 1000 units and there is no "visual programming" (RAD) and therefore no dfm files involved.

The least of my problems is the Delphi units...which is not even added automaticly when you create everything at run-time and not at design time.
0
 
LVL 3

Expert Comment

by:Alisher_N
Comment Utility
it doesn't matter if they 'native' Delphi units or you own - it is checking aall your 1000 units and removes unsed code or whole unit from project... I am not quite sure what is exactly your problem then.
0
 
LVL 4

Expert Comment

by:jeurk
Comment Utility
hello,
what about that beta stuff:
The current version (0.0.1.5) only takes care of unit interfaces
http://www.multimania.com/vincentmahon/out.htm

Maybe you can give it a try ?
....
0
 
LVL 1

Expert Comment

by:xsoft
Comment Utility
What are you trying to achieve?

a:Kick out the unused units from the project?
b:Reduce the Uses clauses in all units to the minimum needed?
c:Both?

Do all the Uses clauses in the whole project look like the include unneccessary references?

Did I understand you right: the whole project consists only of *.pas files?

Thomas
0
 
LVL 1

Expert Comment

by:AttarSoftware
Comment Utility
Can't you write a program to get a directory listing of all .pas files in your project's location, then write some code to parse all of these files for the key word 'uses' (up until the ';'), and compares these filenames with those in the file listing...  Any that remain unmatched in the file listing, are unused...

Well, might be unused at least...

Tim.
0
 

Expert Comment

by:aacrg
Comment Utility
Listening...
0
 
LVL 1

Author Comment

by:miv
Comment Utility
jeurk: The OUT tool is exactly what I need. But the beta is not working...you´ll get the points, if Vince can fixe the errors :-)

xsoft: Both, yes, yes (expect for some res files).

AttarSoftware: Not a solution. If that was what i wanted, i could just remove all dcu files and build the project. The files with no dcu twin afterwards would be unused.

0
 
LVL 4

Expert Comment

by:jeurk
Comment Utility
I've asked him for sources.
I'll tell you when I get something.
0
 
LVL 1

Expert Comment

by:AttarSoftware
Comment Utility
Ahhhh, I see you problem now :O)

Please excuse me :O)

Tim.
0
 
LVL 1

Author Comment

by:miv
Comment Utility
Adjusted points from 500 to 666
0
 
LVL 1

Expert Comment

by:DValery
Comment Utility
Listening...
0
 
LVL 1

Expert Comment

by:xsoft
Comment Utility
Maybe you should take a look at:
www.delphicase.com/

ModelMaker 5 - the Borland Delphi Productivity / CASE tool

(Concerning the size of your project it seems that a Case tool could be helpful anyway.)
This case tool also includes a Unit dependency analyser that you can use as an independent tool.

On the page www.delphicase.com/Tips.htm they describe the tool like this.

"Unit dependency analyser
In main menu Tools|Uunit Dependencies you'll find the unit dependency analyser. This allows you to analyse uses relations and dependencies (inverse uses relations). Inputs for the analyser are: a searchpath (top left list) and a set of 'root files'. The root files are the top-level heirarchy files that you want to analyse. The analyser scannes all root files and the files they contain in the interface 'uses' and implementation 'uses' sections. Root files can be a single .dpr or all files in directory such as all files in Delphi\VCL\Source. The use of root files gives the flexibility to analyse not only dpr's but also a set of units. Building the search path and the roots is done most conviniently by drag-drop from the windows- (NT) explorer.

After the search path and root files have been defined and the 'analyse' button has been clicked, a list of all scanned units is displayed in which a unit can be selected for analyses. This list may be filtered for 'found files only' which omits nay files which were not found on the search path, or 'selected directory only' which displays only thoses files found in the directory selected in the searchpath list.

On the right there is a tabbed notebook which shows the uses and dependencies trees. These trees contain the main analyses information. Different bitmaps in the trees show whether a unit is used in the interface or implementation section of a unit. Left from the trees a list shows all units (indirectly) used by the analysed unit.

The unit analyser is an independent tool which has no relations with other parts of ModelMaker."

I am not using this tool at the moment (but I might be soon...) so I cannot tell you how the analyzer works. But you can download a trial version with which you should be able to find out.
As I read by someone else in the newsgroups (borland.public.delphi.oodesign) the respond quite fast to questions and to suggestions from users and I think also from potential users so I would imagine, that this link would point you in the right direction.

Thomas
0
 
LVL 12

Expert Comment

by:rwilson032697
Comment Utility
Listening
0
 
LVL 1

Author Comment

by:miv
Comment Utility
xsoft: ModelMaker seems like a nice tool and it can analyze our unit structure.
But it can´t tell me which units in the uses clause are unneccesary...
I´ll contact the creators and check if they have a solution.

thanks for the help so far,
  Michael
0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 1

Author Comment

by:miv
Comment Utility
Answer from the ModelMaker guys:
(Fast support! :-)

"ModelMaker has a unit dependency analyzer which will: -1- scan units "uses"
clauses and display them in depedency and uses trees, and -2- instantly
visualize these trees (from mm5.16 onwards)

ModelMaker will not do full parsing of code to see which units are actually
referenced which is needed for your question.

Gerrit Beuze
ModelMaker
"

Too bad...anybody else has any suggestions?
0
 
LVL 1

Author Comment

by:miv
Comment Utility
Adjusted points from 666 to 1000
0
 
LVL 4

Expert Comment

by:jeurk
Comment Utility
For that amount of points.
I'll try to write you something if I get some time... This is hard stuff my friend...It won't be easy...but we'll see.

Is someone in that thread near from a working solution ?

John.
0
 

Expert Comment

by:aacrg
Comment Utility
Perhaps there's some information in the debuginfo...
0
 

Expert Comment

by:arcrotty
Comment Utility
I don't know if this helps you now, but when I work on a major project with a lot of units, I comment the 'uses' line with each unit.

Sometimes I only use one function out of a whole unit so I just list the function in a comment next to the listed unit.

uses
  SysUtils,  // Now
  MyUnit1,   // MyFunction1
  MyUnit2,   // To display about box
  MyUnit3;   // Etc.

This way if anything ever gets removed, you can look at the uses clause to remove the unit that it belonged too.

Hope this helps in your future projects.  Sorry it isn't much help to you now.
0
 
LVL 4

Expert Comment

by:jeurk
Comment Utility
Miv...
Would you give me an adress where I can
write you when I come up with something ? Some Day ?
Because this may take longer then the time the question lasts...
CU
0
 
LVL 1

Author Comment

by:miv
Comment Utility
Sure: miv_world@hotmail.com

Good luck!
0
 
LVL 12

Expert Comment

by:rwilson032697
Comment Utility
OK - here is how to do it, it requires writing a simple program, which I do not at the present have time to write - but it is very simple. Here are the steps:


1. Turn Map File to Detailed in Linker Options
2. Build the app (Use Buil all option)
3. Write simple Delphi routine to parse .MAP file which looks for lines that look like this:

Line numbers for Consts(Consts.pas) segment .text

You can just check for a line starting with "Line numbers for" and parse out the file name.

Add each filename to a string list.
4.  Write another routine to rename all pascal source files not in the list created in step 3 so that they have a leading underscore (eg: fred.pas --> _fred.pas if fred.pas is not in the lsit)

5. Build the application again. The compiler will now obligingly stop at each unit in the uses clause which had no code included from it in the resulting executable.

Yes step 5 may be tedious - but use of a simple batch replace utility like breplace.exe (from DSP?) will allow you to remove all instances of each offending unit in all the files in a single step. And you only need to do it once (or at least very seldom...)

Cheers,

Raymond.
0
 
LVL 1

Author Comment

by:miv
Comment Utility
Seems like a solution which could work...but I need a working program!

I don´t have the time to write it myself at the moment, so I´ll open the Q for other answers.

Thanks for the effort so far.

  Michael
0
 
LVL 2

Expert Comment

by:craig_capel
Comment Utility
whooohooooooooooo @1000 points......
0
 
LVL 12

Accepted Solution

by:
rwilson032697 earned 1000 total points
Comment Utility
OK then - this took only a few minutes to write, seems to work OK...

///////////////////DPR:
program UnusedUnits;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


///////////////////Unit:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    edtLocation: TEdit;
    Button1: TButton;
    Label2: TLabel;
    Label3: TLabel;
    edtMapFileName: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  filectrl;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  FileNames : TStringList;
  Files : TFileListBox;
  I : Integer;
  MapFile : TextFile;
  Line : String;
  AFileName : String;
begin
  if not directoryexists(edtlocation.text) then
    begin
      ShowMessage('Directory does not exist');
      exit;
    end;

  if not fileexists(IncludeTrailingBackslash(edtlocation.text) + edtMapFileName.text) then
    begin
      ShowMessage('Map file does not exist');
      exit;
    end;

  try
    FileNames := TStringList.Create;
    Files := TFileListBox.Create(Self);
    Files.Visible := False;
    Files.Parent := self;    
    try
  // parse the map file
      assignfile(MapFile, IncludeTrailingBackslash(edtlocation.text)+ edtMapFileName.text);
      reset(MapFile);
      while not eof(MapFile) do
        begin
          Readln(MapFile, Line);
          if pos('Line numbers for', Line) = 1 then
            begin
              AFileName := Copy(Line, Pos('(', Line) + 1, length(Line));
              AFileName := Copy(AFileName, 1, Pos(')', AFileName) - 1);
              FileNames.add(AFileName);
            end;
        end;
      Closefile(MapFile);

  // Now rename all the files not used...
      Files.ApplyFilePath(IncludeTrailingBackslash(edtlocation.text)+'*.pas');
      for I := 0 to Files.Items.Count - 1 do
        if FileNames.IndexOf(Files.Items[I]) = -1 then
          RenameFile(Files.Items[I], '_'+Files.Items[I]);
    finally
      FileNames.Free;
      Files.Free;
    end;
  except
    on e : exception do
      ShowMessage(format('Exception %s occured', [e.message]));
  end;
end;

end.


////////////////////Form:

object Form1: TForm1
  Left = 457
  Top = 259
  Width = 622
  Height = 134
  Caption = 'Find Unused Units'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 8
    Top = 16
    Width = 77
    Height = 13
    Caption = 'Location of files:'
  end
  object Label2: TLabel
    Left = 8
    Top = 72
    Width = 499
    Height = 33
    Caption =
      'Enter the location of the source files and the map file then pre' +
      'ss the go button (assumes map file and source files are in the s' +
      'ame location'
    WordWrap = True
  end
  object Label3: TLabel
    Left = 8
    Top = 40
    Width = 69
    Height = 13
    Caption = 'Map file name:'
  end
  object edtLocation: TEdit
    Left = 112
    Top = 8
    Width = 489
    Height = 21
    TabOrder = 0
  end
  object Button1: TButton
    Left = 528
    Top = 72
    Width = 75
    Height = 25
    Caption = '&Go!'
    TabOrder = 1
    OnClick = Button1Click
  end
  object edtMapFileName: TEdit
    Left = 112
    Top = 34
    Width = 489
    Height = 21
    TabOrder = 2
    Text = 'Program.map'
  end
end

Cheers,

Raymond.
0
 
LVL 12

Expert Comment

by:rwilson032697
Comment Utility
miv: Have you tried the program?

Cheers,

Raymond.
0
 
LVL 1

Author Comment

by:miv
Comment Utility
Not yet, on vacation at the moment.
I´ll do it as soon as possible.

  Michael
0
 
LVL 12

Expert Comment

by:rwilson032697
Comment Utility
No problem - have a great holiday!

Cheers,

Raymond.
0
 
LVL 4

Expert Comment

by:jeurk
Comment Utility
Your stuff is working Raymond...
But it's only handling the files of the current project.
It would be cool to also be able to tell all the unit names that are included in the units and that are no longer needed.
Like components units, etc...
But I agree, it's not hard to do...
CU all...
0
 
LVL 12

Expert Comment

by:rwilson032697
Comment Utility
Well, it already does this. This loop:

      for I := 0 to Files.Items.Count - 1 do
        if FileNames.IndexOf(Files.Items[I]) = -1 then
          RenameFile(Files.Items[I], '_'+Files.Items[I]);

is running through all the unused units by filename. The unit name as used in the uses clause may be retrieved like this:

AUnitName := ChangeFileExt(ExtractFileName(Files.Items[I]), '');
 
Cheers,

Raymond.

0
 
LVL 4

Expert Comment

by:jeurk
Comment Utility
I agree,
I mean that it will only remove the units no longer used in the project (only those in the current dur?).
But let's say someday you used the rxdbgrid... This will add the rxgrids to your uses clausule. Let's say someday you change for gxdbgrid and you forget to remove the rxgrids clausule.
Then I think that your routine will not be able in that state to say that I should remove the rxgrids from the uses clausule in the xxxx.pas unit.
Is it ? I'm not sure...
And for example, if you use the rxgrids units in some of your projects units...
If someday you remove the rxdbgrid from most of your forms but not all...
Will your routine say, which units no longer need to include the rxdbgrids unit ?
It's only things I'm asking myself, ok ?
It's not that I contest your answer...That's not my goal ;)

Regards...
0
 
LVL 12

Expert Comment

by:rwilson032697
Comment Utility
I see what you mean.

Yes, the code does assume all the source you are using is in a single location (which is a little idealistic - the app I am working on has no fewer than 20 folders containing source compiled into the app). However, it could be modified to search a path list to retrieve all files on the set of paths, or you could temporarily point your source control working directories to a single location while doing this excercise.

As long as the code can find the source in the given location it will identify unused units, whether thay are the likes of rxdbgrid or something else. Another useful enhacement might to be cater for components released only as DCUs - but then you would get bitten by those released only as .BPLs.

This code won't say which units use other units that are not used any more. It will say which units are no longer used. Grep is a much more effective tool for this!

Cheers,

Raymond.
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now