初めに
本記事では、自分が考えたC++の勉強し易い環境を作成する事を目的として記載しています。
その為、他の人から見たらあれこれあると思いますが・・・お優しい目で見ていいただければなと思います。
更新履歴
- 2020/11/22:libtorch+google testにを合わせたビルド環境に修正。
目的
C++開発での単体テスト勉強やcmakeの勉強を行える環境を構築する事を目的とする。
また、makeコマンドでいい感じにビルドやプロジェクトの作成をできるようにする。
ディレクトリ構成
以下のようディレクトリ構成にしました。
cpp_train/
├─cpp/ # C++の開発環境ディレクトリ
│ ├─project/ # プロジェクトごとにディレクトリを切る
│ ├─ ...
│ ├─.editorconfig # c,h,cpp,hppに対しての設定
│ └─makefile # build,clean,projectのコマンドを記載
├─doc/ # 開発時のドキュメントやメモのディレクトリ
├─Docker/ # Dockerイメージのディレクトリ(環境ごとにディレクトリを切る)
│ ├─ubuntu/
│ │ └─Dockerfile # 必ずDockerfileにする。(変なファイル名にして指定する事はなるべくやりたく無いから)
│ └─windows/ # 今回は省略(サイレントインストール難しかったから)
│ └─Dockerfile
├─scripts/ # 環境開発に対してのスクリプトのディレクトリ(今回は特に必要ないので省略)
│ ├─switch_linux.bat # docker cliでLinuxコンテナに切り替えるスクリプト
│ └─switch_win.bat # docker cliでWindowsコンテナに切り替えるスクリプト
├─template/ # make projectコマンドのテンプレート
│ └─project/
│ ├─include/
│ ├─lib/
│ ├─src/
│ ├─test/
│ └─CMakeLits.txt
├─.gitignore # ignoreファイル
├─docker-compose.yml # マウントを簡略化する為
└─README.md
Dockerイメージ
FROM ubuntu:20.04
ENV BOOST_VERSION 1.70.0
ENV BOOST_FILE_VERSION 1_70_0
ENV GOOGLE_TEST_VERSION 1.10.0
ENV PYTORCH_VERSION 1.7.0
ENV DEBIAN_FRONTEND=noninteractive
# 開発に必要なパッケージをインストール(ダウンロード元のリポジトリを日本サーバーに変更)
RUN sed -i 's@archive.ubuntu.com@ftp.jaist.ac.jp/pub/Linux@g' /etc/apt/sources.list \
&& apt-get -o Acquire::Check-Valid-Until=false -o Acquire::Check-Date=false update \
&& apt-get install --no-install-recommends -y \
tzdata \
build-essential \
zlib1g-dev \
libffi-dev \
libpthread-stubs0-dev \
cmake \
gdb \
curl \
openssh-server \
ca-certificates \
zip \
unzip \
&& apt-get clean \
&& rm -r /var/lib/apt/lists/* \
&& mkdir /tmp/src
# BoostとGoogle testとlibtorchをダウンロードし解凍する。
WORKDIR /tmp/src
RUN curl -OL https://dl.bintray.com/boostorg/release/${BOOST_VERSION}/source/boost_${BOOST_FILE_VERSION}.tar.gz\
&& tar -zxvf boost_${BOOST_FILE_VERSION}.tar.gz \
&& curl -OL https://github.com/google/googletest/archive/release-${GOOGLE_TEST_VERSION}.tar.gz \
&& tar -zxvf release-${GOOGLE_TEST_VERSION}.tar.gz \
&& mkdir ./googletest-release-${GOOGLE_TEST_VERSION}/build \
&& curl -OL https://download.pytorch.org/libtorch/cpu/libtorch-shared-with-deps-${PYTORCH_VERSION}%2Bcpu.zip \
&& unzip libtorch-shared-with-deps-${PYTORCH_VERSION}%2Bcpu.zip
# boostのインストール
WORKDIR /tmp/src/boost_${BOOST_FILE_VERSION}
RUN ./bootstrap.sh --prefix=/usr/local/ --with-libraries=program_options \
&& ./b2 install
# Libtorchのインストール(CPU only)
WORKDIR /tmp/src/libtorch
RUN cp -v -r include/* /usr/local/include \
&& cp -v -r lib/* /usr/local/lib \
&& cp -v -r share/* /usr/local/share/
# Google testのインストール
WORKDIR /tmp/src/googletest-release-${GOOGLE_TEST_VERSION}/build
# libtorchとgoogletestの競合対応(gtestするときはlibtorchのリンクが必須になる)
RUN sed -i -e "11i add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)" ../CMakeLists.txt \
&& cmake ..\
&& make \
&& make install
# インストール作業で出た一時的なファイルを削除
WORKDIR /tmp
RUN rm -rf ./src/
# 最後に作業場を参照
WORKDIR /tmp/project
ハマった点
- ubuntuイメージでとあるpackageをapt-getするとtime zoneを聞かれるので
ENV DEBIAN_FRONTEND=noninteractive
を入れて対応しています。 - Google Testがlibtorchを入れるとABI周りで変なエラーが出力される為
Google Test側のCMakeLists.txt
にコンパイルオプションを追加しています。
docker-compose.yml
マウントを簡略化する為だけに使用しています。
いちいちdockerコマンドをスクリプト化するよりいいかなと思います。
開発プロジェクト配下とプロジェクトテンプレート配下をマウントしています。
書いてて思ったのですが、
build/dockerfileの所がDockerfile指定しちゃっているので、ダサいですね。
version: '3'
services:
dev_linux:
build:
context: .
dockerfile: ./Docker/ubuntu/Dockerfile
tty: True
volumes:
- ./cpp:/tmp/project
- ./template:/tmp/src/template
makefile
ここでは、C++開発を効率的に行う為のコマンドを記載します。
その為、cppディレクトリ直下に置いています。
開発時は常にcppディレクトリ上でコマンドを使用するイメージです。
build
はプロジェクト配下にあるCMakeLists.txtを元にビルドまで行います。
出力は<c++プロジェクト>/build
に格納されるようにしています。
clean
はbuildで作成されたbuildディレクトリを削除する為のコマンドです。
project
はC++開発のプロジェクトテンプレートを、指定した名前のプロジェクト名に置き換えて作成するコマンドです。プロジェクトテンプレートはdocker-compose.yml
でマウントした先にある為そこを指定しコピーを行っています。その後CMakeLists.txtにproject名を記入するコマンドを行っています。
変数名を統一したほうがよかったと思っていますが、
変数必須の書き方忘れてしまった。。
.ONESHELL:
.PHONY: clean build
clean:
-rm -r ./$(dir)/build
build:
cmake -S ./$(dir) -B ./$(dir)/build
cmake --build ./$(dir)/build
.PHONY: project
project:
cp -r /tmp/src/template/project $(name)
sed -i -e "2i project($(name))" $(name)/CMakeLists.txt
template/
C++のプロジェクトテンプレートを格納しているディレクトリです。
最初はシェルコマンドでテンプレートが作成されるようにしようと思ったのですが、
難しいし、編集するのに癖があると思ったので実際にテンプレートプロジェクトを作成コピーさせるようにしました。
CMakeLits.txtファイル1つでビルドとテストが行えるようにしました。
project/
│ ├─include/ # ビルド・テスト両方で必要なソースを展開
│ ├─lib/ # 使用予定ないけどあったほうがそれっぽいので作っています。
│ ├─src/ # ビルドのみに必要なソースを展開
│ ├─test/ # テストのみに必要なソースを展開
│ └─CMakeLits.txt
CMakeLists.txt
CMakeを全然勉強していなかったのでここでかなり苦労しましたw
CMakeは人によって書き方がかなり違ってたりしたので、参考資料を集め公式のドキュメントと照らし合わせながら勉強しました。
工夫点
ここではあえてプロジェクト名を入れていません。
make project
で挿入されます。
testのif文は別に無くてもいいけど
あったほうが個人的に読みやすかったので、つけています。
testのパスもできるしね!
反省点
target_compile_features
で指定しているオプションを変数に
入れてtestとbuildで統一したかったですが、setを使うのは推奨されていないっぽいので
どうしたらいいのかわからず直書きしてしまいました。
cmake_minimum_required(VERSION 3.16)
# 必須
find_package(Torch REQUIRED)
# Set macros
add_definitions(
)
# project
file(
GLOB_RECURSE
SOURCES
${PROJECT_SOURCE_DIR}/src/*.cpp
${PROJECT_SOURCE_DIR}/include/*.cpp
)
# test
file(
GLOB_RECURSE
TEST_SOURCES
${PROJECT_SOURCE_DIR}/test/*.cpp
${PROJECT_SOURCE_DIR}/include/*.cpp
)
# libs
include_directories(
${PROJECT_BINARY_DIR}
${PROJECT_SOURCE_DIR}/include
)
link_directories(
${PROJECT_SOURCE_DIR}/libs
)
# tests
if(NOT out-test)
find_package(GTest REQUIRED)
add_executable(
${PROJECT_NAME}-Test
${TEST_SOURCES}
)
enable_testing()
target_include_directories(
${PROJECT_NAME}-Test PRIVATE
${INCLUDE_DIRECTORIES}
${GTEST_INCLUDE_DIRS}
)
target_link_libraries(
${PROJECT_NAME}-Test PRIVATE
${LINK_DIRECTORIES}
${TORCH_LIBRARIES} #GTestを行う為必須
GTest::GTest
GTest::Main
)
target_compile_features(
${PROJECT_NAME}-Test PRIVATE
cxx_std_14
)
gtest_add_tests(TARGET ${PROJECT_NAME}-Test)
endif()
add_executable(
${PROJECT_NAME}
${SOURCES}
)
target_link_libraries(
${PROJECT_NAME}
${LINK_DIRECTORIES}
${TORCH_LIBRARIES}
)
target_compile_features(
${PROJECT_NAME} PRIVATE
cxx_std_14
)
開発イメージ
-
docker-compose up -d
でイメージをビルドしコンテナを起動 -
dcoekr-compose exec dev_linux /bin/bash
で開発環境にログイン -
make project name=<プロジェクト名>
でプロジェクトを作成 -
make build dir=<3のプロジェクト名>
でビルドを行う。 -
./<プロジェクト名>/build/<プロジェクト名>
で実行ファイルを実行 -
./<プロジェクト名>/build/<プロジェクト名>-Test
でテスト結果を出力
ド素人ながら頑張ったなと思います。
所感
今後はWindows周りのコンテナやCLIを勉強しWindows・Linux環境の両方に対応した環境を構築したいと思っています。がサイレントインストールが難しいです。