Emerge ( илиemerge-viz ) — это интерактивный инструмент анализа кода, позволяющий получить представление о структуре исходного кода, показателях, зависимостях и сложности программных проектов. Вы можете сканировать исходный код проекта, рассчитывать результаты и статистику метрик, создавать интерактивное веб-приложение со структурами графов (например, графом зависимостей или графом файловой системы) и экспортировать результаты в некоторые форматы файлов. В настоящее время Emerge поддерживает синтаксический анализ следующих языков: C
, C++
, Groovy
, Java
, JavaScript
, TypeScript
, Kotlin
, ObjC
, Ruby
, Swift
, Python
, Go
. Структура, раскраска и кластеризация рассчитываются и основаны на идее сочетания силового графического моделирования и модульности Лувена. Emerging в основном написан на Python 3 и протестирован на macOS, Linux и современных веб-браузерах (например, последних версиях Safari, Chrome, Firefox, Edge).
выйти (/ɪˈməːdʒ/)
- появиться, выйдя из чего-то или из-за чего-то
- стать известным, особенно в результате изучения чего-либо или задавания вопросов об этом
Основная цель этого проекта — создать бесплатный инструмент с открытым исходным кодом, который может легко использовать любой, кто интересуется разработкой программного обеспечения, архитектурой, метриками и визуализацией, чтобы получить больше информации по этим темам. Он должен способствовать/поддерживать лучшее понимание данного программного проекта с помощью исследовательского подхода.
C
, C++
, Groovy
, Java
, JavaScript
, TypeScript
, Kotlin
, ObjC
, Ruby
, Swift
, Python
Groovy
, Java
, Kotlin
, Swift
git-based
(SLOC, сложность пробелов, связь изменений)SwiftUI
и Composable
декларативных объектов пользовательского интерфейса.git-based
например оттока кодаСамый простой способ использовать Emerre в предварительно собранном Docker-контейнере. Единственное необходимое условие — наличие движка Docker. Например, Docker Desktop.
Подготовьте рабочую папку вот так
config.yml
?export
?source
Команда для запуска анализа:
docker run --rm -v <YOUR_WORKING_FOLDER_PATH>:/tmp/emerge achtelik/emerge:2.0.0 /tmp/emerge/config.yml
Последний параметр — это путь к config.yml внутри контейнера Docker.
⚡Вы можете игнорировать ошибку Pyperclip в конце прогона.
Если вы используете предложение выше, обратите внимание, что ваши пути analyses.source_directory
и export.directory
должны начинаться с /tmp/emerge
. Это необходимо, поскольку ваш анализ выполняется внутри контейнера Docker.
Например:
---
project_name: java_project_example
loglevel: info
analyses:
- analysis_name: full java check
source_directory: /tmp/emerge/source
.
.
.
export:
- directory: /tmp/emerge/export
.
.
.
Сам контейнер Docker не зависит от пути. Не стесняйтесь использовать свои собственные пути монтирования томов и конфигурации проекта.
По сути, существует два способа установки. Если вы знакомы с pip
(виртуальная среда с использованием pyenv
, virtualenv
и virtualenvwrapper
рекомендуется, но не обязательна), вы можете просто установить последнюю версиюemerging, выполнив следующие несколько шагов.
Рекомендуемый способ — использовать виртуальную среду, вы можете сделать это, используя следующий пример:
pyenv install 3.10.0
pyenv virtualenv 3.10.0 venv-3.10.0
pyenv activate venv-3.10.0
Вы можете просто установить require с помощью pip
.
В Ubuntu 20.04+ убедитесь, что установлены пакеты graphviz
и graphviz-dev
, т.е.
apt-get install graphviz graphviz-dev
Либо установите как новый пакет с помощью:
pip install emerge-viz
или, если он уже установлен, просто обновите его с помощью:
pip install -U emerge-viz
а затем просто выполните его следующим образом:
(emerge) user@host ~ % emerge
usage: emerge [-h] [-c YAMLCONFIG] [-v] [-d] [-e] [-a LANGUAGE]
? Welcome to emerge x.y.z (yyyy-mm-dd hh:mm:ss)
options:
-h, --help show this help message and exit
-c YAMLCONFIG, --config YAMLCONFIG
set yaml config file
-v, --verbose set logging level to INFO
-d, --debug set logging level to DEBUG
-e, --error set logging level to ERROR
-a LANGUAGE, --add-config LANGUAGE
add a new config from a template, where LANGUAGE is one of [JAVA, SWIFT, C, CPP, GROOVY, JAVASCRIPT,
TYPESCRIPT, KOTLIN, OBJC, RUBY, PY, GO]
Вы можете создать простую специальную конфигурацию проекта из командной строки, а затем просто настроить необходимые пути источника/экспорта.
(emerge) user@host tmp % pwd
/Users/user1/tmp
(emerge) user@host tmp % emerge -a java
✅ created config file from template: /Users/user1/tmp/java-template.yaml
а затем просто настройте необходимые пути ( analyses/source_directory
и export/directory
):
(emerge) user@host tmp % cat java-template.yaml
---
project_name: java_project_example
loglevel: info
analyses:
- analysis_name: full java check
source_directory: /Users/user1/emerge/project/source
only_permit_languages:
- java
only_permit_file_extensions:
- .java
file_scan:
- number_of_methods
- source_lines_of_code
- dependency_graph
- fan_in_out
- louvain_modularity
- tfidf
entity_scan:
- dependency_graph
- source_lines_of_code
- number_of_methods
- fan_in_out
- louvain_modularity
- tfidf
export:
- directory: /Users/user1/emerge/project/export
- graphml
- json
- tabular_file
- tabular_console_overall
- d3
(emerge) user@host tmp %
После этого вы можете просто запустить сканирование с помощью
(emerge) user@host tmp % emerge -c java-template.yaml
2021-12-04 21:18:15 analysis I starting to analyze java_project_example
2021-12-04 21:18:15 analysis I ⏩ performing analysis 1/1: full java check
2021-12-04 21:18:15 analysis I starting to create filesystem graph in full java check
2021-12-04 21:18:15 analysis I ⏩ starting scan at directory: ...
...
...
...
2021-12-04 21:18:27 analysis I ✅ all your generated/exported data can be found here: /Users/user1/tmp/java
2021-12-04 21:18:27 analysis I ✅ copy the following path to your browser and start your web app: file:///Users/user1/tmp/java/html/emerge.html
2021-12-04 21:18:27 analysis I ✅ total runtime of analysis: 00:00:10 + 154 ms
Теперь просто скопируйте вышеупомянутый путь file://
в любой современный веб-браузер и интерактивно просматривайте настроенную кодовую базу.
Вы можете клонировать этот репозиторий и установить его, следуя этой инструкции:
git clone https://github.com/glato/emerge.git
graphviz
. brew install graphviz
Если вы столкнулись со следующей ошибкой на Apple Silicon Mac
pygraphviz/graphviz_wrap.c:2711:10: fatal error: ' graphviz/cgraph.h ' file not found
# include "graphviz/cgraph.h"
^~~~~~~~~~~~~~~~~~~
1 error generated.
вам нужно один раз запустить следующую команду, чтобы обновить каталоги включения pygraphviz для новой среды homebrew
pip install --global-option=build_ext --global-option= " -I $( brew --prefix graphviz ) /include/ " --global-option= " -L $( brew --prefix graphviz ) /lib/ " pygraphviz
См. проблему в контексте здесь.
Убедитесь, что на вашем macOS установлена последняя версия Python 3. Я рекомендую установить/использовать Python 3 от Homebrew. Создайте виртуальную среду Python 3 (необязательно в структуре проекта).
cd emerge
pip3 install virtualenv
virtualenv -p python3 venv
Установите необходимые пакеты и создайте виртуальную среду Python 3 (необязательно в структуре проекта).
apt-get install python3-venv python3-dev graphviz graphviz-dev
cd emerge
python3 -m venv venv
source venv/bin/activate
Установите все необходимые зависимости для проекта с помощью pip.
pip install -r requirements.txt
Установите пакет Wheel, после этого установите все необходимые зависимости для проекта с помощью pip.
pip install wheel
pip install -r requirements.txt
Выполните следующее из корня клонированного проекта:
python -m unittest discover -v -s ./emerge -p "test_*.py"
в противном случае выполните скрипт run_tests.py
:
python run_tests.py
Если у вас возникли какие-либо проблемы при выполнении тестов, проверьте это обходное решение.
emerge
как отдельный инструмент. (emerge) user@host emerge % python emerge.py
usage: emerge.py [-h] [-c YAMLCONFIG] [-v] [-d] [-e] [-a LANGUAGE]
? Welcome to emerge x.y.z (yyyy-mm-dd hh:mm:ss)
options:
-h, --help show this help message and exit
-c YAMLCONFIG, --config YAMLCONFIG
set yaml config file
-v, --verbose set logging level to INFO
-d, --debug set logging level to DEBUG
-e, --error set logging level to ERROR
-a LANGUAGE, --add-config LANGUAGE
add a new config from a template, where LANGUAGE is one of [JAVA, SWIFT, C, CPP, GROOVY, JAVASCRIPT,
TYPESCRIPT, KOTLIN, OBJC, RUBY, PY, GO]
Давайте быстро попробуем запустить require на его собственной кодовой базе.
python emerge.py -c configs/emerge.yaml
Это должно дать аналогичный результат:
... analysis I starting to analyze emerge
... analysis I ⏩ performing analysis 1/1: self-check
... analysis I starting to create filesystem graph in self-check
... analysis I ⏩ starting scan at directory: .
... ...
... analysis I the following statistics were collected in self-check
+-------------------------------------+-------------------+
| statistic name | value |
+-------------------------------------+-------------------+
| scanning_runtime | 00:00:00 + 61 ms |
| scanned_files | 32 |
| skipped_files | 176 |
| parsing_hits | 313 |
| parsing_misses | 141 |
| extracted_file_results | 32 |
| file_results_creation_runtime | 00:00:00 + 538 ms |
| number-of-methods-metric-runtime | 00:00:00 + 4 ms |
| source-lines-of-code-metric-runtime | 00:00:00 + 11 ms |
| louvain-modularity-metric-runtime | 00:00:00 + 161 ms |
| fan-in-out-metric-runtime | 00:00:00 + 4 ms |
| total_runtime | 00:00:00 + 786 ms |
+-------------------------------------+-------------------+
... analysis I the following overall metrics were collected in self-check
+----------------------------------------------+----------------------------+
| metric name | value |
+----------------------------------------------+----------------------------+
| avg-number-of-methods-in-file | 13.0 |
| avg-sloc-in-file | 151.41 |
| total-sloc-in-files | 4845 |
| louvain-communities-dependency-graph | 3 |
| louvain-modularity-dependency-graph | 0.21 |
| louvain-biggest-communities-dependency-graph | 0.49, 0.46, 0.05, 0.0, 0.0 |
| avg-fan-in-dependency-graph | 5.55 |
| avg-fan-out-dependency-graph | 5.55 |
| max-fan-in-dependency-graph | 29 |
| max-fan-in-name-dependency-graph | typing |
| max-fan-out-dependency-graph | 19 |
| max-fan-out-name-dependency-graph | emerge/appear.py |
+----------------------------------------------+----------------------------+
... analysis I ✅ all your generated/exported data can be found here: /Users/user1/tmp/python
... analysis I ✅ copy the following path to your browser and start your web app: file:///Users/user1/tmp/python/html/emerge.html
... analysis I ✅ total runtime of analysis: 00:00:00 + 786 ms
Теперь просто скопируйте указанный выше путь file://
в любой современный веб-браузер и интерактивно просматривайте базу кода Ergo.
s
, чтобы выбрать и выделить или отменить выбор определенного узла.r
f
А теперь давайте сделаем это интереснее...
Если вы хотите использовать «emerging» в других проектах, вы можете просто скопировать или настроить один из существующих шаблонов конфигурации из каталога emerge/configs
.
Для быстрого запуска должно быть достаточно настроить source_directory
и directory
в export
.
---
project_name : c-example-project
loglevel : info
analyses :
- analysis_name : check_c_files
source_directory : /Users/user1/emerge/project/source/github/linux-5.8.5/crypto
only_permit_languages :
- c
only_permit_file_extensions :
- .c
- .h
ignore_dependencies_containing :
- string.h
ignore_dependencies_matching :
- ^test_(.*).h$
file_scan :
- number_of_methods
- source_lines_of_code
- dependency_graph
- louvain_modularity
- fan_in_out
- tfidf
export :
- directory : /Users/user1/emerge/project/export
- graphml
- json
- tabular_file
- tabular_console_overall
- d3
После настройки существующей конфигурации (например, config/c-template.yaml
) или создания своей собственной, просто запустите require еще раз с этой новой конфигурацией.
python emerge.py -c configs/c-template.yaml
После сканирования результаты сканирования (включая интерактивное веб-приложение) можно будет найти в каталоге, который вы создали и установили в параметре конфигурации export
-> directory
, как показано в журналах выше.
Полная конфигурация YAML, содержащая сканирование файлов и объектов, имеет следующий формат:
---
project_name : java_project_example
loglevel : info
analyses :
- analysis_name : check_java_files_and_classes
source_directory : /Users/user1/emerge/project/source
only_permit_languages :
- java
only_permit_file_extensions :
- .java
ignore_dependencies_containing :
- java.util
file_scan :
- number_of_methods
- source_lines_of_code
- dependency_graph
- fan_in_out
- louvain_modularity
- tfidf
entity_scan :
- dependency_graph
- source_lines_of_code
- number_of_methods
- fan_in_out
- louvain_modularity
- tfidf
export :
- directory : /Users/user1/emerge/project/export
- graphml
- json
- tabular_file
- tabular_console_overall
- d3
Иногда имеет смысл исключить обычные для платформы зависимости или зависимости, которые не способствуют пониманию проекта. Хорошей отправной точкой, например, для проекта Android, может стать следующий раздел ignore_dependencies_containing
:
ignore_dependencies_containing :
- android
- java
- javax
или для проекта iOS часто имеет смысл следующий раздел ignore_entities_containing
, например, чтобы не учитывать предварительный просмотр SwiftUI для вывода графика:
ignore_entities_containing :
- _Previews
Конфигурация yaml в основном определяется на следующих уровнях:
ключ | стоимость/описание |
---|---|
project_name | имя проекта для всех анализов, сканирований и экспорта |
loglevel | установите уровень журнала: error (без звука, только ошибки), info (включает error ) дает вам базовые журналы о потоке управления, debug (включает info ) создает множество журналов отладки |
analyses | массив анализов, которые можно настроить индивидуально, поэтому проект может содержать от одного до нескольких анализов. |
ключ | стоимость/описание |
---|---|
analysis_name | конкретное название анализа |
source_directory | исходный каталог, в котором должно начаться рекурсивное сканирование файлов |
git_directory | каталог репозитория git, если должны быть включены метрики git |
git_commit_limit | сколько коммитов из последнего коммита следует добыть? по умолчанию: 150 |
git_exclude_merge_commits | следует ли исключить коммиты слияния из анализа всех метрик? по умолчанию: true |
ignore_files_containing | исключить из проверки имена файлов, содержащие заданные подстроки |
ignore_directories_containing | исключить из проверки имена каталогов, содержащие заданные подстроки |
only_permit_languages | возможные значения включают: java, kotlin, objc, Swift, Ruby, groovy, javascript, c — явно запрещает сканирование любого другого языка, кроме того, который вы здесь установили. |
only_permit_file_extensions | явно разрешите следующие расширения файлов, которые вы здесь установили, например .java |
only_permit_files_matching_absolute_path | Для сканирования файлов разрешен только следующий список абсолютных путей к файлам, например [/Users/user1/source/file1.java] . Файлы должны следовать за source_directory |
ignore_dependencies_containing | игнорировать все зависимости, включенные в этот список подстрок, например java.util |
ignore_dependencies_matching | игнорировать каждую зависимость, соответствующую любому из регулярных выражений в этом списке подстрок, например ^java.util. |
ignore_entities_containing | игнорировать все объекты, включенные в этот список подстрок, например NotRelevantClass |
ignore_entities_matching | игнорировать каждую сущность, соответствующую любому из регулярных выражений в этом списке подстрок, например ^Test |
import_aliases | определить список псевдонимов импорта, т.е. заменить подстроки в полном пути зависимости, например "@foo": src/foo заменит любой псевдоним @foo на src/foo |
override_resolve_dependencies | если это поддерживается анализатором языка, принудительно разрешить каждую зависимость в этом списке. |
override_do_not_resolve_dependencies | если это поддерживается синтаксическим анализатором языка, принудительно НЕ разрешать каждую зависимость в этом списке (т. е. рассматривать ее как глобальную зависимость) |
file_scan | выполнить сканирование файла, содержит метрики, которые следует применять к каждому исходному файлу |
entity_scan | выполнить сканирование объекта, содержит метрики, которые следует применять к каждому объекту (например, к каждому классу) |
export | содержит любые форматы экспорта, которые должны быть созданы в качестве выходных данных. |
appconfig | содержит любые настраиваемые параметры конфигурации приложения |
ключ | стоимость/описание |
---|---|
dependency_graph | создадим структуру графа зависимостей на основе исходных файлов, в узлы графа будут добавлены дополнительные метрики |
source_lines_of_code | применить метрику исходных строк кода к каждому файлу, создать общую метрику |
number_of_methods | применить метрику нескольких методов к каждому файлу, создать общую метрику |
fan_in_out | применить метрику графика разветвления/разветвления к каждому файлу, создать общую метрику |
louvain_modularity | применить метрику модульности Лувена к каждому файлу, создать общую метрику |
tfidf | применить метрику tfidf к каждому файлу и извлечь соответствующие семантические ключевые слова |
ws_complexity | применить метрику сложности пробелов к каждому файлу |
git_metrics | включите некоторые метрики на основе git и попытайтесь применить их к каждому файлу |
ключ | стоимость/описание |
---|---|
dependency_graph | создать структуру графа зависимостей на основе извлеченных сущностей из файлов, в узлы графа будут добавлены дополнительные метрики |
inheritance_graph | создать структуру графа наследования на основе извлеченных сущностей из файлов, в узлы графа будут добавлены дополнительные метрики |
complete_graph | создать полную структуру графа (объединение графа зависимостей/наследования) на основе извлеченных сущностей из файлов, в узлы графа будут добавлены дополнительные метрики |
source_lines_of_code | применить метрику исходных строк кода к каждому объекту, создать общую метрику |
number_of_methods | применить несколько методов метрики к каждому объекту, создать общую метрику |
fan_in_out | применить метрику графика разветвления/разветвления к каждому объекту, создать общую метрику |
louvain_modularity | применить метрику модульности Лувена к каждому объекту, создать общую метрику |
tfidf | применить метрику tfidf к каждому объекту и извлечь соответствующие семантические ключевые слова |
ключ | стоимость/описание |
---|---|
directory | выходной каталог для всех указанных форматов экспорта |
graphml | создать файл GraphML, содержащий структуру графа и результаты метрик, сопоставленные с узлами графика. |
tabular_file | создать текстовый файл в табличном формате, содержащий все показатели и статистические результаты. |
tabular_console | распечатать вывод в табличном формате на консоль, содержащий все показатели и статистические результаты |
tabular_console_overall | распечатать вывод в табличном формате на консоль, который содержит только общие показатели и статистические результаты |
json | создайте файл JSON, содержащий все показатели и статистические результаты |
d3 | создайте веб-приложение Bootstrap/D3 в подпапке force-graph-html для дальнейшего визуального и интерактивного/исследовательского анализа. |
ключ | стоимость/описание |
---|---|
radius_fan_out | Коэффициент умножения радиуса узла для метрики разветвления, по умолчанию: 0.1 |
radius_fan_in | Коэффициент умножения радиуса узла для метрики разветвления, по умолчанию: 0.1 |
radius_louvain | Коэффициент умножения радиуса узла для метрики Лувена, по умолчанию: 0.02 |
radius_sloc | коэффициент умножения радиуса узла для метрики sloc, по умолчанию: 0.005 |
radius_number_of_methods | Коэффициент умножения радиуса узла для метрики количества методов, по умолчанию: 0.05 |
heatmap_sloc_active | Следует ли включать метрику Sloc в расчет показателя тепловой карты? по умолчанию: true |
heatmap_fan_out_active | Следует ли включать показатель разветвления в расчет показателя тепловой карты? по умолчанию: true |
heatmap_sloc_weight | весовой коэффициент метрики Sloc при расчете оценки тепловой карты, по умолчанию: 1.5 |
heatmap_fan_out_weight | весовой коэффициент метрики разветвления при расчете оценки тепловой карты, по умолчанию: 1.7 |
heatmap_score_base | минимальный порог оценки для цветовой карты тепловой карты, по умолчанию: 10 |
heatmap_score_limit | максимальный порог оценки для цветовой карты тепловой карты, по умолчанию: 300 |
Emerge поддерживает следующие расширения файлов и типы сканирования для каждого языка, тогда как file_scan
просто вычисляет метрики и сопоставляет узлы внутри структур графа со сканируемыми файлами, entity_scan
пытается извлечь из файлов более детальные объекты, например классы или структуры.
Расширение файла | Языковой парсер | Файлы | Сущности |
---|---|---|---|
.java | Ява | ✅ | ✅ |
.swift | Быстрый | ✅ | ✅ |
.c / .h / .hpp | С | ✅ | |
.cpp / .h / .hpp | С++ | ✅ | |
.groovy | классный | ✅ | ✅ |
.js / .jsx | JavaScript | ✅ | |
.ts / .tsx | Машинопись | ✅ | |
.k | Котлин | ✅ | ✅ |
.m / .h | Цель-C | ✅ | |
.rb | Руби | ✅ | |
.py | Питон | ✅ | |
.go | Идти | ✅ |
Интерпретация таких графиков часто может быть очень субъективной и зависеть от проекта. Следующие примеры должны помочь распознать определенные закономерности с помощью индикаторов и подсказок.
Магия раскрытия модульности заключается в применении алгоритма обнаружения сообщества, например, оптимизации Лувена, к графу с силовым управлением, так что и расстояния, и раскраска влияют на результат. Следующий пример включает несколько индикаторов для модульной базы кода.
В первом примере слева вы можете заметить несколько когерентных цветных кластеров, которые демонстрируют низкую связь на определенном расстоянии (= генерируется графиком, ориентированным на силу).
Во втором примере справа тот же график отображается с активированными оболочками кластера. В этом примере корпуса перекрываются минимально или вообще не перекрываются. Такие подсказки могут быть индикаторами хорошей архитектуры программного обеспечения, например, с точки зрения модульности , абстракции и четко определенных интерфейсов .
«БОЛЬШОЙ КОМ ГРЯЗИ — это беспорядочно структурированные, разбросанные, небрежные, с изолентой и проволокой, джунгли спагетти-кода» (Б. Фут, Дж. Йодер, 1997). Этот тип графа часто представляет собой менее оптимальную архитектуру. Чтобы проверить подобные спагетти-кодовые джунгли , можно просто включить рендеринг оболочки для всех кластеров, чтобы окончательно определить: в конце концов, существует только один большой кластер.
Иногда игнорирование нерелевантных зависимостей может помочь лучше понять сложность архитектуры программного обеспечения.
ignore_dependencies_containing
(или ignore_dependencies_matching
если вы предпочитаете регулярные выражения). При сравнительно активированной метрике разветвления можно распознать большее рассеяние, некоторые удаленные узловые узлы и более четкие кластеры. Все это возможные подсказки к реальной (= часто более понятной) архитектуре.