강좌
클라우드/리눅스에 관한 강좌입니다.
자격증 분류

리눅스마스터1급: 리눅스 컴파일러 make 사용법

작성자 정보

  • 관리자 작성
  • 작성일

컨텐츠 정보

본문

리눅스마스터1: 리눅스 컴파일러 make 사용법

 

make

 

 

소스코드 한두 개로 이루어진 C/C++ 언어 교양 과목 과제물을 제출하는 것이 아니라면 약간만 프로젝트가 커져도 소스코드는 감당할 수 없을 정도로 불어나게 되고 그것을 일일이 gcc 명령 행 방식으로 처리한다는 것은 상당히 곤욕스러운 일이다.

 

 

 

그래서 하나의 프로젝트를 효율적으로 관리하고 일관성 있게 관리하기 위하여 Makefile이라는 형식을 사용하고 make라는 유틸리티를 사용한다.

 

 

 

리눅스에서 소스코드 형태로 되어있는 것을 가져와서 컴파일하게 되면 보통 마지막에는 make라는 명령 또는 make <....> 이런 식으로 실행하게 된다.

 

 

 

 

 

make라는 유틸리티는 보통 현재 디렉토리에 Makefile 또는 makefile이라는 일정한 규칙을 준수하여 만든 파일의 내용을 읽어서 목표(target)를 달성한다.

 

 

 

Makefile의 이름을 다르게 명시하고 싶을 때는 다음과 같이 한다.

 

 

 

 

 

 

 

 

 

# make f Makefile.linux

 

 

 

 

 

 

 

보통 멀티플랫폼용 소스코드들은 Makefile.solaris, Makefile.freebsd, Makefile.hp 이런 식으로 Makefile을 여러 개 만들어두는 경향이 있다.

 

 

 

또는 적절하게 만들어두어 다음과 같이 make <플랫폼>이라는 식으로 하면 컴파일하도록 하기도 한다.

 

 

 

 

 

 

 

 

 

 

# make linux

 

 

 

 

 

 

 

이런 일은 보통의 관례일 뿐이다.

 

 

 

예를 들어 다음은 커널 컴파일 작업할 때의 경우이다.

 

 

 

 

 

 

 

 

 

# make config /* 설정 작업을 한다.

 

*/

# make menuconfig /* 메뉴 방식의 설정 작업을 한다.

 

*/

# make clean /* 만든 파일을 지우고 깨끗한 상태로 만든다.

 

*/

# make bzImage /* bzImage를 만든다.

 

*/

# make modules /* 커널 모듈을 만든다.

 

*/

# make modules_install /* 커널 모듈을 인스톨한다.

 

*/

 

 

 

 

 

 

 

 

복잡한 것 같아도 항상 일관성 있게 make라고 입력하면 된다.

 

 

 

분량이 작은 소스코드들의 경우에는 일반적으로 다음만 해도 되는 경우가 많다.

 

 

 

 

 

 

 

 

 

# make 또는 make all

# make install

 

 

 

 

 

 

 

다른 사람에게 공개하는 소스라면 더욱 make를 사용해야 한다.

 

 

 

공개하는 자신도 make 라고 입력하면 원하는 결과가 나올 수 있도록 하는 것이 좋다.

 

 

 

일단 make를 사용하는 일반적인 관례를 익히는 것이 중요하다.

 

 

 

리눅스 배포판 패키지만 설치하지 말고 적극적으로 소스코드를 가져다 컴파일해 보자.

 

 

 

1) make 사용하기

 

 

Makefile을 어떻게 만드는지 살펴보자.

 

 

 

 

 

 

 

# gcc o foo foo.c bar.c

 

 

 

 

 

 

 

여기서 foo라는 실행 파일은 foo.c, bar.c라는 2개의 소스로부터 만들어지고 있다.

 

 

 

지금 계속 코딩을 하고 있는 중이라면 이 정도쯤이야 가상 콘솔 또는 X 터미널을 여러 개 열어두고 편집하면서 쉘의 히스토리 기능을 사용하면 그만이지만 하루 이틀 계속 한다고 하면 곤욕스러운 일이 아닐 수 없다.

 

 

 

이에 대해 다음과 같이 vi 편집기를 이용하여 Makefile을 작성해 보자.

 

 

 

 

 

 

 

 

foo: foo.o bar.o

gcc o foo foo.o bar.o

foo.o: foo.c

gcc c foo.c

bar.o: bar.c

gcc c bar.c

 

 

 

 

 

 

 

입력할 때 주의할 것이 있다.

 

 

 

형식은 다음과 같다.

 

 

 

 

 

 

 

 

 

목표: 목표를 달성하는데 필요한 구성 요소들...

목표를 달성하기 위한 명령 1

목표를 달성하기 위한 명령 2

...

 

 

 

 

 

 

 

맨 첫 번째 목표인 foo를 살펴보자.

 

 

