Keep Your Project Files Clean With Project Magician

This is sitting on my desktop for quite a while waiting that I find the time to write this blog post. So now be it.

In the past a couple of you have downloaded and probably also used my DprojNormalizer IDE plugin. Well, I have to say that I am not going to update that plugin any longer. But don’t hesitate – a replacement product will  be available with some interesting features added: Project Magician

DprojNormalizer as well as DprojSplitter are one-purpose plugins. If they are installed, they do their jobs. No switches, no settings inside the IDE, although DprojSplitter respects an optional configuration file. Both plugins can be installed side by side without one interfering with the other.

Although this lean approach has some advantages, it also has some drawbacks: a good portion of the code is similar and the IDE events handled are almost the same. But the most prominent issue I have with these one-purpose plugins is: I am running out of proper icons.

This was the birth of Project Magician which combines the features of DprojNormalizer and DprojSplitter adding some new features that roughly fall into the same area.

In contrast to both other plugins Project Magician now has settings inside the IDE. Have a look at the settings form for the Project Magician project itself.

The settings are grouped in four different areas.

Project file settings contains the Normalize and Split entries, which resemble the already know behavior of DprojNormalizer and DprojSplitter. The third option in this group allows to remove the Excluded Packages entries from the project file. Usually this entry is unnecessary and depends on the local installation often causing trouble on commits. It will be re-created automatically with default settings when a project is opened.

Clear Settings in Child Configurations probably needs some words of explanation. Sometimes the inheritance of project settings as implemented in the IDE is not wanted and leads to a lot of work or unexpected results. For instance, having different version info values for each build configuration is rarely required. Changes made to one configuration are not visible in others. In addition inheritance doesn’t work well with these either. Changes in the base configuration have no effect if values in depending configurations exist.

That said, the Version info option clears all settings made in child configurations and only keeps the settings in the base configuration. In case of a pure Windows project this actually is the base configuration, while for mixed OS projects each platforms base configuration serves as a basis. Note that the Module attributes are excluded from the clearing so that each build configuration can have its own attributes.

Application Settings are always platform specific and thus the settings in the platform base configuration are the ones kept. The settings targeted here are Icons, Manifest File, Output Settings and Appearance.

Package Settings affect Prefix, Suffix and Version as well as the Description and the Never Build option. Needless to say that Package Settings are only enabled for packages while Application Settings have only a meaning for applications.

When upgrading a project from a previous Delphi version it sometimes happens that you cannot add platforms even if they are available in the IDE. This is where Enable Missing Platforms steps in, enabling all platforms known to the current Delphi version, even if they are currently not available for your SKU. Although you still can only add those platforms provided by your IDE, the other platforms can later be enabled on systems that include them.

Projects that are only useful for some platform and will never be used on others (like f.i. design time packages) are nevertheless cluttered with platform specific entries for these never be used platforms. When the Remove Unused Platforms option is set Project Magician will remove those entries resulting in a much cleaner project file. This doesn’t remove the ability to add those platforms to the project later.

Maintaining packages for different Delphi versions requires to use a decent LIBSUFFIX to distinguish the bpl-files for each version (see Delphi Library Guidelines). Creating the project files for a new Delphi version usually involves copying those from the previous version to a new folder and adjusting the LIBSUFFIX entries. The Auto LibSuffix option eliminates the last step.

Sometimes the FormType entries in the project file are missing. This leads to the fact that during design time one cannot instantiate frames affected by this. The  Refresh Form Type functionality restores the missing entries when opening a project.

As you may notice there are settings for different scopes: global, project group and project. Settings are inherited from the next higher scope and can only be activated in a lower scope, but not deactivated. Take care when activating settings on global level! In the example above Refresh Form Type and Remove Unused Platforms are set on project group level.

The global settings can alternatively set from the Tools – Options dialog. The Project Manager options can be found in the Third Party branch.

Only as part of the global options you have access to the Clean Line Feeds feature. When opening dpr, dpk, pas or inc files any invalid line feed sequence (like single <CR> or <LF> alone) will be converted to a proper <CR><LF> sequence.

