Jump to content
Uwe Raabe

On The Design Of Uses Clauses

Recommended Posts

While working on a tool for cleaning up uses clauses I stumbled upon this beast (slightly changed to protect the innocent):

{$if defined(DEBUG) or defined(DEBUG_SPECIAL)}
uses
{$ifend}
{$IFDEF DEBUG}
  dialogs
{$ENDIF}
//<some comment about the following ifdef>
{$IFDEF DEBUG_SPECIAL}
  mmsystem, // timeGetTime()
  messages
{$ENDIF}
{$if defined(DEBUG) or defined(DEBUG_SPECIAL)}
  ;
{$ifend} 

If you are only looking for uses clauses inside some Delphi sources and try to avoid a full featured parser, you will have a pretty hard job to detect, parse and interpret that one correctly.

 

So, if you are interested to make any use of such a tool, please do me a favor: Don't write your code that way! It is hard to read for humans anyway.

  • Like 1
  • Confused 1

Share this post


Link to post

A good tool must handle comments and conditionals correctly IMHO.

But it'll not be easy.:classic_biggrin:

Share this post


Link to post

Well, the actual problem here are the conditionals around the uses keyword and the closing semicolon. The rest inside is fine.

 

This version is handled without problems:

{$if defined(DEBUG) or defined(DEBUG_SPECIAL)}
uses
{$IFDEF DEBUG}
  dialogs
{$ENDIF}
//<some comment about the following ifdef>
{$IFDEF DEBUG_SPECIAL}
  mmsystem, // timeGetTime()
  messages
{$ENDIF}
  ;
{$ifend} 

 

Share this post


Link to post

Yes this look like a nightmare, if you want to parse the source and not only create a AST

Share this post


Link to post
3 hours ago, Uwe Raabe said:

Well, the actual problem here are the conditionals around the uses keyword and the closing semicolon. The rest inside is fine.

 

This version is handled without problems:


{$if defined(DEBUG) or defined(DEBUG_SPECIAL)}
uses
{$IFDEF DEBUG}
  dialogs
{$ENDIF}
//<some comment about the following ifdef>
{$IFDEF DEBUG_SPECIAL}
  mmsystem, // timeGetTime()
  messages
{$ENDIF}
  ;
{$ifend} 

 

I have been pondering the problem myself, as I want to reorder the uses clauses in a large number of units. The variations imposed over the years by thoughtless developers make the task a good deal more challenging than it ought to be:

uses

  OneUnit, Another, MyThird

, Fourth, {NoLongerUsed,} 

StillMore {Why here?}, 

Last

;

 

Conditionals obviously add to the fun. Moreover, I want to organize so that Delphi library modules are in the first grouping, then third-party, and last, units of the application. And then, of course, to provide for walking the file tree and applying changes to each application module found in the map file. 

Share this post


Link to post
1 hour ago, Bill Meyer said:

Moreover, I want to organize so that Delphi library modules are in the first grouping, then third-party, and last, units of the application. And then, of course, to provide for walking the file tree and applying changes to each application module found in the map file. 

Are you interested to do some testing?

Share this post


Link to post
10 minutes ago, Uwe Raabe said:

Are you interested to do some testing?

Yes, I could do that. I assume, however, that you are implementing for v14, not for earlier versions? Most of my work is in D2007, where I have literally thousands of such uses clauses to work with. 

Share this post


Link to post
1 hour ago, Bill Meyer said:

I assume, however, that you are implementing for v14, not for earlier versions? Most of my work is in D2007

I am referring to a command line tool.

Share this post


Link to post
3 minutes ago, Uwe Raabe said:

I am referring to a command line tool.

Well, that would be very interesting. I would be happy to do some testing.

Share this post


Link to post
On 7/18/2019 at 9:44 PM, Bill Meyer said:

Moreover, I want to organize so that Delphi library modules are in the first grouping, then third-party, and last, units of the application. And then, of course, to provide for walking the file tree and applying changes to each application module found in the map file. 

That's exactly the scheme I came to. I also avoid using units in impl. section except for app's forms.

Share this post


Link to post
4 hours ago, Fr0sT.Brutal said:

That's exactly the scheme I came to. I also avoid using units in impl. section except for app's forms.

