Jenkins で C++ プロジェクトを管理するにはどうすればよいか?

全体図

Jenkins_CPP.png

サンプルプログラム

src

https://github.com/kagyuu/DockerExam/blob/master/jenkins/WarehouseCpp/src/main.cpp

#include <stdio.h>

int main ( ) {
    printf( "hello!\n" );
    return 0;
}

https://github.com/kagyuu/DockerExam/blob/master/jenkins/WarehouseCpp/src/warehouse.h

#ifndef WAREHOUSE_H__
#define WAREHOUSE_H__

class Warehouse {
private:
	int stock;
public:
	Warehouse();
	void putGoods(int amount);
	int takeGoods(int amount);
	int getStock();
};

#endif

https://github.com/kagyuu/DockerExam/blob/master/jenkins/WarehouseCpp/src/warehouse.cpp

#include "warehouse.h"

Warehouse::Warehouse() {
	stock = 0;
}

void Warehouse::putGoods(int amount){
	stock += amount;
}

int Warehouse::takeGoods(int amount){
	if (stock >= amount) {
		stock -= amount;
		return amount;
	}
	int tmp;
	tmp = stock;
	stock = 0;
	return tmp;
}

int Warehouse::getStock() {
	return stock;
}

test

https://github.com/kagyuu/DockerExam/blob/master/jenkins/WarehouseCpp/test/warehouseTest.cpp

#include "warehouse.h"
#include <cppunit/extensions/HelperMacros.h>

using namespace CPPUNIT_NS;

class warehouseTest : public TestFixture {
    CPPUNIT_TEST_SUITE(warehouseTest);
    CPPUNIT_TEST(test1);
    CPPUNIT_TEST(test2);
    CPPUNIT_TEST_SUITE_END();

    void test1(){
    	Warehouse *w = new Warehouse();
    	w->putGoods(100);
        CPPUNIT_ASSERT( 25 == w->takeGoods(25) );
        CPPUNIT_ASSERT( 75 == w->getStock() );
        delete w;
    }

    void test2(){
    	Warehouse *w = new Warehouse();
    	w->putGoods(100);
        CPPUNIT_ASSERT( 100 == w->takeGoods(1000) );
        CPPUNIT_ASSERT( 0 == w->getStock() );
        delete w;
    }
};

CPPUNIT_TEST_SUITE_REGISTRATION(warehouseTest);
test1 : 100 個入庫して 75 個出庫したら残り在庫は 25 個になるはず
test2 : 100 個入庫して 1000 個出庫しようとしても 100 個しか出庫できず、残り在庫は 0 個になるはず
最期に CPPUNIT_TEST_SUITE_REGISTRATION(warehouseTest); でテストクラスを CPPUnit に登録する

https://github.com/kagyuu/DockerExam/blob/master/jenkins/WarehouseCpp/test/mainTest.cpp

#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/TestRunner.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/XmlOutputter.h>

using namespace CPPUNIT_NS;

int main(int argc, char **argv) {
    TestResult controller;
    TestResultCollector result;
    controller.addListener(&result);

    BriefTestProgressListener progress;
    controller.addListener(&progress);

    TestRunner runner;
    runner.addTest(TestFactoryRegistry::getRegistry().makeTest());
    runner.run(controller);

    // compiler output
    CompilerOutputter coutputter(&result, stdCOut());
    coutputter.write();

    // output test result for jenkins 
    std::ofstream ofs("test_result.xml");
    CPPUNIT_NS::XmlOutputter outputter(&result, ofs,"UTF-8");
    outputter.write();

    return result.wasSuccessful() ? 0 : 1;
}
登録されているテストメソッドをあるだけ実行して、結果を XML 形式でファイルに出力する
テストクラスが増えても mainTest.cpp には変更の必要なし

Makefile

https://github.com/kagyuu/DockerExam/blob/master/jenkins/WarehouseCpp/Makefile

