예제 http://blog.naver.com/delusion1224/50175392681 

[출처] Makefile 예제|작성자 착각쟁이

 

makefile 예제

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

CC = gcc

CFLAGS = -D_DEBUG

TARGET = test

SRCS = $(wildcard *.c)

OBJECTS = $(SRCS:.c=.o)

 

.SUFFIXES : .o .c

%.o : %.c

$(CC) $(CFLAGS) -c -o $@ $<

 

all : $(TARGET)

 

$(TARGET) : $(OBJECTS)

$(CC)  $(CFLAGS) -o $@ $^

 

clean :

rm -rf *.o $(TARGET)

 

4~5 line은 다음과 같이 한 줄로 축약될 수도 있다

OBJECTS = $(patsubst %c, %o, $(wildcard *.c))

 

7~9 line은 현재 Makefile에 정의된 CC와 CFLAGS를 사용하기 위해 추가된 내용이다.

해당 내용이 없더라도 각 object들은 자동으로 compile이 되지만, make 내부에 정의된 매크로가 사용된다.

 

make 내부에 정의된 매크로에 대한 자세한 내용은 make -p를 해보면 알 수 있다.

다음은 make -p의 일부분

...

#기본값

COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c 

...

%.o: %.c
#  실행할 명령어 (내장):
 $(COMPILE.c) $(OUTPUT_OPTION) $<

...

 

추가로, Makefile 작성 시 주의해야 할 점은,

각 명령어들은 다른 shell에서 실행된다.

즉, 다음과 같이 Makefile을 작성하면,

 ...

del :

cd ./backup

rm -rf *

두 명령이 각자 다른 shell에서 실행되기 때문에 현재 folder의 모든 내용이 삭제가 된다.

올바른 방법은 다음과 같이 한 line에 적는것이다.

cd ./backup && rm -rf *

&& 대신 ;를 사용해도 되지만 backup folder가 없을 경우에는 역시 현재 folder의 내용들이 삭제 되므로 좋지 않다.

[출처] Makefile 예제|작성자 착각쟁이

http://wiki.kldp.org/KoreanDoc/html/GNU-Make/GNU-Make.html#toc2


아래와 같은 강좌가 있는데 자주 잊어먹으니 포스팅 하자



GNU Make 강좌

임대영 RAXIS@hitel.net

v1.0, 1997년 8월 28일


이 글에서는 컴파일 과정과 같이 반복되는 작업을 효과적으로 처리하는 GNU-Make에 대해서 설명한다.

1. make (만든다 ?)

2. 간단한 Makefile

3. 매크로(Macro) 와 확장자(Suffix) 규칙

4. Makefile를 작성할 때 알면 좋은 것들

5. make 중요 옵션 정리

6. Makefile 작성의 가이드라인

7. Makefile의 실제 예제

8. make 수행 시에 나타나는 에러들


이전페이지 다음페이지 차례



세그멘테이션 폴트는 수행 중인 프로그램이 메모리 상에 이상한 주소를 접근하려 할 때 발생한다. 여러분이 만든 프로그램이라면 세그멘테이션 폴트는 아마도 포인터가 뭔가 이상한 곳을 가리키고 있다는 말일 것이다. 그러나 표준 유닉스유틸리티인 경우 결국 같은 내용이겠지만 아마 프로그램에게 다룰 수 없는 이상한 입력을 준다든지 했을 것이다


프로그램을 중단시킬 수 있는 대부분의 버그들은 ‘SegmentationFault’라는 메시지를 남긴다. 이 메시지의 ‘fault’라는 말은 결함, 실수 등의 의미임을 쉽게 생각할 수 있다. 하지만 ‘segmentation’은 무슨 의미일까? 

세그먼테이션은 운영체제에서 메모리 관리(Memory Management)의 한 종류이다. 세그먼테이션을 정의한다면 완전히 머신(machine) 독립적인 주소 공간을 할당해주는 직접적인 방법이라고 할 수 있다. 

