自作Cコンパイラのビルド基盤をMakeからCMakeに移行してます(途中)
- compiler bookの古い版のmake file
CFLAGS=-std=c11 -g -static SRCS=$(wildcard *.c) OBJS=$(SRCS:.c=.o) ycc: $(OBJS) $(CC) -o ycc $(OBJS) $(LDFLAGS) $(OBJS): ycc.h test: ycc ./test.sh clean: rm -f ycc *.o *~ .PHONY: test clean
適当に以降要件を洗い出してみます。
- debug buildできること
- ISO C11の規格に対応したビルドにすること
- テストが実行できるようにすること
※全てのコマンドはプロジェクトの最上位(.gitが格納されているフォルダ)から打ち始めるものとします
debug build
このMakefileだとビルドスクリプト内にdebug optionがベタ書きになっていますが、cmakeの場合は実行時に
mkdir ../debug_build && cd ../debug_build cmake -DCMAKE_BUILD_TYPE=Debug ../ycc cmake --build .
このオプション付きでビルドするとデバッグ情報がバイナリに埋めこれます。gdbで確認してみます
$ gdb ycc -q Reading symbols from ycc...done. (gdb) list 1 #include "ycc.h" 2 3 4 // 現在着目しているトークン 5 Token *token; 6 7 // 入力プログラム 8 char *user_input; 9 10 (gdb)
-DCMAKE_BUILD_TYPE=Debug
なしだとシンボル情報は埋め込まれません
mkdir ../build && cd ../build cmake ../ycc cmake --build .
- check
$ gdb ./ycc -q Reading symbols from ./ycc...(no debugging symbols found)...done. (gdb) list No symbol table is loaded. Use the "file" command. (gdb)
C11オプション
- これをCMakeLists.txtの最初の方に書くだけ。
set(CMAKE_C_STANDARD 11)
テスト(途中)
- testは全てctestに書き換えます
- originalのテスト用のshell script
test.sh #!/bin/bash try() { expected="$1" input="$2" ./ycc "$input" > tmp.s gcc -o tmp tmp.s ./tmp actual="$?" if [ "$actual" = "$expected" ]; then echo "$input => $actual" else echo "$input => $expected expected, but got $actual" exit 1 fi } try 0 '0;' ...テストケース省略... try 8 'returnx = 3;return returnx + 5;' echo OK
- テストデータに大量のメタ文字を含んでいるため、cmake上でのデータ受け渡しに苦労しています
- 今の私のcmake力だとテストの完全対応は難しそうです。
- しばらくコマンドからテストを叩くことにします
../test.sh 0; => 0 42; => 42 5+20-4; => 21 12 + 34 - 5; => 41 5+6*7; => 47 5*(9-6); => 15 (3+5)/2; => 4 -(-3); => 3 -(3-11); => 8 -(-3*+5); => 15 4==4; => 1 3==-3; => 0 4!=-3; => 1 -3!=-3; => 0 3<4; => 1 -3<-4; => 0 -4 <= 100; => 1 4<=4; => 1 8<=11; => 1 -3<=-4; => 0 2>-4; => 1 21>199; => 0 10>=10; => 1 1 >=0; => 1 -101 >= -100; => 0 a = 3;b = 5 * 6 - 8; a + b / 2; => 14 aa=10;b=20;ccc=b-aa;ccc+1; => 11 aa=10;bb=20;bb=bb/2;aa==bb; => 1 a = 3;b = 5 * 6 - 8;return a + b / 2; => 14 return 5;return 8; => 5 returnx = 3;return returnx + 5; => 8 OK
結果
- こんな感じのcmakefileになってしまいましたが、CLionがドハデにリファクタリングしてくれました...
cmake_minimum_required(VERSION 3.10) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") project(ycc) add_library(container STATIC container.c) add_library(parse STATIC parse.c) target_link_libraries(parse PRIVATE container) add_library(codegen STATIC codegen.c) add_library(main STATIC main.c) target_include_directories(container PRIVATE "${PROJECT_SOURCE_DIR}") target_include_directories(codegen PRIVATE "${PROJECT_SOURCE_DIR}") target_include_directories(parse PRIVATE "${CMAKE_SOURCE_DIR}") add_executable(ycc main.c) target_link_libraries(ycc PRIVATE container parse codegen ) target_include_directories(main PRIVATE "${PROJECT_SOURCE_DIR}")
- refacter後
cmake_minimum_required(VERSION 3.14) project(ycc) set(CMAKE_C_STANDARD 11) include_directories(.) add_executable(ycc CMakeLists.txt codegen.c container.c main.c parse.c ycc.h)
- どっかに設定とかあるんだろうけど、cmake_minimum_requiredのバージョン勝手に変えるのやめてもらえませんかねぇ
ninjaでビルド
せっかくMakeからCMakeに移行したんだから、Makeには出来ないことをやってみようと思います。 CMakeはメタビルドシステムなので下位のビルドツールを柔軟に変更することができます。今回はmakeの変わりにninjaを使います
$ cmake -GNinja ../ycc/ -- The C compiler identification is GNU 7.4.0 -- The CXX compiler identification is GNU 7.4.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Detecting C compile features -- Detecting C compile features - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Detecting CXX compile features -- Detecting CXX compile features - done -- Configuring done -- Generating done -- Build files have been written to: /home/yabu/Documents/study/compiler/ninja_build $ ninja [5/5] Linking C executable ycc $ ls CMakeCache.txt CMakeFiles build.ninja cmake_install.cmake rules.ninja ycc
ninjaで実行バイナリが作成されることが確認できました。