Project Magician comes with a command line application that allows to execute most of the tasks done by the plugin from the command line. It is named ProjectMagicianCmd.exe and resides in the installation folder (default: %ProgramFiles(x86)%\ProjectMagician).

The general usage is:
ProjectMagicianCmd [-v:<version> | -n | -r | -x | -f] [<filepath>]<filename> [-l:<logfile>] [-s]

The parameters have the following meaning:
-v = Sets VersionInfo in dproj files to a given value. Clears all version info entries in child build configurations.
<version> up to 4 numbers separated by dots

-n = Normalize

-r = Removes unused platforms

-x = Removes “Excluded Packages”

-f = Refreshes and adds missing form type entries

<filename> may contain wildcards. If no extension is given .dproj is assumed

Project Magician can be downloaded here:  Project Magician

Currently Delphi XE3 to 10.2 Tokyo are supported.

Comments Disabled

To minimize the effort needed to comply with the GDPR rules, as of today all comments are disabled until further notice.

I plan to reactivate comments when I can get reliable instructions on how to make it work according to GDPR. Unfortunately it is not done with just adding a checkbox to get consent for storing personal data. I am still investigating if I should better delete all existing comments just to be on the safe side.

In the meantime I will post a note about any new blog entry on G+ where comments are still welcome.

BTW, the blog uses a new theme now – just in case you didn’t notice.

Another Update for DprojNormalizer

There are weeks when bug findings are just going to agglomerate. Fixing them as soon as possible then leads to multiple releases in a short time frame. Sorry about that (well, not really), but nothing comes without a price to pay.

Another small update for  DprojNormalizer is available. This one is only relevant when you have quite a number ( > 10 ) of build configurations in your project, where the hierarchy sometimes gets broken after normalizing.

Thanks to Tobias Reißenweber for bringing that to my attention.

Update: This turned out to be way more complex as expected! V2.2.3 should be the version to go. The good news is, that I learned a lot about the internal assumptions made by the IDE developers.

Update for DprojNormalizer

There is a small update for DprojNormalizer available. The only change is that the entry for SanitizedProjectName is moved to the beginning of the property group. This allows the use of this variable in subsequent properties.

I stumbled upon this when working on a project where $(SanitizedProjectName) is used in the DCU output path to have different DCU paths for each project inside a project group. This reduces the chance of linking the wrong DCU files when working with different conditional defines or settings between projects sharing some source files.

Although this definitely worked some time ago, this entry now resolves to an empty string. The reason is the changed sorting of the dproj file due to the use of DprojNormalizer. The system tries to resolve the DCU output path while SanitizedProjectName is still not defined yet.

BTW, I know it has been rather quiet here during the last few months. Rest assured there are some nice things cooking in the labs, so stay tuned.

Configuring DprojSplitter to Your Needs

Unless I already told you privately, you might not be aware of an undocumented (well, at least before today) option to adjust DprojSplitter to your needs.

By default DprojSplitter splits the following entries in each PropertyGroup of a dproj file:

  • Debugger_RunParams
  • Debugger_RemoteRunParams
  • Debugger_HostApplication
  • Debugger_RemotePath
  • Debugger_RemoteHost
  • Debugger_EnvVars
  • Debugger_SymTabs
  • Debugger_Launcher
  • Debugger_RemoteLauncher
  • Debugger_IncludeSystemVars
  • Debugger_UseLauncher
  • Debugger_UseRemoteLauncher
  • Debugger_CWD
  • Debugger_RemoteCWD
  • Debugger_RemoteDebug
  • Debugger_DebugSourcePath
  • Debugger_LoadAllSymbols
  • Debugger_LoadUnspecifiedSymbols
  • Debugger_SymbolSourcePath

If you want to modify or extend this list, you have to provide a configuration file for DprojSplitter with a list of all PropertyGroup entries to be split. The file has to be named DprojSplitter.cfg and has to reside in the IDE AppData folder (which is %AppData%\Roaming\Embarcadero\BDS\19.0\ for Tokyo, adjust the number to your IDE version). The file should contain one entry name per line.

Be aware that such a file will override the default list shown above. So better make sure to include that list in your file unless you explicitly don’t want some or all of those entries to be split.

F.i., if you want the DCU output folder be split, you have to add DCC_DcuOutput to the above list.