목숨걸고 시작하는 리눅스 시스템 프로그래밍 – 3

 

————————————————————–

안녕하세요, 저는 길당 홍길한이라 합니다.
리눅스 c 프로그래밍에 있어서 c 를 하려면
중요한 것이 바로 디버깅과 컴파일 과정입니다.

코드를 작성하는 것도 중요하지만,
에러가 났을 때 해결하는 능력과
코드의 분량이 많아졌을 때,

이를 관리하는 능력도 프로그래머로서 매우
중요한 일이기도 합니다.

보통 연습용으로 디버깅은
gdb를 많이 사용하는데,

저는 오늘은 컴파일에 대한 이야기 시작하려
합니다.

오늘은 바로 Make입니다.

간단한 하나의 c 소스 파일 같은 경우는 보통
gcc -o filename filename.c
로 해결할 수 있습니다.

그런데 만약 다음과 같다면 문제가
틀려집니다.

gildang@gildang-P5Q ~/alsp_3rd/src $ cd 4.memory/
gildang@gildang-P5Q ~/alsp_3rd/src/4.memory $ ls
Makefile b_search b_search.c h_search h_search.c hello hello.c l_search l_search.c meet meet.c memory_lock memory_lock.c sect_memory.c stack_heap stack_heap.c t_search t_search.c
gildang@gildang-P5Q ~/alsp_3rd/src/4.memory $

이 많은 소스 코드를 같이 묶어서 컴파일하기란 쉽지 않을 것입니다.

이럴 때 필요한 것이 바로 Makefile 입니다.

그런 위 예제의 경우에 있는 Makefile 의 내부를 들여다 보기로 합니다.

gildang@gildang-P5Q ~/alsp_3rd/src/4.memory $ cat Makefile
#—————————————————————————-
# Prefix and Output target
#—————————————————————————-
PREFIX_DIR = ../..
OUT = stack_heap \
b_search \
l_search \
h_search \
t_search \
memory_lock

include $(PREFIX_DIR)/common_opt.mk

#—————————————————————————-
# Default target.
#—————————————————————————-
.PHONY: all clean dep

all: $(OUT)

#—————————————————————————-
# Suffix rules
#—————————————————————————-
include $(PREFIX_DIR)/common_rule.mk

#—————————————————————————-
# Build binaries
#—————————————————————————-

#—————————————————————————-
# others
#—————————————————————————-
include $(PREFIX_DIR)/common_target.mk
gildang@gildang-P5Q ~/alsp_3rd/src/4.memory $

위의 예제를 보면,
이것이 무슨 코드인지 당황스러울 수도 외계어 처럼 보일 수도 있습니다.

그럼 이제

알기 쉽도록
직접 알아가 보기로 합니다.

먼저 다음과 같이 하여 빈 파일들을 만들어 보기로 합니다.

gildang@gildang-P5Q ~/alsp_3rd/dd $ touch module.h module.c main.c
gildang@gildang-P5Q ~/alsp_3rd/dd $ ls
main.c module.c module.h
gildang@gildang-P5Q ~/alsp_3rd/dd $

그러면 이제 이 빈 파일 내에 다음과 같이 차례로
소스 코드를 작성하기로 합니다.

module.h:

#include <stdio.h>
void sample_func();
module.c:

#include “module.h”
void sample_func()
{
printf(“Hello world!”);
}
main.c:

#include “module.h”
void sample_func();
int main()
{
sample_func();
return 0;
}

이것을 터미널에서 정리하면
다음과 같습니다.

gildang@gildang-P5Q ~/alsp_3rd/dd $
gildang@gildang-P5Q ~/alsp_3rd/dd $ vim module.h
gildang@gildang-P5Q ~/alsp_3rd/dd $ vim module.c
gildang@gildang-P5Q ~/alsp_3rd/dd $ vim main.c
gildang@gildang-P5Q ~/alsp_3rd/dd $ cat module.h
#include <stdio.h>
void sample_func();
gildang@gildang-P5Q ~/alsp_3rd/dd $ cat module.c
#include “module.h”
void sample_func()
{
printf(“Hello world!”);
}

gildang@gildang-P5Q ~/alsp_3rd/dd $ cat main.c
#include “module.h”
void sample_func();
int main()
{
sample_func();
return 0;
}

gildang@gildang-P5Q ~/alsp_3rd/dd $

이 코드들을 gcc로 컴파일하려면 다음과 같이 하여야 합니다.

gcc -I . -c main.c # Obtain main.o
gcc -I . -c module.c # Obtain module.o
gcc main.o module.o -o target_bin #Obtain target binary

이것이 3개의 코드라서 그렇지 만일 20 개 이상이라면 이런 식으로는
관리가 어렵게 됩니다.

그래서 make 파일이 전통적으로 사용해 왔습니다.
유닉스와 리눅스에서는…

그럼 이제 Makefile을 만들어 보기로 합니다.

gildang@gildang-P5Q ~/alsp_3rd/dd $ vim Makefile
gildang@gildang-P5Q ~/alsp_3rd/dd $ cat Makefile
CC = gcc # Compiler to use
OPTIONS = -O2 -g -Wall # -g for debug, -O2 for optimise and -Wall additional messages
INCLUDES = -I . # Directory for header file
OBJS = main.o module.o # List of objects to be build
.PHONY: all clean # To declare all, clean are not files

all: ${OBJS}
@echo “Building..” # To print “Building..” message
${CC} ${OPTIONS} ${INCLUDES} ${OBJS} -o target_bin

%.o: %.c # % pattern wildcard matching
${CC} ${OPTIONS} -c $*.c ${INCLUDES}
list:
@echo $(shell ls) # To print output of command ‘ls’

clean:
@echo “Cleaning up..”
-rm -rf *.o # – prefix for ignoring errors and continue execution
-rm target_bin
gildang@gildang-P5Q ~/alsp_3rd/dd $

이것을 이제 사용해 보고 감을 잡아 보기로 합니다.

gildang@gildang-P5Q ~/alsp_3rd/dd $ make
gcc -O2 -g -Wall -c main.c -I .
gcc -O2 -g -Wall -c module.c -I .
Building..
gcc -O2 -g -Wall -I . main.o module.o -o target_bin

여기서 make 명령을 내리니
맨 마지막에 -o target_bin
이라는 메시지가 나왔습니다.

이것이 제대로 실행 되는지 보기로 하죠.. ^^

gildang@gildang-P5Q ~/alsp_3rd/dd $ ./target_bin
Hello world!gildang@gildang-P5Q ~/alsp_3rd/dd $

예.. 역시 전통적인

printf(“Hello world!”); 문에 있는 Hello world!라는
메시지가 터미널 상에 뿌려 졌습니다.

Makfile에 있는 문장을 간단히 설명하자면

CC = gcc 는 gcc를 CC로 정의하여 gcc를 컴파일러로 지정한 것이고,
OPTIONPS에서는 -O2 -g -Wall 로 -O2로 코드를 최적화하고 -g는 코드에 문제가
있을 gdb를 사용해겠다는 것이고,

-Wall은 헤더 파일에서 함수의 원형을 생략하더라도 컴파일/링크할 수 있다는
것을 말하는 것입니다.

일단, makefile은 많은 것을 가지고 있는 유용한 도구라서,
찬찬히 다루기로 하고,

오늘은 여기까지 하여 빌드에 성공하면 오늘 공부는 성공리에 마친 것이라고
생각하시면 됩니다.

^^

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다

This site uses Akismet to reduce spam. Learn how your comment data is processed.