2013年6月18日火曜日

CppNcss を使ってみた (+Jenkins)

CppNcss というコードメトリクスツールを知ったので試してみました。
CppNcss のダウンロードはこちらからしました。

セットアップ
ダウンロードしたファイルを解凍すると、バイナリなどが入っているフォルダが展開されるので適当な場所に保存します。
このとき、パスに "空白" が含まれていると動作しないので気をつけてください。

cppncss\bin にパスを通します。
コマンドプロンプトを開いて、cppncss -h[ENTER] と打ちます。
>cppncss -h

Usage: cppncss [options] [file [file2 [directory [directory2] ...]]]
Version: 1.0.3

Options:
  -h                      print this message
  -d                      print debugging information
  -v                      be extra verbose
  -k                      keep going on parsing errors
  -r                      process directories recursively
  -x                      output result as xml
  -m=<measurements>       output the <measurements> sorted in given order, defau
lt is equivalent to -m=NCSS,CCN,function
  -n=<number>             output only the top <number> results
  -f=<file>               output result to <file>
  -D<symbol>[=[<value>]]  replace define <symbol> with <value>
  -M<symbol>[=[<value>]]  replace macro <symbol> with <value>
  -p=<path>               remove <path> prefix when displaying file names

See http://cppncss.sourceforge.net for more information.
上記のようにヘルプが表示されれば OK です。

使ってみる
試しに使ってみました。解析対象は iutest を使用しました。
include フォルダ以下すべてを解析させるので、 -r オプションをつけています。
>cppncss -r include
---- 省略 ----
Average Function NCSS: 4.86
Average Function CCN: 2.14

Nr. NCSS CCN Functions File
  1  106  45        21 include\gtest\iutest_assertion_only.hpp
  2    0   0         0 include\gtest\iutest_gtest_ver.hpp
  3    0   0         0 include\gtest\iutest_spi_switch.hpp
Average File NCSS: 35.33
Average File CCN: 15.00
Average File Functions: 7.00

Project NCSS: 106
Project CCN: 45
Project Functions: 21
全部のファイルが解析されていないようです。
-k オプションをつけてパーサーエラーが発生しても続行するようにします。
>cppncss -k -r include
---- 省略 ----
Average Function NCSS: 3.75
Average Function CCN: 1.61

Nr. NCSS CCN Functions File
  1  228  81        38 include\internal\iutest_list.hpp
  2  120  42        30 include\iutest_core.hpp
  3  112  56        10 include\util\iutest_util_tests.hpp
  4  106  45        21 include\gtest\iutest_assertion_only.hpp
  5   98  39        30 include\iutest_result.hpp
  6   83  24        11 include\internal\iutest_console.hpp
  7   76  26        22 include\internal\iutest_message.hpp
  8   70  21        14 include\internal\iutest_params_util.hpp
  9   67  23        21 include\iutest_body.hpp
 10   62  16        15 include\internal\iutest_filepath.hpp
 11   46   3         3 include\internal\iutest_option_message.hpp
 12   44  22        18 include\util\iutest_util_quiet_result_printer.hpp
 13   36  12        11 include\internal\iutest_random.hpp
 14   35  10         9 include\internal\iutest_result_reporter.hpp
 15   31  10        10 include\internal\iutest_factory.hpp
 16   30   9         5 include\internal\iutest_exception.hpp
 17   25   1         1 include\internal\iutest_stdlib.hpp
 18   24   4         4 include\internal\iutest_mediator.hpp
 19   18   4         3 include\internal\iutest_pool.hpp
 20   13   2         2 include\iutest_static_assertion.hpp
 21   11   4         2 include\gtest\switch\iutest_switch_inform.hpp
 22   10   4         4 include\gtest\switch\iutest_switch_filepath.hpp
 23    5   0         0 include\internal\iutest_regex.hpp
 24    2   0         0 include\util\iutest_util_output.hpp
 25    1   0         0 include\internal\iutest_tuple.hpp
 26    1   0         0 include\iutest_util.hpp
 27    0   0         0 include\gtest\iutest_gtest_ver.hpp
 28    0   0         0 include\gtest\iutest_spi_switch.hpp
 29    0   0         0 include\gtest\switch\iutest_switch_assert.hpp
 30    0   0         0 include\gtest\switch\iutest_switch_core.hpp
 31    0   0         0 include\gtest\switch\iutest_switch_expect.hpp
 32    0   0         0 include\gtest\switch\iutest_switch_no_failure.hpp
 33    0   0         0 include\gtest\switch\iutest_switch_package.hpp
 34    0   0         0 include\gtest\switch\iutest_switch_peep.hpp
 35    0   0         0 include\gtest\switch\iutest_switch_pmz.hpp
 36    0   0         0 include\gtest\switch\iutest_switch_pred.hpp
 37    0   0         0 include\gtest\switch\iutest_switch_skip.hpp
 38    0   0         0 include\gtest\switch\iutest_switch_throw_value.hpp
 39    0   0         0 include\internal\iutest_compiler.hpp
 40    0   0         0 include\internal\iutest_internal.hpp
 41    0   0         0 include\internal\iutest_pp.hpp
 42    0   0         0 include\internal\iutest_pragma.hpp
 43    0   0         0 include\iutest_config.hpp
 44    0   0         0 include\iutest_ignore.hpp
 45    0   0         0 include\iutest_ver.hpp