Actually, I find it is better to add references in the implementation section, if they are not needed in the interface. Unit dependency cycles should always be avoided, but there are tools which can help you with that (MMX, Delphi Unit Dependency Scanner), and the need for such cycles is always indicative of design problems. 

In general, keep scope everywhere as narrow as possible.

Share this post


Link to post
16 minutes ago, Bill Meyer said:

In general, keep scope everywhere as narrow as possible.

Amen!

Share this post


Link to post
On 7/18/2019 at 2:44 PM, Bill Meyer said:

I have been pondering the problem myself, as I want to reorder the uses clauses in a large number of units. The variations imposed over the years by thoughtless developers make the task a good deal more challenging than it ought to be:

uses

  OneUnit, Another, MyThird

, Fourth, {NoLongerUsed,} 

StillMore {Why here?}, 

Last

;

 

What is the purpose of keeping {NoLongerUsed,} in the uses clause? If it compiles, it can be removed, right?

 

Also on StillMore {Why here?}, ... why would there be such indication? If it doesn't compile without StillMore then it should be there, no?

 

I don't understand these concepts, I only set those units in uses clause that need to be there. I follow compiler and hints what needs to be there. If it compiles without units, I don't put them there.

  • Like 1

Share this post


Link to post
9 minutes ago, Mike Torrettinni said:

What is the purpose of keeping {NoLongerUsed,} in the uses clause? If it compiles, it can be removed, right?

 

Also on StillMore {Why here?}, ... why would there be such indication? If it doesn't compile without StillMore then it should be there, no?

 

I don't understand these concepts, I only set those units in uses clause that need to be there. I follow compiler and hints what needs to be there. If it compiles without units, I don't put them there.

Legacy code is its own excuse. Whatever the reasons may have been, these are examples which exist now, and which I need to parse out without damage to the rest. With over 2,000 units to process, and most containing uses clauses in both interface and implementation, the why of what is there is not my concern. Cleaning is my focus. And NOT by hand.

Share this post


Link to post
7 minutes ago, Bill Meyer said:

Legacy code is its own excuse. Whatever the reasons may have been, these are examples which exist now, and which I need to parse out without damage to the rest. With over 2,000 units to process, and most containing uses clauses in both interface and implementation, the why of what is there is not my concern. Cleaning is my focus. And NOT by hand.

I don't envy you, at all. I'm cleaning up my own code and it takes a lot for each unit, form, especially if redesign is needed. The easiest is decoupling forms, but some redesigns take time, too much time.

Share this post


Link to post
50 minutes ago, Mike Torrettinni said:

I don't envy you, at all. I'm cleaning up my own code and it takes a lot for each unit, form, especially if redesign is needed. The easiest is decoupling forms, but some redesigns take time, too much time.

It's not always fun. 😉 But it is a continuing challenge.

Share this post


Link to post
On 7/18/2019 at 8:44 PM, Bill Meyer said:

I have been pondering the problem myself, as I want to reorder the uses clauses in a large number of units. The variations imposed over the years by thoughtless developers make the task a good deal more challenging than it ought to be:

uses

I've actually been using a similar approach too. But as a Delphi Consultant I've seen very strange things already ... The order of the Uses clause might actually be important especially when some variables or other things get set up in the Initialisation section of a unit. Oh and @Uwe Raabe any news on the tool ? Been searching for a good tool which can detect unused units / restructure uses clauses for a while now.,

Share this post


Link to post

@sLesage

This is an early beta. It is a command line tool called with the following parameters:

UsesCleaner [<filepath>]<filename> [-c:<configfile>] [-o:<outputpath>] [-l:<logfile>] [-s]

<filename> may contain wildcards
<configfile> default is UsesCleaner.cfg
<outputpath> if not specified, original files will be changed!
-s: also handles subfolders

 

The example config file is targeting Delphi 10.2 Tokyo, but can easily be adjusted. Some entries are self-explaining.

[Settings]
Indentation=2
Compressed=1
MaxLineLength=130
SearchPath=c:\program files (x86)\embarcadero\studio\19.0\lib\Win32\release;
UnitAliases=WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
UnitScopeNames=Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Bde;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell
GroupNames=@DelphiOTA;Winapi;System.Win;System;Data;FireDAC;Vcl;Rz*