예를 들어, 컴파일러가 소스를 컴파일 할 때 다음과 같은 테이블을 만들 수 있다. 

  • 텍스트 소스를 저장하는 공간(테이블)
  • 변수의 특성, 이름 등을 저장하는 심벌 공간
  • 정수, 부동소수 등으로 지정된 상수를 저장하는 공간
  • 프로그램을 문법적으로 분석한 Parse tree
  • 컴파일러가 사용한 프로시저 콜 스택
이러한 여러 가지의 테이블을 세그먼테이션 방법에서는 각각 세그먼트라는 독립된 주소 공간을 할당하게 된다. 앞의 그림은 세그먼트를 나타낸 것이다. 반면에 가상 주소(Virtual Memory-pa ging의 대표적인 방법)라는 방법에서는 일정한 크기의 주소 공간을 여러 영역으로 나눠(세그먼테이션과는 다르게 일차원적이다) 앞의 여러 가지 테이블을 저장하게 된다. 

세그먼테이션을 사용할 때의 장점에는 여러 가지가 있지만 우선 각 프로시저가 주소 번지 0에서 시작되는 분리된 세그먼트를 갖기 때문에 독립된 프로시저의 연결이 단순해진다. 또한 여러 개의 프로세스가 프로시저나 데이터를 쉽게 공유할 수 있다. 이러 장점은 세그먼테이션이 가상 메모리와는 달리 그 크기가 유동적이라는 데서 기인한다.





Bus error 

이것은 성격상 세그먼테이션 폴트와 유사하다. 그러나 미묘한 차이가 있는데 버스에러란 커널이 스스로 문제점을 발견하지 못했다는 점에서다. 어떤 문제가 있음을 메모리 시스템(즉 하드웨어)이 발견한다. 많은 시스템에서느 이 메시지는 입출력 연산을 잘못 시도했음을 말한다. (있지도 않은 장치를 접근하려 했거나 뭔가 자연적이지 못했을때 등등)





- skt_wipi_c를 컴파일 할때 오류가 난 부분인데 wipi_sdk 버전을 낮추니 문제가 해결되었다. 상위 버전으로 컴파일 할때 다시 오류가 날지 모르겠다...


출처 : Tong - dicastyle님의 Compiler통

writer : Martin Brown (questions@mcslp.com), Freelance writer and consultant
 
http://www.ibm.com/developerworks/jp/linux/library/l-ccache/index.html

 

UNIXでC/C++を使ったアプリケーション開発での標準的なビルド・プロセスにおいては、gccのようなコンパイラーと、makeのように何らかのビルド・ツールを使います。makeやその他どんなCコンパイラーでも問題になるのは、Cのプリプロセッサーがヘッダー・ファイルをどう扱うかです。典型的なCソースファイルを見れば、様々なヘッダー・ファイルへの#include参照がいくつか入っています。

Cプリプロセッサー(cpp)はファイルをコンパイルする度に、こうした各ファイルを、また参照される予定のファイルをすべて構文解析し、そしてインクルードします。その内容を構文解析することによってcppは、ごく基本的な1 KBのソースファイルだったものを8 KBのソースファイルにし、やがて何十もの、時には何百ものソースファイルを取り込むのです。一般的な開発プロジェクトでは、関係のあるヘッダー・ファイルは様々なソースファイルに何度もインクルードされ、またそれぞれのヘッダー・ファイルが数多くの他のヘッダー・ファイルを参照している場合もあります。

典型的なビルドでは、makeツールは対象が最後にビルドされた後に変更されたファイルだけをコンパイルすることによって、大幅にプロセスを単純化します。例えばリスト1のディレクトリを見ると、foo.oオブジェクトは、対応するfoo.cソースファイルを最後に変更した時よりも古いことが分かります。一方bar.oはbar.cよりも新しくなっています。適切に設定されたMakefileを使えば、ソースから再コンパイルされるのはfoo.oのみになります。