Average File NCSS: 30.09
Average File CCN: 10.18
Average File Functions: 6.31

Project NCSS: 1354
Project CCN: 458
Project Functions: 284

できました。

NCSS と CCN
NCSS は "Non Commenting Source Statements"
CCN は "Cyclomatic Complexity Number"
の略です。計測できるのはこの2つだけのようです。

Jenkins との連携
CPPNCSS Plugin があるのでインストールします。

適当に CppNcss を行うプロジェクトを作成したら、
[ビルド後の処理の処理の追加] から [Publish Cpp NCSS Report] を選択します。

xml ファイルのパスと、関数単位でのステートメント数と複雑度の閾値を設定します。

こんな感じで集計、グラフ化できました。


CCCC との比較
C/C++ のコードメトリクスツールには他にも SourceMonitorCCCC などがあります。
個人的に CCCC を使っているので比較をしてみたいと思います。
(※ CCCC についてはこちらも参考にしてください)

計測できる項目
CppNcssCCCC概要
Number of Modulexoモジュール(クラス)数
Line of Codexoコード行数
Non Commenting Source Statementsoxステートメント数
Cyclomatic Numberoo複雑度
Line of Commentxoコメント行数
LOC/COMxo1コメントあたりのコード行数
MVG/COMxo1コメントあたりの複雑度
Information Flow measure (inclusive)xoモジュール間結合
Information Flow measure (visible)xoモジュール間結合
Information Flow measure (concrete)xoモジュール間結合
Weight Methods per Class (unity)xo関数の重み(重み=1)
Weight Methods per Class (visible)xo関数の重み(重み=外部アクセスの可否)
Depth of Inheritance Treexo継承の深さ
Number of Childrenxo子クラスの数
Coupling between objectsxoクラスが依存する外部クラスの数
Lines of Code rejected by parserxo解析に失敗した行数

ステートメント数
CppNcss で計測できるのは、ステートメント数(NCSS)です。
一方 CCCC で計測できるのは、コード行数(LOC)です。

以下のコードで比較してみました。
class X
{
    int value;
public:
    int getValue() { return value; }
    void setValue(int v)
    {
        value = v;
    }
};

int f()
{
    int a=0;
    int b=0, c=0;
    
    // loop
    for( int i=0; i < 10; ++i )
    {
        do
        {
            while(1)
            {
                ++a;
                break;
            }
        } while(0);
    }
    
    if( a != b )
    {
        if( a != c ) {
            X x;
            x.SetValue(a);
            c = x.
                GetValue();
        } else {
        }
    }
    else
    {
    }
    
    a = b; b = c; c = a;
    return b;
}

int main(int, char**)
{
    f();
    return 0;
}

CppNcss の結果
Nr. NCSS CCN Function
1 19 6 f() at main.cpp:14
2 3 1 main( int, char** ) at main.cpp:50
3 2 1 X::getValue() at main.cpp:7
4 2 1 X::setValue( int ) at main.cpp:8
Average Function NCSS: 6.50
Average Function CCN: 2.25

CCCC の結果