맨 첫 칸에 foo:라고 입력하고 나서 foo가 만들어지기 위해서 필요한 구성 요소를 적어준다.

 

 

 

foo가 만들어지기 위해서는 컴파일된 foo.o, bar.o가 필요하다.

 

 

 

각 요소를 구분하는 데 있어 콤마(,)같은 건 사용하지 않고 공백으로 한다.

 

 

 

그 다음 줄로 넘어가서는 <>을 누른다.

 

 

 

꼭 한 번 이상은 눌러야 한다.

 

 

 

절대 스페이스나 다른 키는 사용해선 안 된다.

 

 

 

목표 파일을 만들어 내기 위한 명령에 해당하는 줄들은 모두 <>으로 시작해야 한다.

 

 

 

Makefile 만들기에서 제일 중요한 내용이다.

 

 

 

그리고 foo를 만들기 위한 명령은 바로 gcc -o foo foo.o bar.o이다.

 

 

 

 

 

다시 한 번 해석하면 이렇다.

 

 

 

foo를 만들기 위해서는 foo.obar.o가 우선 필요하다(foo: foo.o bar.o). 일단 foo.o, bar.o가 만들어져 있다면 우리는 gcc -o foo foo.o bar.o를 실행하여 foo를 만든다.

 

 

 

foo를 만들려고 하니 foo.obar.o가 필요하다.

 

 

 

그렇다면 foo.o는 어떻게 만들까?

 

 

 

 

 

 

 

foo.o: foo.c

gcc c foo.c

 

 

 

 

 

 

 

바로 이 부분이다.

 

 

 

foo.ofoo.c를 필요로 하며 만드는 방법은 gcc -c foo.c이다.

 

 

 

 

 

그 다음 bar.o는 어떻게 만들까?

 

 

 

 

 

 

 

bar.o: bar.c

gcc c bar.c

 

 

 

 

 

 

 

소스를 만들어서 실행 해 보자.

 

 

 

 

foo.c의 내용

 

 

 

 

extern void bar ( void ) ;

int

main ( void )

{

bar ();

return ();

}

 

 

 

 

 

 

 

 

bar.c의 내용

 

 

 

 

#include <stdio.h>

void

bar ( void )

{

printf ( “Good by, my love. \n” );

}

 

 

 

 

 

 

 

 

Makefile을 위처럼 만들고 실행한다.

 

 

 

명령이 실행되는 순서를 잘 보라.

 

 

 

 

 

 

 

# make 또는 make foo

gcc c foo.c

gcc c bar.c

gcc o foo foo.o bar.o

 

 

 

 

 

 

 

만들어진 foo를 실행해 보자.

 

 

 

 

 

 

 

# ./foo

Good bye, my love.

 

 

 

 

 

 

다시 한 번 make를 실행해 보자.

 

 

 

 

 

 

 

# make

make: ‘foo’ is up tp date.

 

 

 

 

 

 

 

makefoo를 다시 만들 필요가 없다고 생각하고 더 이상 처리하지 않는다.

 

 

 

이번에는 foo.c를 약간만 고쳐보자. return 0; 라는 문장을 exit (0); 라는 문장으로 바꾸어보고 다시 한 번 다음과 같이 한다.

 

 

 

 

 

 

 

 

 

# make

gcc c foo.c

gcc o foo foo.o bar.o

 

 

 

 

 

 

 

원하던 결과가 나타났다.

 

 

 

당연히 foo.c만 변화되었으므로 foo.o를 만들고 foo.o가 갱신되었으므로 foo도 다시 만든다.

 

 

 

하지만 bar.c는 아무 변화를 겪지 않았으므로 이미 만들어둔 bar.o는 그대로 둔다.

 

 

 

 

 

 

 

 

# rm f foo

# make

gcc o foo foo.o bar.o

 

 

 

 

 

 

 

이것도 원하던 결과이다.

 

 

 

기존에 foo.obar.o가 생성되어 있기 때문에 foo를 생성하기 위한 명령만 수행되었다.

 

 

 

 

2) Makefile의 내부 구조

 

 

Makefile은 기본적으로 아래와 같이 목표(target), 의존 관계(dependency), 명령(command)의 세 개로 이루어진 기본적인 규칙(rule)들이 계속적으로 나열되어 있다고 봐도 무방하다.

 

 

 

make가 지능적으로 파일을 갱신하는 것도 모두 이 간단한 규칙에 의하기 때문이다.

 

 

 

 

 

 

 

 

 

목표(target): 의존 관계(dependency, 목표를 달성하는데 필요한 구성 요소들) ...

명령(command, 목표를 달성하기 위한 명령)

...

...

 

 

 

 

 

 

 

 

여기서 목표(target) 부분은 명령(command)이 수행되어 나온 결과 파일을 지정한다.

 

 

 

당연히 목적 파일(object file)이나 실행 파일이 될 것이다.

 

 

 