makeでは変更されたソースファイルのみをコンパイルすることによって、コンパイルすべきソースファイルの数を制限しますが、それでもまだ無駄があります。プロジェクトをコンパイルする度に、アセンブラーそして最終的にマシンコードにコンパイルされるまでに、ソースファイルはcppに構文解析されます。各ファイルにとって見ると、これは毎回ヘッダー・ファイルを再構文解析することになっている可能性があります。ビルドのプロセス全体で考えると、同じヘッダー・ファイルを何度も構文解析し、プロセッサー・サイクルを浪費している可能性があります。さらに重要なことは、ビルドのプロセスが完了するまで待たせることによって、開発者の時間を浪費してしまうのです。チーム全体として見ると、複数の開発者がそのプロセスを何度も繰り返すかもしれず、しかも日によってはそれを同時に行う可能性もあることから、その影響はさらに重大です。


リスト1. ソース環境の例
                
total 808
-rw-------  1 mc  mc    5123 24 Jul 14:17 bar.c
-rw-------  1 mc  mc   39474 24 Jul 14:19 bar.o
-rw-------  1 mc  mc    7856 24 Jul 14:17 foo.c
-rw-------  1 mc  mc   28443 24 Jul 14:19 foo.o
-rwx--x--x  1 mc  mc  319742 24 Jul 14:19 foobar*
-rw-------  1 mc  mc    1045 24 Jul 14:21 foobar.h

ccacheを使う

ccache(compiler cacheの省略)ツールはコンパイルで生成された情報をキャッシュし、例えばヘッダー・ファイルのようにビルドの特定な部分のキャッシュ情報を使うことで、通常はcppで情報を構文解析するために使われるはずの時間を節約します。例えばリスト2のファイルのコンパイルについて言えば、(foobar.hが他のヘッダー・ファイルへの参照を含んでいるとすると)ccacheはincludeステートメントを、cppで構文解析した方のファイルで置き換えるのです。これほど単純なのです。ccacheは実際に内容を読み取って理解し解釈するのではなく、(それまではファイルとなるべき最終テキストであった)あとはコンパイルするだけだったものを、単にコピーするのです。


リスト2. ソースファイルの内容
                
#include "foobar.h"
void main(void)
{
}

インストール

ccacheのインストールも、それを使うのも、皆さんが想像するほど複雑なものではありません。ccacheは今までのコンパイラーの使い方には全く影響を与えることはなく、皆さんとコンパイラーの間のインターフェースとして動作するのです。ですから、必要に応じて使うか使わないかという選択ができます。ccacheをインストールするにはSambaグループから直接ソースをダウンロードするか、あるいはローカルのミラー(参考文献)からダウンロードします。そしてダウンロードしたものを解凍します。

$ bunzip2 -c ccache-2.3.tar.bz2|tar xf -

次のディレクトリに変更します。

$ cd ccache-2.3

configureを実行します。

$ ./configure

ビルドします。

$ make

そして最後にccacheをインストールします。

$ make install

これで準備完了です。

展開

先に述べた通り、ccacheは皆さんと通常のコンパイラーの間に位置することによって動作します。ですからgccを呼ぶ代わりに、gccを最初の引き数としてccacheを呼びます。例えばコマンドラインからファイルをコンパイルするには、通常次のようにします。

$ gcc foo.c

ccacheを使うには次のようにタイプします。

$ ccache gcc foo.c

こうしたファイルを一度だけコンパイルしても、特にccacheを使ってファイルをコンパイルするのが初めてであれば、コンパイル情報はまだキャッシュされていないので、何ら利点は感じないでしょう。ですから、一般的にはccacheを設定して、恒久的にメインのコンパイラーとして入れ替える方が効果的です。そのためにはCC環境変数の値を設定します。

$ export set CC='ccache gcc'

ccacheをプロジェクト・ベースでのみ使用可能にしたいのであれば、例えばPerlのようなサードパーティーのツールをコンパイルする時にのみ使用可能にしたいのであれば、環境のトリックを使うか、configureスクリプトでそのように指定するか、またはどのCコンパイラーを使うかを指定するようなコマンドを作ります。

