yurikhan: (Default)
[personal profile] yurikhan

Иногда при разборе какого-нибудь проекта с кучей файлов бывает удобно посмотреть на общую картину — какие файлы зависят от каких других файлов. Это может быть надо для того, чтобы решить, в каком порядке их читать, или разрулить цикл в #include’ах.

Ну или, например, на прошлой работе (да! Я сменил работу. Об этом в другой раз) надо было сделать большую серию рефакторингов, типа «снять модификатор static с каждого поля класса», при этом, если поле используется в функции, то сначала надо снять static с функции; а если функция вызывается из static-функции другого класса, то сначала нужно устроить, чтобы у вызывающей функции был объект, на котором можно было бы вызывать эту функцию, то есть, как правило, снять static и с неё тоже. Если начать делать, не подумав, то одно потянет за собой второе, второе потянет третье и в результате получится патч, который невозможно review’ить. Если же сразу определить правильный порядок рефакторингов, то патчи получаются маленькие и простые.

Поскольку все доступные инструменты UML-моделирования — полное г***о с точки зрения юзабилити, рисовать будем Graphviz’ом. Заодно он нам сам оптимально разложит всё по слоям и упорядочит так, чтоб было меньше пересечений рёбер.

Однако, у Graphviz’а не слишком удобный синтаксис для подобных задач. Я его постоянно забываю.

digraph "Dependencies"
{
    "ControlProgram.cpp" -> "UpdateServerCommander.h"

    "UpdateServerCommander.cpp" -> "UpdateServerCommander.h"
    "UpdateServerCommander.cpp" -> "Infofile.h"
    "UpdateServerCommander.cpp" -> "CdShellTitle.h"

    "UpdateServerCommander.h" -> "Infofile.h"

    "Infofile.cpp" -> "Infofile.h"
}

В традициях UML, стрелочки рисуем от зависимого к требуемому.

Что здесь плохо? Ну, как минимум, приходится повторять имя вершины для каждой её зависимости. Хочется один раз написать вершину, а потом все её зависимости скопом. Ну и, иногда, наоборот: написать все зависящие, а потом то общее, от чего они зависят.

Вот, например, какой-то такой make-подобный синтаксис:

ControlProgram.cpp:
    UpdateServerCommander.h
UpdateServerCommander.cpp:
    UpdateServerCommander.h
    Infofile.h
    CdShellTitle.h
UpdateServerCommander.h:
Infofile.cpp:
    Infofile.h

Да, а ещё Graphviz по умолчанию направляет рёбра сверху вниз. Для графов зависимостей это не очень удобно — то, от чего зависят все, должно быть слева и/или сверху, а то, что зависит от всего — наоборот, справа и/или внизу. Это определяется атрибутом графа rankdir (ему надо задать значение BT (bottom to top) или RL (right to left)). Опыт показывает, что RL даёт более компактные картинки.

Ну и, для пущего соответствия нотации UML, пусть вершины будут прямоугольными, а рёбра — пунктирными и ломаными (а не сплайнами).

Итого, для конвертации написался вот такой скрипт:

#! /usr/bin/python

import fileinput, re

def dump(dependents, dependencies):
    print "".join(["\t\"%s\" -> \"%s\"\n" % (dependent, dependency)
                   for dependent in dependents
                   for dependency in dependencies])

dependent_regexp = re.compile("^(.*?):$")
dependency_regexp = re.compile("^\t(.*?)$")

dependents = []
dependencies = []

print "digraph \"Dependencies\""
print "{"
print "\tgraph [rankdir = RL, splines = polyline]" # Place most dependent nodes rightmost
print "\tnode [shape = box]"
print "\tedge [arrowhead = open, style = dashed]" # UML Dependency arrow style

for line in fileinput.input():
    m = dependent_regexp.match(line)
    if m:
        if len(dependencies) > 0:
            dump(dependents, dependencies)
            dependents = [m.group(1)]
            dependencies = []
        else:
            dependents.append(m.group(1))
        continue
    m = dependency_regexp.match(line)
    if m:
        dependencies.append(m.group(1))
        continue

dump(dependents, dependencies)

print "}"

Вызывается так:

$ ./dep2dot ControlProgram.dep |dot -Tsvg -oControlProgram.svg
$ firefox ./ControlProgram.svg
(will be screened)
(will be screened if not validated)
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

yurikhan: (Default)
Yuri Khan

August 2018

S M T W T F S
   1234
567891011
12131415161718
19202122232425
26 2728293031 

Links

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated 2025-06-22 15:00
Powered by Dreamwidth Studios