그리고 명령(command) 부분에 정의된 명령들은 의존 관계(depenency) 부분에 정의된 파일의 내용이 바뀌었거나, 목표 부분에 해당하는 파일이 없을 때 이곳에 정의된 것들이 차례대로 실행이 된다.

 

 

 

일반적으로 쉘에서 쓸 수 있는 모든 명령어들을 사용할 수가 있으며 bash에 기반한 쉘 스크립트도 지원한다.

 

 

 

 

 

 

 

 

 

 

 

 

목표 부분에는 결과 파일만 올 수 있는 것이 아니고, 보통 make clean에서와 같이 간단한 레이블(label) 기능을 제공하기도 한다.

 

 

 

 

명령 부분은 꼭 TAB 글자로 시작해야 한다.

 

 

 

그냥 빈 칸 등을 사용하면 make 실행 중에 에러가 난다.

 

 

 

make가 명령어인지 아닌지를 TAB을 가지고 구별하기 때문이다.

 

 

 

 

 

 

 

 

 

 

3) Makefile 예제

 

 

간단한 Makefile을 만들어 본다.

 

 

 

우리가 만들려고 하는 프로그램은 main.c read.c write.c로 구성되어 있고, 모두 io.h라는 헤더 파일을 사용한다고 가정한다.

 

 

 

이들을 각각 컴파일해서 test라는 실행 파일을 생성시킨다.

 

 

 

 

 

 

 

 

 

# gcc c main.c

# gcc c read.c

# gcc c write.c

# gcc o test main.o read.o write.o

 

 

 

 

 

 

 

위의 방식은 make를 쓰지 않고 그냥 명령어를 주는 방식이다.

 

 

 

파일의 수가 작아서 오히려 더 간단하게 보일 수 있으나, 파일이 100개 정도 된다고 가정하면 make의 유용함을 알 수 있을 것이다.

 

 

 

 

그리고 아래는 위와 똑같은 일을 수행하는 Makefile의 내용이다.

 

 

 

 

 

 

 

 

 

test: main.o read.o write.o

gcc o test main.o read.o write.o

main.o: io.h main.c

gcc c main.c

read.o: io.h read.c

gcc c read.c

write.o: io.h write.c

gcc c write.c

 

 

 

 

 

 

 

가령 main.c를 고쳤다고 생각한다면 main.o가 컴파일 되어 다시 생기고, test도 다시 링크되어 갱신된다.

 

 

 

만약 io.h가 바뀌었다고 가정하면 모든 파일들이 컴파일 되어서 목적 파일이 생기고, 그것들이 링크가 되어 test가 생긴다.

 

 

 

 

 

위와 같이 파일들을 구성한 다음 Makefile을 실행시켜 보자. Makefile의 실행은 그냥 make라고 실행하면 된다.

 

 

 

 

 

 

 

 

 

# make

gcc c main.c

gcc c read.c

gcc c write.c

gcc o test main.o read.o write.o <- OK

 

 

 

 

 

 

 

 

 

 

4) 매크로의 사용

 

 

간단한 매크로 기능을 사용해 보자. main.o read.o write.o라는 것을 OBJECTS 라는 매크로로 바꾸는 것이 아래의 예제에 나와 있다.

 

 

 

 

 

 

 

 

 

OBJECTS = main.o read.o write.o

test: $(OBJECTS)

gcc o test $(OBJECTS)

main.o: io.h main.c

gcc c main.c

read.o: io.h read.c

gcc c read.c

write.o: io.h write.c

gcc c write.c

 

 

 

 

 

 

 

 

위에서 보다시피 매크로는 그냥 프로그램 짤 때와 같이 사용해서 값을 대입한다.

 

 

 

대신 사용할 때는 반드시 $(..) 안에 넣어서 사용한다.

 

 

 

매크로의 사용법은 위와 같이 간단하므로 다양하게 정의해서 사용할 수 있다.

 

 

 

5) 레이블의 사용

 

 

목표 부분에 해당하는 부분이 그냥 레이블과 같이 사용될 수도 있다고 이미 설명하였다.

 

 

 

위의 예제에 목적 파일들을 모두 삭제하는 명령어를 추가하기로 한다.

 

 

 

 

 

 

 

 

 

OBJECTS = main.o read.o write.o

test: $(OBJECTS)

gcc o test $(OBJECTS)

main.o: io.h main.c

gcc c main.c

read.o: io.h read.c

gcc c read.c

write.o: io.h write.c

gcc c write.c

clean:

rm $(OBJECTS)

 

 

 

 

 

 

 

 

레이블로 사용될 때는 당연히 의존 관계 부분은 없어도 된다.

 

 

 

그리고 clean을 실행시키려면 아래와 같이 한다.

 

 

 

 

 

 

 

 

 

# make clean

rm main.o read.o write.o <- OK

 

 

 

 

 

 

관련자료

댓글 0
등록된 댓글이 없습니다.

공지사항


뉴스광장


  • 현재 회원수 :  60,043 명
  • 현재 강좌수 :  35,855 개
  • 현재 접속자 :  106 명