キャッシュを制御する

ccacheはデフォルトで、カレント・ユーザーのホーム・ディレクトリ($HOME/.ccache)内のディレクトリを使ってキャッシュ情報を保持します。チーム環境では、誰もがビルド中にキャッシュ情報を使えるように、中心となる場所をキャッシュ用に使いたいでしょう。もう一つの環境変数CCACHE_DIRがキャッシュ・ディレクトリの位置を規定します。単一マシンの環境では、必要な人が誰でもアクセスできるようなディレクトリにこれを置きます。充分なメモリーがあるとして、さらに高速にするためには、tmpfsを使ってマウントされたディレクトリを使います。これによってさらに10%から25%高速化することができます。

ネットワークを経由して接続したマシンでccacheを使用するのであれば、必ず共有するディレクトリがNFSでエクスポートされ、各クライアントにマウントされるようにします。この場合でも、さらに高速化したい場合にはtmpfsファイルシステムを使うことができます。

キャッシュをさらに細かく制御できるようなオプションが他にも幾つかあります。

  • CCACHE_LOGFILE環境変数は、ログファイル(ccacheを使うと中身が入ります)の場所を定義します。
  • ccacheで-sコマンドライン・オプションを使うと、キャッシュのパフォーマンス統計が分かります(リスト3)。
  • -Mコマンドライン・オプションを使うと、キャッシュの最大サイズを設定できます。デフォルトは1 GBです。キャッシュに対する設定はキャッシュ・ディレクトリに書き込まれます。ですから別々のユーザーやグループに対して、別々の場所で別々のキャッシュ・サイズを設定することができます。
  • -Fオプションは最大ファイル数を設定しますが、一番近い16の倍数に丸められます。-Mと同様、デフォルトを変更したい時にのみ使用します。
  • -cオプションはキャッシュを整理します。ccacheは実行中に情報を更新するため、通常はこれを使うべきではありませんが、しばらく使っていなかったキャッシュ・ディレクトリを再び使用するような時には、このオプションを使うことができます。
  • -Cオプションはキャッシュを完全にクリアします。

リスト3. ccacheキャッシュの統計
                
cache hit                             44
cache miss                           152
called for link                      107
compile failed                        11
no input file                          2
files in cache                       304
cache size                           8.8 MB
max cache size                     976.6 MB

初期オプションを設定し、ディレクトリやキャッシュ・サイズを設定してしまえば、何も変更する必要はありません。定期的なメンテナンスを行う必要もありません。

ccacheとdistccを組み合わせる

皆さんは、同じくSambaグループの別のツールdistccを既にご存じかも知れません。distccを使うと、何台かのマシンにコンパイルのプロセスを分散させることができます。そのため、あたかもmakeで(-jコマンドライン・オプションを使って)複数ジョブ・オプションを使ったかのように、起こり得る同時コンパイルの数を実質的に増やすことができます。各ホストでは、ソースファイルを構文解析前の最終形式で受け付けてから(出来上がったオブジェクト・ファイルを戻す前に)ファイルをローカルでコンパイルするデーモンがあり、distccシステムはこのデーモンを使うことで動作します。

distccはソースファイル全体を分散するので、効果があるのは一つ以上のソースファイルがあるようなプロジェクトのみです。適切に使った場合、同等な新しいノードを追加する毎にビルド時間が減少する割合は、直線的な減少よりもやや少ないものになります。

distccは構文解析された状態でファイルを分散するので、Cのプリプロセス部分をスピードアップするccacheとdistccを組み合わせて、オブジェクト・コードを生成する実際のコンパイルを行うことができます。distccとccacheをこの方法で使うには、自分のホストでdistccを設定し、メインの開発マシンでdistccとccacheを設定します。

リスト4のように、プロジェクトをビルドしたいマシンで環境変数を設定します。


