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