全く別物であることがわかりますね。


さて、ステートメント数の計算についてもう少し調べてみたいと思います。
今度はこちらのコードを CppNcss にかけます。

int a=0;

void for_()
{
    for( int i=0; i < 10; ++i )
    {
        ++a;
    }
}

void do_()
{
    do
    {
        ++a;
    } while(1);
}

void while_()
{
    while(1)
    {
        ++a;
    }
}

void if_()
{
    if(1)
    {
        ++a;
    }
    else
    {
    }
}

void elseif_()
{
    if(0)
    {
    }
    else if(1)
    {
        ++a;
    }
    else
    {
    }
}

void switch_()
{
    switch(a)
    {
    case 0:
        ++a;
        break;
    }
}

void switch2_()
{
    switch(a)
    {
    case 0:
        ++a;
        break;
    case 1:
        break;
    }
}

void switchdefault_()
{
    switch(a)
    {
    case 0:
        ++a;
        break;
    default:
        break;
    }
}

void cond_()
{
    a > 0 ? --a : ++a;
}

class A
{
public:
    int get() const { return 1; }
};
class B
{
    A a;
public:
    const A* get() const { return &a; } 
};

B b;
void call_()
{
    b.get();
}
void call2_()
{
    b.get()->get();
}

こちらのコードを解析した結果がこちら。
Nr. NCSS CCN Function
1 7 3 switch2_() at main.cpp:62
2 7 2 switchdefault_() at main.cpp:74
3 6 3 elseif_() at main.cpp:38
4 5 2 switch_() at main.cpp:52
5 4 2 if_() at main.cpp:27
6 3 2 for_() at main.cpp:3
7 3 2 do_() at main.cpp:11
8 3 2 while_() at main.cpp:19
9 2 2 cond_() at main.cpp:86
10 2 1 A::get() at main.cpp:94
11 2 1 B::get() at main.cpp:100
12 2 1 call_() at main.cpp:104
13 2 1 call2_() at main.cpp:108
Average Function NCSS: 3.69
Average Function CCN: 1.85

想定した結果になりました。とくに言うことはありませんね。
(switchdefault_ と switch2_ で CCN が違うのはこういうもん?)

複雑度
CppNcss の複雑度の計算方法がわかりませんが(CCCC は McCabe)、
こちらも上記の最初のコードで比較してみました。

CppNcssCCCC
f67
main11

これだけではわかりにくいので、Visual Studio 2012 の Win32 プロジェクトのテンプレートコードで比較してみました。
計測できなかったので、スルー。
iutest の結果で比較しようと思いましたが、どちらか片方が計測できていないものばかりで、良いサンプルがなかったので割愛させてもらいます。m(__)m

c++ ファイル以外の計測
CppNcss では、.cpp や .c など標準的な拡張子のファイルしか計測してくれません。
なので、.ipp など独自拡張子を計測することができません。

CCCC の場合は、コマンドライン引数に --lang=c++ とすることで、独自ファイルの計測も可能です。

Jenkins との連携
CCCC の Jenkins との連携はちょっと残念なかんじです。
プロジェクトやモジュール単位の結果をみることができるのですが、関数単位での結果が見れません。
この関数単位での結果が一番みたいところなんですが…


CCCC の結果 xml/html ファイルには関数単位の結果も書き込まれているので、
Jenkins でそれらを見たい場合は HTML Publisher Plugin を使うとよいでしょう。
また、独自に xml を解析して Jenkins で集計可能な xml にするのも手です。
そのへんについては、
ブログズミ: CCCC + Jenkins + HTML Publisher Plugin
ブログズミ: CCCC + Jenkins
を参考にしてください。

CppNcss の方は関数単位の結果を表示してくれます。
Jenkins プラグインは CppNcss の方がイイ感じです。

総評
CppNcss は CCCC と比べて、解析出来る内容は少ないですが手軽に使えそうです。
また、ステートメント数が計測できるのと、CCCC では計測失敗する箇所でも CppNcss なら計測できる場合もあるので、CppNcss と CCCC を両方使うのもありだと思いました。

あとは、集めた情報をどう活かすかになりますが、それはまた今度の機会にでも…(できたら…

0 件のコメント:

コメントを投稿