APPNAME=warehouse
APPMAIN=./src/main.cpp
TESTMAIN=warehouseTest
###########################################################################
# Which compiler
CC=g++
FC=g77
###########################################################################
# Where to install
TARGET=./
###########################################################################
# Where are include files kept
LIBS=-lcppunit
INCLUDES=-I./src -I./test
###########################################################################
# Compile option
CFLAGS=-g -Wall -coverage

SRC:=$(filter-out $(APPMAIN),$(wildcard ./src/*.cpp))
TEST:=$(wildcard ./test/*.cpp)
OBJ:=$(SRC:.cpp=.o) $(TEST:.cpp=.o)

###########################################################################
# Control Script
all: clean compile report
clean:
	find ./ -name *.o -exec rm -v {} \;
	find ./ -name *.gcno -exec rm -v {} \;
	find ./ -name *.gcda -exec rm -v {} \;
	-rm $(APPNAME)
	-rm $(TESTMAIN)
	-rm *_result.xml
	-rm doxygen_*
	-rm -rf html
	-rm -rf latex

###########################################################################
# Body
compile: $(OBJ)
	$(CC) $(CFLAGS) $(OBJ) -o $(TESTMAIN) $(LIBS)

%.o : %.cpp
	$(CC) $(CFLAGS) $(LIBS) $(INCLUDES) -c $< -o $@

report:
	./$(TESTMAIN)
	gcovr --xml --output=gcover_result.xml src/
	cppcheck --enable=all --xml src 2> cppcheck_result.xml
	doxygen doxygen.conf > /dev/null
src/main.cpp を除いて、src/*.cpp test/*.cpp をビルドする。main() は test/mainTest.cpp の main() になる
本番プロジェクトでは、製品ビルド用の Makefile とは別に Makefile_test とかを作って $ make -f Makefile_test でテスト・静的解析を行えばいいだろう。今回は Makefile でテスト・静的解析をやっている。

Doxygen

$ doxygen -g doxygen.conf

で、設定ファイルのひな形ができる。doxygen.conf を修正する。たとえば、

$ diff -u doxygen.conf.org doxygen.conf
--- doxygen.conf.org
+++ doxygen.conf
@@ -409,7 +409,7 @@
 # normally produced when WARNINGS is set to YES.
 # The default value is: NO.
 
-EXTRACT_ALL            = NO
+EXTRACT_ALL            = YES
 
 # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
 # be included in the documentation.
@@ -758,7 +758,7 @@
 # spaces.
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  =
+INPUT                  = src
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses

コマンドラインから実行してみる

$ make
g++ -g -Wall -coverage -lcppunit -I./src -I./test -c src/warehouse.cpp -o src/warehouse.o
g++ -g -Wall -coverage -lcppunit -I./src -I./test -c test/warehouseTest.cpp -o test/warehouseTest.o
g++ -g -Wall -coverage -lcppunit -I./src -I./test -c test/mainTest.cpp -o test/mainTest.o
g++ -g -Wall -coverage ./src/warehouse.o ./test/warehouseTest.o ./test/mainTest.o -o warehouseTest -lcppunit
./warehouseTest
warehouseTest::test1 : OK
warehouseTest::test2 : OK
OK (2)
gcovr --xml --output=gcover_result.xml src/
cppcheck --enable=all --xml src 2> cppcheck_result.xml
Checking src/main.cpp...
1/2 files checked 19% done
Checking src/warehouse.cpp...
2/2 files checked 100% done
Checking usage of global functions..
doxygen doxygen.conf > /dev/null

$ ls
Makefile             doxygen.conf      doxygen_sqlite3.db  html   src   test_result.xml
cppcheck_result.xml  doxygen.conf.org  gcover_result.xml   latex  test  warehouseTest

まぁ、ちゃんと動いたんじゃないでしょうか

Jenkins

  1. Dockerfile

    https://github.com/kagyuu/DockerExam/blob/master/jenkins/Dockerfile

    FROM jenkins
    
    USER root
    
    RUN apt-get -y update && apt-get -y upgrade
    
    # install gcc g++ gfortran
    RUN apt-get -y install build-essential
    
    # install static analysis
    RUN apt-get -y install cppcheck libcppunit* gcovr
    RUN apt-get -y install doxygen graphviz
    
    # install japanese fonts for many charts on jenkins
    RUN apt-get -y install fonts-ipafont
    
    WORKDIR /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/fonts
    RUN mkdir fallback
    RUN cd fallback && ln -s /usr/share/fonts/opentype/ipafont-gothic/ipag.ttf
    RUN cd fallback && ln -s /usr/share/fonts/opentype/ipafont-gothic/ipam.ttf
    # RUN cd fallback && pwd && ls -la
    WORKDIR /
    
    USER jenkins
    
    本家の提供している Docker イメージ( https://github.com/jenkinsci/docker )に、C++ のビルド環境を追加。
    あと、Jenkins で表示するグラフ用に日本語フォントをインストールしておく
  2. Build

    https://github.com/kagyuu/DockerExam/blob/master/jenkins/build.sh

    #!/bin/bash
    
    docker build -t atsushi/jenkins ./
    
    
  3. データを永続化するディレクトリの作成
    $ sudo mkdir /var/jenkins_home
    $ sudo chown 1000 /var/jenkins_home/
    
    UID 1000 は、Docker コンテナの Jenkins ユーザの UID。cf. https://github.com/jenkinsci/docker
  4. 実行

    https://github.com/kagyuu/DockerExam/blob/master/jenkins/run.sh

    #!/bin/bash
    
    docker run -p 8080:8080 -v /var/jenkins_home:/var/jenkins_home atsushi/jenkins
    
    
  5. Plugin のインストール
    Cobertura、xUnit、CppUnit?、Doxygen plugin をインストールする
    plugin01.png
     
    plugin02.png
    一回失敗しても、もう一度やれば別のミラーサイトを見に行って上手くいくかもしれない。

Warehouse プロジェクト

  1. 新しい Job の作成
    job01.png
  2. フリースタイル (Jenkins にビルドしてもらうのではなく make コマンドを実行してビルドする)
    job02.png
  3. ビルドは make コマンド
    job03.png
  4. ビルド後処理で Cobertura、CppCheck?、Doxygen、xUnit を設定する。
    それぞれの plugin の解析対象に、結果レポートの xml ファイルを設定する (xmlのファイル名は Makefile や mainTest.cpp で決めたもの)
    job04.png
     
    job05.png
     
    xUnit plugin では、CppUnit? 形式のレポートを読み取るように設定する
    job06.png
  5. 一度ビルドを走らせて workspace を作成する
    job07.png
  6. ソースコードをコピーする
    $ cp -vR ~/DockerExam/jenkins/WarehouseCpp/* /var/jenkins_home/jobs/Warehouse/workspace/
    

ビルドレポート

  1. 2回目のビルドはうまくいく
    build01.png
  2. Covertura
    build02.png
  3. CppCheck?
    build03.png
  4. CppUnit?
    build04.png
  5. Doxygen
    build05.png
  6. 3回目以降は履歴がグラフで出力される。Jenkins(Java)に日本語フォントを設定していないと、凡例が □ になる。
    build06.png

Computer


添付ファイル: filebuild06.png 2123件 [詳細] filebuild05.png 2114件 [詳細] filebuild04.png 2276件 [詳細] filebuild03.png 2143件 [詳細] filebuild02.png 2223件 [詳細] filebuild01.png 2138件 [詳細] filejob07.png 1890件 [詳細] filejob06.png 1834件 [詳細] filejob05.png 2000件 [詳細] filejob04.png 1879件 [詳細] filejob03.png 1882件 [詳細] filejob02.png 1698件 [詳細] filejob01.png 1668件 [詳細] fileplugin02.png 1683件 [詳細] fileplugin01.png 1716件 [詳細] fileJenkins_CPP.graffle 956件 [詳細] fileJenkins_CPP.png 2126件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS   sitemap
Last-modified: 2015-05-17 (日) 23:42:25 (3491d)
Short-URL: http://at-sushi.com/pukiwiki/index.php?cmd=s&k=b43d922c42
ISBN10
ISBN13
9784061426061