リスト4. ccacheとdistccを使うための環境変数
                
export set DISTCC_HOSTS='localhost atuin nautilus pteppic kernel'
export set CCACHE_DIR=/Data/Cache/CCache
export set CCACHE_PREFIX=distcc
export set CCACHE_LOGFILE=/Data/Cache/CCache.log
export set CC='ccache gcc'

環境変数は次のように定義されます。

  • DISTCC_HOSTS は作業を分散する先のホストを指定します。
  • CCACHE_DIR はccacheディレクトリの位置を指定します。
  • CCACHE_PREFIX はccacheが(プリプロセスの後)ソースをコンパイルする真のコンパイラーを呼ぶ時に使うプレフィックスを指定します。
  • CC は最初に使うCコンパイラー(ccache)の名前を設定します。

同時に実行するコンパイルの数を-jオプションで指定してmakeを実行すると、ファイルはdistccのホストの一台に分散される前に、最初にccacheで(必要な場合にはそのキャッシュを使って)構文解析されます。

distccはコンパイルのプロセスをスピードアップしますが、環境の持つ基本的な制約を変更するわけではありません。例えば、makeが操作する同時ジョブの数は、使用できるCPUの数の2倍以上にすべきではありません。例えば4台の2CPUマシンの場合、ジョブの値を16以上に設定しても、ほとんどスピードの改善は感じられないでしょう。

統計

さて、全てが設定できたところで、どのくらい差があるものかを見てみましょう。ここではPerlをビルドする一連のテストを実行しました。ccacheは構文解析されたヘッダー・ファイルをキャッシュした時に最も効果があるので、コンパイルするにはよく身の詰まったものが必要です。これは単にmakeフェーズであり、標準のconfigureを(configure.gnuを使って)実行した後に起こります。コードのコンパイルに関係したものだけではなく、全てのステージを含んでいますが、非コンパイラー操作は全体的な統計には影響しません。

先に述べた通り、ccacheの効果は最初のコンパイルでは感じられません。以前のプリプロセッサーのパスを再利用する時に、その違いが出るのです。表1に示す再コンパイル時間は、メインのPerlソース・ディレクトリにあるCソースファイルのそれぞれを、ごくわずかいじった結果に基づいています。ここでは4ノードのネットワークで同時に行われるdistccジョブの様々な値を使って、ごく普通のgccでビルドした場合、ccache+gccでビルドした場合、ccache+distcc+gccでビルドした場合の時間を計測しています。


表1. 再コンパイル時間
環境 時間
gcc (1回目)   8m02.273s
gcc (再コンパイル)   3m30.051s
ccache+gcc (1回目)   8m54.714s
ccache+gcc (再コンパイル)   0m45.455s
ccache+distcc+gcc -j4   4m14.546s
ccache+distcc+gcc -j4 (再コンパイル)   0m38.985s
ccache+distcc+gcc -j8   3m13.020s
ccache+distcc+gcc -j8 (再コンパイル)   0m34.380s

何と素晴らしい! ccacheを使うだけでPerlのビルド時間をほとんど3分(実は2分45秒ですが)の短縮です。これはすべて、ccacheが事前に構文解析されたヘッダー・ファイルを保存しておいて使うためであり、各ソースファイルに対して常にcppを再実行することがないからです。プロセスの中にdistccも取り入れれば、再コンパイル時間が少し早くなると同時に、全体的なスピードも向上します。

まとめ

この記事では、ccacheのように比較的単純で使いやすいツールを使うことによって、どれほどスピードが改善されるかを見てきました。ccacheをdistccと組み合わせて使えば、さらにコンパイル時間を短縮することができます。こうしたツールをチーム環境で使えば、コンパイル時間だけで毎日何時間も節約できることになります。開発メンバーにとってはコーヒー・ブレークの時間が少なくなることを意味するかも知れませんが、アプリケーションの開発時間も短縮できるのです。


参考文献

 

빨리 집에갈려면 익혀둬야 할 기술 

+ Recent posts