본문 바로가기
OS/Linux

Makefile 이란 ? gcc, g++과 makefile의 차이?

by SAMSUNG CLOUD-OKY 2021. 8. 25.
반응형

 

윈도우에서는 비쥬얼 스튜디오등을 사용하여 버튼을 눌러 컴파일하지만,
리눅스 환경에서 프로그램을 컴파일 할 때 make라는 프로그램을 사용한다. 

 

 

1. makefile 이란?

Shell에서 컴파일하는 방법중 하나이다.

makefile이라는 파일에, 어떤 파일을 컴파일 하는지, 어떠한 방식으로 컴파일 할지 작성해놓는다.
그리고 make라는 명령어를 입력하면 makefile 이 들어있는 디렉토리에서 파일들의 종속관계를 파악하여 자동적으로 컴파일하게 된다.

즉, makefile를 사용하여 컴파일 하면 여러개의 파일을 컴파일할때, 자동화로 인해 시간을 절약하고, 프로그램의 종속 구조를 쉽게 파악할 수 있다.
+ 만약 프로그램을 일부 수정한다면, 그 부분에 대해서만 컴파일 하도록 도와주기 때문에 훨씬 효율적이다.

 

2. gcc를 사용할때 컴파일의 순서 (make을 사용하지 않았을때 프로그램의 빌드 절차)

일반적으로 gcc , g++, clang 등을 사용하여 컴파일 할 때의 순서는 아래와 같다.

만약에 디렉토리 내에 아래와 같이 프로그램 파일들이 있다고 가정한다면,

  • main.c
#include "f1.h" // 현재경로에 있기 때문에 쌍따옴표 + 헤더파일이름 으로 include해주면 된다.
#include "f2.h"

int main(void)
{
	f1();
    f2();
}
  • f1.c
#include <stdio.h>
void f1()
{
	printf("this is f1");
}
  • f2.c
#include <stdio.h>
void f1()
{
	printf("this is f1");
}
  • f1.h
void f1(); //헤더파일에는 함수 형태 + 함수명을 적는다.
  • f2.h
void f2();

 

파일을 다 작성했으면, 컴파일 하여 오브젝트 파일을 생성해야 한다.
(헤더파일들은 main.c 파일에서 명시되어 있으므로, 따로 컴파일 할때 포함시키지 않아도 된다.)

$ gcc -c main.c f1.c f2.c

 

그럼 아래와 같은 오브젝트파일들이 생성된다.

  • main.o
  • f1.o
  • f2.o
$ gcc main.o f1.o f2.o

다음으로는 위 명령어를 통해서 오브젝트파일들을 하나로 연결시킨 실행파일을 생성한다.
이 과정은 "링킹"이라고 말한다.

 

따로 파일명을 지정하지 않으면 a.out이라는 파일로 생성된다.

$ ./a.out

이렇게 실행해주면 된다. 

 

 

3. Makefile 을 사용하여 빌드하기

makefile은 3가지 요소로 구성된다.

  • Target (타겟)
  • Prerequisites (필요한 조건들)  || 혹은 Dependency : Target이 의존하는 파일들
  • Recipes (실행할 명령어) || 혹은 Command (명령어)
target ... : prerequisites ...
	recipe
    	...
        ...
        ...
//recipe 앞은 반드시 tab키로 들여쓰기 해주어야한다.

 

그럼 목차 2번에서 했던 빌드과정으로 예를들어서 makefile을 만들어 보면

makefile

f1.o : f1.h f1.c
	gcc -c f1.c
    
f2.o : f2.h f2.c
	gcc -c f2.c
    
main.o : main.c f1.h f2.h
	gcc -c main.c

main : f1.o f2.o main.o
	gcc f1.o f2.o main.o

들여쓰기는 반드시 tab으로 해야한다. 

$ make main

make 명령을 통해 빌드하면 2번에서 했던 과정들(컴파일 + 링킹)이 그대로 빌드된다.

 

3 - 1  . makefile 내에서 변수 정의하기

g = gcc

f1.o : f1.h f1.o
	$(g) -c f1.c

변수는 위와 같이 설정한다. 사용할때는 $(변수명) 으로 사용하면 된다.

 

 

4 - 2 . PHONY

makefile 내에 흔하게 추가하는 기능으로 clean이 있다.
clean은 빌드와 관련된 파일들 (오브젝트 파일)을 모두 제거하는 명령을 넣는다.

clean:
	rm -rf $(OBJS) main

이 기능을 사용할때는 마찬가지로 make clean 이라고 입력하여 사용하게 되는데, clean이라는 파일이 디렉토리내에 존재할 경우에는 명령이 무시되어버린다. 이를 방지하기 위해서 PHONY에 등록한다.

.PHONY: clean
clean:
	rm -f $(OBJS) main

PHONY에 등록된 명령어는, 동일 이름의 파일 존재여부과 관계 없이 항상 타겟 명령을 실행하게 된다.

 

 

 

4 - 3. 패턴 사용

프로젝트가 방대해진다면, 자동화를 하기 위해 makefile내에 recipe을 작성할때도 많은 입력이 필요하다.
(파일명만 바꾸어 반복적이고 비효율적인 작업) 이를 편하게 하기 위해서 패턴을 사용하게 된다.

* 패턴은 target과 prerequisite 부분에만 사용 가능하다.
따라서 recipe에서는 "자동 변수"라는 미리 정해진 변수를 사용해야한다.

 

* 자동 변수

  • $@ : 타겟 이름에 대응된다. (ex : f1.o)
  • $< : 의존 파일 목록에 첫 번째 파일에 대응된다. (ex : f1.c)
  • $^ : 의존 파일 목록 전체에 대응된다. (ex : f1.c , f1.h)
패턴을 사용하기 전
f1.o : f1.c f1.h
	gcc -c f1.c
f2.o : f2.c f2.h
	gcc -c f2.c

 

패턴 사용 후
%.o: %.c %.h
	gcc -c $<

%.o 는 와일드카드에 대응시키면 *.o와 같다. 확장자가 o인 모든 파일을 타겟으로 삼겠다는 의미가 된다.

 

 

 

* 자동 prerequisite 생성

컴파일시 -MD옵션을 주면, d확장자를 가진 파일이 생성된다.
이 파일에는 자동적으로 목적파일과 컴파일한 소스파일을 타겟으로 삼는 의존파일들을 담은 목록을 담고있다.

$ gcc -c -MD main.c
>>>>> main.d 파일 생성됨

이러한 기능을 이용하여 makefile에서 자동적으로 prerequisite를 생성 가능하다.

OBJS = f1.o f2.o main.o
%.o : %.c
	gcc -c $<
    
main : $(OBJS)
	gcc -o main
    
.PHONY: clean
clean:
	rm -f $(OBJS) main
    
-include $(OBJS:.o=.d)

OBJS의 o확장자를 갖는 파일들을 d로 대체하라는 의미가 된다.

 

 

 

 

 

 

 

참조

https://infinitt.tistory.com/315

반응형

댓글