Introduction
Acording to ThoughtWorks, Continuous Integration (CI) is a development practice that requires developers to integrate code into a shared repository several times a day. Each check-in is then verified by an automated build, allowing teams to detect problems early. By integrating regularly, you can detect errors preemptively, and locate them more easily.
Commonly used CI services in the open source community are Travis CI and AppVeyor. Travis CI supports numerous languages as well as builds on Linux and OS X. AppVeyor, on the other hand, is a Windows build system. Many people use them simultaneously to support cross-platform development. In this article, in addition to a CI service, CMake is used to manage the build process while unit testing is done via the Boost Test Library.
Example
The example code is arranged in a common folder structure. The first three code blocks contain the C++11 source code, header file, and a boost unit test. The last three code blocks are the CMake, Travis CI, and AppVeyor build scripts. If you place the code on GitHub and add the project to Travis CI and AppVeyor, then successful builds should occur. Furthermore, Travis CI will run two builds (gcc and clang) and AppVeyor one (VS2015).
src/foo.cpp
int foo()
{
auto x = 1; return x;
}
include/foo.hpp
#ifndef FOO_HPP
#define FOO_HPP
int foo();
#endif
test/test.cpp
#define BOOST_TEST_MODULE test
#include <boost/test/unit_test.hpp>
#include "foo.hpp"
BOOST_AUTO_TEST_CASE(testFoo)
{
BOOST_CHECK(foo() == 1);
}
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(myProject)
# Add source code and setup folder structure.
set(src src/foo.cpp)
set(test test/test.cpp)
set(include include/foo.hpp)
include_directories(include)
# Add Boost unit test library.
set(Boost_USE_STATIC_LIBS on)
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
include_directories(${Boost_INCLUDE_DIR})
# Add C++11 compiler flags.
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
add_definitions(-std=c++11 -stdlib=libc++ -O3 -Wall)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
add_definitions(-std=c++11 -O3 -Wall)
endif()
# Create executable that links the source code, unit test, header file, and Boost.
add_executable(exe ${src} ${test} ${include})
target_link_libraries(exe ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
# Add support for unit tests e.g. ctest.
enable_testing()
add_test(myTest exe)
.travis.yml
matrix:
include:
# Build with Linux and gcc.
# The default gcc compiler does not support C++11 so install a higher version.
- os: linux
env: CC=gcc-5 CXX=g++-5
addons: &gcc5
apt:
packages:
- libstdc++-5-dev
sources:
- ubuntu-toolchain-r-test
install:
- sudo apt-get update -qq
- sudo apt-get install -qq g++-5
- sudo apt-get install -y libboost-test-dev
# Build with OS X and clang.
# The default clang compiler supports C++11.
- os: osx
env: COMPILER=clang++
script:
- mkdir build
- cd build
- cmake ..
- make
- ctest
appveyor.yml
os: Visual Studio 2015
# Boost is already installed on AppVeyor.
environment:
BOOST_ROOT: C:\Libraries\boost_1_59_0
BOOST_LIBRARYDIR: C:\Libraries\boost_1_59_0\lib64-msvc-14.0
build_script:
- md build
- cd build
- cmake -G "Visual Studio 14 2015 Win64" ..
- cmake --build . --config Release
- ctest
Discussion
To speed up the build process, only a subset of the boost library is used, namely, unit_test_framework
. However, this code can be modified to support other parts of the boost library.
Sometimes, you'll see dynamic linking to the boost library. However, it was necessary to statically link to the library on AppVeyor. This was accomplished by not including in test/test.cpp.
#define BOOST_TEST_DYN_LINK
and including in CMakeLists.txt:
set(Boost_USE_STATIC_LIBS on)