[Groups]
DelphiOTA=ToolsAPI;DesignIntf;DesignEditors

The tool will resolve any unit aliases and unit scope names. Then it will group the units according to the order given in GroupNames followed by any remaining units. Plain group names are taken as namespace prefix (like Data or Vcl). Unit names without such namespace can be grouped with wildcards (like Rz* for Raize Components units). You can define groups by simply listing the unit names in the Groups section as it is show with the DelphiOTA group.

UsesCleaner.zip

Share this post


Link to post

This looks interesting ... Some Suggestions / Ideas :

 

  • Option to put every unit on it's own line
  • Option to put an empty line in between groups

I will give it a few more spins on bigger projects to see if everything works OK.

Share this post


Link to post

I like this ... This in combination with a remove unused units would be heaven for me ... Well maybe a remove unused unit which would also move uses from Interface to Implementation if they aren't needed in the interface section 🙂

 

what does the Compressed setting in the Config file do ? Havn't been able to make it do anything ... or I don't know what it does.

Share this post


Link to post
3 hours ago, sLesage said:

I've actually been using a similar approach too. But as a Delphi Consultant I've seen very strange things already ... The order of the Uses clause might actually be important especially when some variables or other things get set up in the Initialisation section of a unit. Oh and @Uwe Raabe any news on the tool ? Been searching for a good tool which can detect unused units / restructure uses clauses for a while now.,

I solved the initialization issue by logging to find the order in which the initialization clauses were fired, then replaced initialization and finalization with procedures which are called from my own initialization unit.

 

Still much work to be done on the uses cleanup, and the untangling of dependency cycles, but I no longer have to worry over initialization stability.

Edited by Bill Meyer

Share this post


Link to post
1 hour ago, sLesage said:

Option to put every unit on it's own line

Set Compressed=0

 

55 minutes ago, sLesage said:

what does the Compressed setting in the Config file do ? Havn't been able to make it do anything ... or I don't know what it does.

See above.

Share this post


Link to post
1 hour ago, sLesage said:

This in combination with a remove unused units would be heaven for me ... Well maybe a remove unused unit which would also move uses from Interface to Implementation if they aren't needed in the interface section

Well, that would require some sort of compilation or so and is definitely outside the scope of such a tool.

 

It might be worth to investigate in interpreting the output of Peganza Pascal Analyzer Uses Report and act accordingly. This would eliminate the burden of analyzing the code.

Share this post


Link to post

Well ... I actually though I had tried that but with Compressed=1 becuase I thought it would put 1 uses on every line. But now I get it ... it's just a boolean, nut the number of units on a line. Loving it already ... Currently I have this approach but might be fooling around with the groups.

 

[Settings]
Indentation=2
Compressed=0
MaxLineLength=80
SearchPath=c:\program files (x86)\embarcadero\studio\19.0\lib\Win32\release;
UnitAliases=WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
UnitScopeNames=Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;System;Xml;Data;Datasnap;Bde;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell
GroupNames=@DelphiOTA;Winapi;System.Win;System;Data;FireDAC;Datasnap;REST;Vcl;cx*;dx*

[Groups]
DelphiOTA=ToolsAPI;DesignIntf;DesignEditors

Oh ... and I know I'm exagerating here ... but an option to add the GroupNames as a comment line before the first Uses ? And maybe an option to add a blank line between every group of uses ? I know I'm probably one of the very few peeps who does it like that, so no worries if it isn't possible.

Share this post


Link to post
18 minutes ago, Uwe Raabe said:

It might be worth to investigate in interpreting the output of Peganza Pascal Analyzer Uses Report and act accordingly. This would eliminate the burden of analyzing the code.

I can attest that the Peganza Pascal Analyzer Lite Uses Report has been very helpful. Note, however, that it is by no means perfect.

1. It seems always to report falsely that a class helper unit is not needed.

2. It tends to suggest removing units which are actually needed.

3. It sometimes asserts a unit ref can be in implementation, when the IDE wants to replace it in interface.

4. Form inheritance complicates the issues.

 

That said, it is the only tool I am aware of which provides assistance in finding which unit refs can be demoted to implementation.

 

For removal of unused units, I prefer CnPack, which does a test compile, and works with the map file, I believe.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×