Emerge (またはemerge-viz ) は、ソフトウェア プロジェクトのソース コード構造、メトリクス、依存関係、複雑さに関する洞察を収集する対話型コード分析ツールです。プロジェクトのソース コードをスキャンし、メトリクスの結果と統計を計算し、グラフ構造 (依存関係グラフやファイルシステム グラフなど) を含む対話型 Web アプリを生成し、結果を一部のファイル形式でエクスポートできます。 Emerge は現在、次の言語の解析をサポートしています: C
、 C++
、 Groovy
、 Java
、 JavaScript
、 TypeScript
、 Kotlin
、 ObjC
、 Ruby
、 Swift
、 Python
、 Go
。構造、カラーリング、クラスタリングは、力指向グラフ シミュレーションとルーヴァンのモジュール性を組み合わせるというアイデアに基づいて計算されます。 emerge は主に Python 3 で書かれており、macOS、linux、最新の Web ブラウザ (最新の Safari、Chrome、Firefox、Edge) でテストされています。
出現(/ɪˈməːdʒ/)
- 何かから出たり、何かの後ろから出たりして現れること
- 特に何かを調べたり、それについて質問したりした結果として知られるようになる
このプロジェクトの主な目標は、ソフトウェア開発、アーキテクチャ、メトリクス、視覚化に興味を持つ人なら誰でも簡単に使用して、それらのトピックに関するより多くの洞察を収集できる、無料のオープンソース ツールを作成することです。探索的なアプローチを使用することで、特定のソフトウェア プロジェクトをより深く理解できるよう促進/サポートする必要があります。
C
、 C++
、 Groovy
、 Java
、 JavaScript
、 TypeScript
、 Kotlin
、 ObjC
、 Ruby
、 Swift
、 Python
Groovy
、 Java
、 Kotlin
、 Swift
git-based
メトリクス (SLOC、Whitespace Complexity、Change Coupling) の実験的実装SwiftUI
およびComposable
宣言型 UI エンティティの抽出が含まれますgit-based
メトリクスの視覚化ビルド済みの Docker コンテナーで emerge を使用する最も簡単な方法です。唯一の前提条件は、Docker エンジンがあることです。たとえば、Docker デスクトップです。
このように作業フォルダーを準備します
config.yml
?export
?source
分析を実行するコマンドは次のとおりです。
docker run --rm -v <YOUR_WORKING_FOLDER_PATH>:/tmp/emerge achtelik/emerge:2.0.0 /tmp/emerge/config.yml
最後のパラメータは、Docker コンテナ内の config.yml へのパスです。
⚡実行終了時の 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 コンテナ自体はパスに依存しません。独自のボリューム マウントおよびプロジェクト構成パスを自由に使用してください。
基本的にemergeをインストールするには2つの方法があります。 pip
( pyenv
、 virtualenv
、およびvirtualenvwrapper
使用した仮想環境が推奨されますが、必須ではありません) に精通している場合は、次のいくつかの手順で最新バージョンの emerge を簡単にインストールできます。
推奨される方法は仮想環境を使用することです。次の例を使用してこれを行うことができます。
pyenv install 3.10.0
pyenv virtualenv 3.10.0 venv-3.10.0
pyenv activate venv-3.10.0
pip
使用して簡単に emerge をインストールできます。
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://
パスを最新の Web ブラウザにコピーし、構成されたコードベースを対話的に表示するだけです。
次の手順に従って、このリポジトリのクローンを作成してインストールできます。
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 include ディレクトリを更新するには、次のコマンドを 1 回実行する必要があります。
pip install --global-option=build_ext --global-option= " -I $( brew --prefix graphviz ) /include/ " --global-option= " -L $( brew --prefix graphviz ) /lib/ " pygraphviz
ここで問題の文脈を参照してください。
macOS に最新の Python 3 がインストールされていることを確認してください。 Homebrew から Python 3 をインストールして使用することをお勧めします。 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]
早速、独自のコードベースでemergeを実行してみます。
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://
パスを最新の Web ブラウザにコピーし、emerge コードベースを対話的に公開します。
s
押して、特定のノードを選択して強調表示または選択解除します。r
を押して、現在アクティブなノードの選択をリセットします。f
を押すと、選択されていないノードをすべてフェードアウトして、現在選択しているノードをより強調表示します。それでは、これをさらに面白くしてみましょう...
他のプロジェクトで emerge を使用したい場合は、 emerge/configs
ディレクトリから既存の構成テンプレートの 1 つをコピーまたはカスタマイズするだけです。
簡単に実行するには、 export
のsource_directory
、 directory
調整するだけで十分です。
---
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
) をカスタマイズするか、独自の構成を作成した後、この新しい構成で emerge を再度実行します。
python emerge.py -c configs/c-template.yaml
スキャン後、上記のログに見られるように、スキャン出力 (インタラクティブ Web アプリを含む) は、構成パラメーター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 | 個別に構成できる分析の配列。したがって、プロジェクトには 1 つから多数の分析を含めることができます。 |
鍵 | 値/説明 |
---|---|
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 | さらに視覚的で対話型/探索的な分析を行うために、サブフォルダーforce-graph-html に Bootstrap/D3 Web アプリケーションを作成します。 |
鍵 | 値/説明 |
---|---|
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 entity_scan
、ファイルからより詳細なエンティティ (クラスや構造体など) を抽出しようとします。
ファイル拡張子 | 言語パーサー | ファイル | エンティティ |
---|---|---|---|
.java | ジャワ | ✅ | ✅ |
.swift | 迅速 | ✅ | ✅ |
.c / .h / .hpp | C | ✅ | |
.cpp / .h / .hpp | C++ | ✅ | |
.groovy | グルーヴィー | ✅ | ✅ |
.js / .jsx | JavaScript | ✅ | |
.ts / .tsx | TypeScript | ✅ | |
.k | コトリン | ✅ | ✅ |
.m / .h | 目的-C | ✅ | |
.rb | ルビー | ✅ | |
.py | パイソン | ✅ | |
.go | 行く | ✅ |
このようなグラフの解釈は非常に主観的でプロジェクトに依存することがよくあります。次の例は、インジケーターとヒントを通じて特定のパターンを認識するのに役立ちます。
モジュール性を明らかにする魔法は、ルーヴァン最適化などのコミュニティ検出アルゴリズムを力指向グラフに適用して、距離と色の両方が結果に影響を与えることにあります。次の例には、モジュール式コードベースのいくつかのインジケーターが含まれています。
左側の最初の例では、特定の距離 (= 力指向グラフによって生成された) による低い結合を示す、複数の一貫した色のクラスターを見つけることができます。
右側の 2 番目の例では、同じグラフがアクティブ化されたクラスター ハルを使用してレンダリングされます。この例では、ハルの重なりは最小限かまったくありません。このようなヒントは、モジュール性、抽象化、明確に定義されたインターフェイスなどの観点から、優れたソフトウェア アーキテクチャの指標となる可能性があります。
「大きな泥の塊は、無計画に構成され、不規則に広がり、ずさんで、ダクトテープと柵のワイヤーで覆われた、スパゲッティ コード ジャングルです。」 (B. Foote、J. Yoder、1997)。この種のグラフは、多くの場合、最適化されていないアーキテクチャを表します。この種のスパゲッティ コード ジャングルを検証するには、すべてのクラスタのハル レンダリングを有効にするだけで、最終的に大きなクラスタは 1 つだけであるかどうかを判断できます。
無関係な依存関係を無視すると、ソフトウェア アーキテクチャの複雑さをよりよく理解できる場合があります。
ignore_dependencies_containing
(正規表現を使用したい場合はignore_dependencies_matching
)を使用して、無関係な依存関係を構成的に削除できます。比較的アクティブなファンアウト メトリックでは、より多くの散乱、いくつかの遠く離れたハブ ノード、およびより明確なクラスターが認識されます。これらはすべて、その下にある実際の (= より理解しやすい) アーキテクチャへの手がかりとなる可能性があります。