分割コンパイル (2018 年度 ) 担当 : 笹倉 佐藤 2018.12.20 分割コンパイルとは 一つのプログラムのソースを複数のソースファイルに分けてコンパイルすること ある程度大きなプログラムの場合ソースファイルをいくつかに分割して開発するのが普通 1
なぜ分割コンパイルするのか 1. コンパイル時間を短縮するため 2. ソースコードを見やすくするため 3. ソースコードを再利用しやすくするため 4. 複数人での開発をしやすくするため 分割コンパイル演習 クイックソートのプログラムで試してみる. 1. ~sasakura/compiler/2007/src/qsort.c を自分のところにコピーする 2. このプログラムを main 関数だけのソースファイル main.c と quicksort 関数だけのソースファイル quicksort.c の二つに分ける. 3. それぞれをコンパイルする 2
分割コンパイル演習 コンパイルのしかた gcc -o main main.c gcc -o quicksort quicksort.c gcc -c main.c gcc -c quicksort.c gcc -o qsort main.o quicksort.o それぞれのソースファイルをコンパイルしてオブジェクトファイルを作っている オブジェクトファイル同士をリンクしている 分割コンパイル演習 quicksort.c の中のコメントアウトしてある部分のコメントを外して ( アンコメント ) 動くようにする ( この部分はクイックソートの途中経過を表示するもの ) 3
分割コンパイル演習 コメントを外しただけでは動かない理由 1. MAXNUM を知らないと言われる 2. data を知らないと言われる 対処方法 1. #define MAXNUM 10 を quicksort.c にいれる 2. extern data[]; を quicksort.c にいれる 大域変数と分割コンパイル 大域変数のスコープは通常はそのソースファイルの中 他のソースファイルで宣言されている大域変数を参照したいときは参照したい方のソースファイルで extern 宣言をする 大域変数はプログラムを読みにくくするので極力使わない方がいい 4
プロトタイプ宣言と分割コンパイル 他のソースファイルにある関数のプロトタイプ宣言はなくてもコンパイルエラーにはならない しかし, それはコンパイル時に検査していないだけ もし型が違えばリンク時にエラーが出る よって, 使用する関数についてはプロトタイプ宣言をしておいた方がよい ヘッダファイルの活用 記号定数,extern 宣言, 関数のプロトタイプ宣言は main.c にも quicksort.c にも同じものを書かなくてはならない 面倒 共通のものをヘッダファイルにしてそれを include するようにする 他にも共通の typedef などはヘッダファイルにいれると便利 5
ヘッダファイルの例 myheader.h (main.c, quicksort.c と同じディレクトリに作る ) #define MAXNUM 10 extern int data[]; void quicksort(int[], int, int); #include myheader.h を main.c と quicksort.c にいれる make の利用 毎回以下の用なコマンドを手で打つのは面倒 gcc -c main.c gcc -c quicksort.c gcc -o qsort main.o quicksort.o make (GNU make) を使って楽をしよう 6
make とは ファイルの更新時間をみて処理を行うためのツール 主に分割コンパイルの支援のために使われる Unix で標準でついてくるツール make の基本 デフォルトでは Makefile (makefile でもいい ) に処理の規則を書く 処理の規則の書き方 FileB : FileA1 FileA2. <tab> 指定するコマンド FileA1, FileA2, のファイルのうちどれか一つでも更新時間が FileB よりも新しければ指定するコマンドを実行する ここは必ず タブ でなければならない 7
Makefile の例 以下の内容の Makefile をソースファイルのおいてあるディレクトリで作成する qsort : main.o quicksort.o gcc -o qsort main.o quicksort.o main.o : main.c myheader.h gcc -c main.c quicksort.o : quicksort.c myheader.h gcc -c quicksort.c make とすると, 更新されたものだけコンパイルされる でも, これだとソースファイルが増えるたびにいちいち同じようなことを書かないといけない 面倒 賢い make の使い方 マクロの利用 長いものに別名をつけて何度も書かなくていいように 変更も容易 暗黙のルールの利用 よく使うルールはあらかじめ make が知っている. 例えば.c ファイルからは.o ファイルを作る. 8
CC = gcc CFLAGS = -O -Wall HDRS = myheader.h LDFLAGS = LIBS = OBJS = main.o quicksort.o PROGRAM = qsort all: $(PROGRAM) Makefile の例 2 $(PROGRAM): $(OBJS) $(HDRS) $(CC) $(OBJS) $(LDFLAGS) $(LIBS) -o $(PROGRAM) clean:; rm -f *.o *~ ### main.o: main.c myheader.h quicksort.o: quicksort.c myheader.h make についてもっと知りたい人は http://www.ecoop.net/coop/translated/gnumake3.77/make_toc.jp.html などを参照のこと yacc,lex を使うときの makefile の例 CC = gcc CFLAGS = -O Wall LEX = flex YACC = bison -d HDRS = parse.tab.h LDFLAGS = -lfl -ly LIBS = OBJS = parse.tab.o lex.yy.o ast.o PROGRAM = mycompiler all: $(PROGRAM) $(PROGRAM): $(OBJS) $(HDRS) $(CC) $(OBJS) $(LDFLAGS) $(LIBS) -o $(PROGRAM) lex.yy.c: lex.l $(LEX) lex.l parse.tab.c: parse.y $(YACC) parse.y clean:; rm -f *.o *~ ### ast.o: ast.c ast.h 9