The Road to Delphi

Delphi – Free Pascal – Oxygene

Fun with Delphi RTTI – Building a Unit Dependency Tree

Leave a comment

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).
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;

Finally the output for the source code

Author: Rodrigo

Just another Delphi guy.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s