C

거믄마루 2012. 11. 1. 15:54
make 스크립트는 왠만한 프로그램 개발자라면 한번씩은 작성해보았을 것이다.
그러나 매우 어렵고 복잡하게만 생각하는 것은 그 내부를 잘 모르기 때문일 것이다.

아주 옛날 버전의 make 만 아니라면 다음과 같은 명령으로 내부 데이터베이스를 확인할 수 있다.

make -p -f /dev/null


결과를 잘 살펴보면 어떻게 make 스크립트를 작성해야 하는지 알 수 있게 된다.


예를 들어 test.c 를 가지고 test 라는 프로그램을 만드는 스크립트는 다음과 같다.


all: test 
test: test.o


어떤가 정말 간단하지 않은가?


이제 make 명령만 치면 test 라는 프로그램이 생성될 것이다.


만약 test 라는 프로그램이 test.c 와 그 외 여러개의 소스파일 a.c, b.c, c.c ... 들로 이루어져 있다면 다음과 같이 작성하면 된다.


all: test
test: test.o a.o b.o c.o


마찬가지로 make 명령만 치면 test 라는 프로그램이 생성될 것이다.


추가로 test2.c 로 test2 를 만들어야 한다면 다음과 같이 한다.


all: test test2
test: test.o a.o b.o c.o
test2: test2.o


이번에 좀 다른 놈을 컴파일하려한다.  예를 들어 C 가 아니라 C++ 소스이다. 그럼 어떻게 해야하나? 지금까지 해온대로 해볼까?

test3.cpp 로 test3 를 만들어보려 다음과 같이 작성해보자.


all: test test2 test3
test: test.o a.o b.o c.o
test2: test2.o
test3: test3.o


아마 컴파일은 제대로 하는데 링크과정에서 에러가 발생할 것이다. 이유는 링커가 C++ 로 작성한 프로그램에 필요한 라이브러리를 제대로 찾지 못하기 때문이다.  이때문에 다음과 같이 작성하는 개발자들이 많다.


all: test test2 test3
test: test.o a.o b.o c.o
test2: test2.o
test3: test3.o
        g++ $<   -o $@

즉, 직접 링크하는 명령을 쓰는 것이다... -_-

그러나 이런식이면 프로그램작성하는 것보다 make 스크립트 작성하는것이 더 어렵게 된다. -_-


그럼 넌 뭐가 잘났냐?  뭐 해결방법이라도 있다는 거냐?

물론있다.  다음과 같이 작성해 보라.


all: test test2 test3
test: test.o a.o b.o c.o
test2: test2.o
test3: CC = g++
test3: test3.o


즉, test3 의 링커만 다른 프로그램과 다르게 지정하는 것이다.

음.... 그렇군...


여기까지가 기본이다.


도입부에 make -p -f /dev/null 을 기억하는가?  이것의 결과를 파일로 남겨서 보자.


파일에서 COMPILE.c, LINK.c 라는 부분을 찾아보면 다음과 같다.


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

COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)


복잡해보이는가?


이들은 내부규칙에서 컴파일과 링크과정에서 사용되는 명령들이다.


아까 링커를 CC 라는 변수지정규칙을 추가하여 프로그램별로 지정할 수 있다는 것을 알았다.

마찬가지로 CFLAGS 변수로 컴파일 옵션을 프로그램별로 지정할 수 있고

또한 LDFLAGS 변수로 링커 옵션을 프로그램별로 지정할 수 있다.

어떤것은 로컬 플랫폼으로 컴파일하고 어떤것은 다른 플랫폼으로 컴파일하려면 TARGET_ARCH 변수를 이용하면 된다.


test1.c, test1_select.c, test1_update_or_delete.c 소스파일들은 오러클의 OCI API 를 사용하는 프로그램 test1 을 생성하고

test2.cpp 는 C++ 프로그램으로 멀티쓰레드를 테스트하는 프로그램 test2 를 생성한다고 하고

해당 make 스크립트를 작성해보자.


all: test1 test2

test1: CFLAGS += -I ${ORACLE_HOME}/rdbms/public
test1: LDFLAGS += -L ${ORACLE_HOME}/lib
test1: LDLIBS += -l clntsh
test1: test1.o test1_select.o test1_update_or_delete.o

test2: CC = g++
test2: LDLIBS += -l pthread
test2: test2.o


어떤가? 정말 세련되어 보이지 않는가?

오늘은 여기까지...