Skip to content
The Road to Delphi — Delphi, VCL, VCL Styles, UxTheme and WinAPI

Fun with Delphi RTTI – Building a Unit Dependency Tree

, , ,

You can build a unit dependency tree, wich shows the direct dependency for each unit in your project using the New Rtti.

Here a short description of the algorithm used in this code.

  • For each Type(TRttiType) in the list do the following
  • check if the basetype exist in the same unit else add the unit to the list.
  • for each public field in the current type check if exist in the same unit else add the unit to the list.
  • for each method in the current type with an valid ReturnType check if exist in the same unit else add the unit to the list.
  • for each property in the current type check if exist in the same unit else add the unit to the list.

Limitations:

  • Only show direct dependency of the units (example if Unit A depends on Unit B and Unit B depends on UnitC, the tree will show wich the Unit A depends on only of Unit B)
  • Only supports Types with Rtti info.
  • Due to Rtti Limitations only supports public fields (TRttiField).

[sourcecode language=”delphi” wraplines=”false”]
uses
Rtti,
Generics.Collections,
TypInfo;

procedure FillTreeUnits(TreeViewUnits:TTreeView);
var
ctx : TRttiContext;
TypeList : TArray<TRttiType>;
lType : TRttiType;
lMethod : TRttiMethod;
lProperty: TRttiProperty;
lField : TRttiField;
Node : TTreeNode;
UnitName : string;
RefUnit : string;
UnitsDict: TObjectDictionary<String, TStringList>;
UnitList : TStringList;

function GetUnitName(lType: TRttiType): string;
begin
{
if lType.IsInstance then
Result:=lType.UnitName
else
}
Result := StringReplace(lType.QualifiedName, ‘.’ + lType.Name, ”,[rfReplaceAll]);
end;

//Check if exist the Unit in the Dictionary and if has a Unit Children in the associated list
procedure CheckAndAdd(UnitName,RefUnit:string);
begin
if UnitName<>RefUnit then
if not UnitsDict.ContainsKey(UnitName) then
begin
UnitList:=TStringList.Create;
UnitList.Add(RefUnit);
UnitsDict.Add(UnitName,UnitList);
end
else
begin
UnitList:=UnitsDict.Items[UnitName];
if UnitList.IndexOf(RefUnit)<0 then
UnitList.Add(RefUnit);
end;
end;

begin
ctx := TRttiContext.Create;
UnitsDict := TObjectDictionary<String, TStringList>.Create([doOwnsValues]);
TreeViewUnits.Items.BeginUpdate;
try
TreeViewUnits.Items.Clear;
TypeList:= ctx.GetTypes;

//Fill a Dictionary with all the units and the dependencies
for lType in TypeList do
begin
//Search references to another units in the BaseType
UnitName:=GetUnitName(lType);
if Assigned(lType.BaseType) then
CheckAndAdd(UnitName,GetUnitName(lType.BaseType));

//Search references to another units in the public fields (due to RTTI limitations only works with public fields)
for lField in lType.GetDeclaredFields do
if Assigned(lField.FieldType) and (lField.FieldType.IsPublicType) then
CheckAndAdd(UnitName,GetUnitName(lField.FieldType));

//Search references to another units in the properties
for lProperty in lType.GetDeclaredProperties do
if Assigned(lProperty.PropertyType) then
CheckAndAdd(UnitName,GetUnitName(lProperty.PropertyType));

//Search references to another units in functions with ExtendedInfo (HasExtendedInfo=True)
for lMethod in lType.GetDeclaredMethods do
if (lMethod.HasExtendedInfo) and (lMethod.MethodKind in [mkFunction,mkClassFunction]) then
CheckAndAdd(UnitName,GetUnitName(lMethod.ReturnType));
end;

//finally fill the treeview
for UnitName in UnitsDict.Keys do
begin
UnitList:=UnitsDict.Items[UnitName];
Node :=TreeViewUnits.Items.Add(nil,UnitName);
for RefUnit in UnitList do
TreeViewUnits.Items.AddChild(Node,RefUnit);
end;

finally
UnitsDict.Destroy;
ctx.Free;
TreeViewUnits.Items.EndUpdate;
end;
end;
[/sourcecode]

Finally the output for the source code

Leave a Reply