自作言語GELを作る日記

C++で自作言語を作るブログ

自作言語を作る その3 ~プロジェクト作成~

この記事ではプロジェクトの作成を行います。ディレクトリ構造やビルド手順等を解説します。

開発環境

項目
OS ubuntu14.04
コンパイラ g++4.8.4
言語 C++11
ビルドツール make

ディレクトリ構成

プロジェクトのディレクトリ構成は以下の様にします。

  • src/ ソースコード
  • test/ テストコード
  • bin/ 実行ファイル出力先
  • build/ オブジェクトファイル出力先
  • include/ 外部ヘッダ置き場
  • lib/ 外部ライブラリ置き場

ビルド手順

make で実行ファイル出力を、
make test でテスト実行が行われるよう Makefile を記述します。

.PHONY: all test clean

PROGRAM      := ./bin/gel
SOURCES      := $(wildcard src/*.cc)
DEPENDS      := $(addprefix build/, $(patsubst %.cc, %.d, $(SOURCES)))
OBJS         := $(addprefix build/, $(patsubst %.cc, %.o, $(SOURCES)))

TEST_PROGRAM := ./bin/test
TEST_SOURCES := $(wildcard test/*.cc)
TEST_DEPENDS := $(addprefix build/, $(patsubst %.cc, %.d, $(TEST_SOURCES)))
TEST_OBJS    := $(addprefix build/, $(patsubst %.cc, %.o, $(TEST_SOURCES)))

INCLUDE_DIR  := ./include ./src
LIB_DIR      := ./lib
LIBS         :=

CC           := g++
CCFLAGS      := -Wall $(addprefix -I ,$(INCLUDE_DIR)) $(addprefix -isystem ,$(INCLUDE_DIR)) $(addprefix -L ,$(LIB_DIR)) $(addprefix -l, $(LIBS)) -std=c++1y

all: $(DEPENDS) $(PROGRAM)

test: $(DEPENDS) $(TEST_DEPENDS) $(TEST_PROGRAM)
	@$(TEST_PROGRAM)

$(PROGRAM): $(OBJS)
	@mkdir -p `dirname $@`
	$(CC) $(CCFLAGS) -o $@ $^

build/%.d: %.cc
	@mkdir -p `dirname $@`
	$(CC) $(CCFLAGS) -MM $< > $@

build/%.o: %.cc
	@mkdir -p `dirname $@`
	$(CC) $(CCFLAGS) -o $@ -c $<

$(TEST_PROGRAM): $(filter-out build/src/main.o, $(OBJS)) $(TEST_OBJS)
	@mkdir -p `dirname $@`
	$(CC) $(CCFLAGS) -o $@ $^ 

ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEPENDS)
endif

ifeq "$(MAKECMDGOALS)" "test"
-include $(TEST_DEPENDS)
endif

clean:
	rm -rf build bin

簡単なテストを作ってみる

簡単なプログラムを作成し、それのテストコードを書いて make, make test, make clean コマンドの動作を試します。

プログラム

整数の加算する関数 addとそれを呼び出すmain関数を含むコードを作成します。

  • src/add.h
#pragma once

int add(int a,int b);
  • src/add.cc
#include "add.h"

int add(int a, int b){
    return a+b;
}
  • src/main.cc
#include <iostream>
#include "add.h"

int main(){
    std::cout << add(1,2) << std::endl;
}

これで、makeすると bin/gel が生成されます。

テストコード

テストフレームワークとして、doctestというライブラリを利用します。
doctestはヘッダオンリーのライブラリなので、includeディレクトリに doctest.h を配置するだけで利用可能になります。

今回はテストコードとして、エントリーポイントを含むmain.ccとadd関数をテストするtest_add.ccを作成します。

  • test/main.cc
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
  • test/test_add.cc
#include "add.h"
#include "doctest.h"

TEST_SUITE( "suite" );

TEST_CASE( "case" ) {
    CHECK_EQ( add(1,2), 2 ); // わざと失敗させる.
}
 
TEST_SUITE_END();

これで、make testを実行するとテストが実行されます。

[doctest] doctest version is "1.1.4"
[doctest] run with "--help" for options
===============================================================================
test/test_add.cc(6)
case

test/test_add.cc(7) FAILED! 
  CHECK_EQ( add(1,2), 2 )
with expansion:
  CHECK_EQ( 3, 2 )

===============================================================================
[doctest] test cases:    1 |    0 passed |    1 failed |    0 skipped
[doctest] assertions:    1 |    0 passed |    1 failed |

ちゃんと意図通りテスト失敗となっていることが確認できました。

最後に clean

make clean を実行すれば、オブジェクトファイルと実行ファイルが削除されることが確認できました。

おわりに

プロジェクト作成まで終わったので、次回から実際の開発を行っていこうと思います。次回はlexを用いた字句解析を行う予定です。