윈도우에서는 비쥬얼 스튜디오등을 사용하여 버튼을 눌러 컴파일하지만,
리눅스 환경에서 프로그램을 컴파일 할 때 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로 대체하라는 의미가 된다.
참조
'OS > Linux' 카테고리의 다른 글
Linux - tee 명령어로 터미널 출력 내용을 파일로 저장하기 (0) | 2021.10.17 |
---|---|
gcc 이란? (0) | 2021.08.25 |
[Linux] 스크립트 파일 systemd service 등록하기 (0) | 2021.06.30 |
'ps aux'와 'ps -ef'의 차이 (0) | 2021.06.11 |
[학습] NFS와 CIFS 설명 (0) | 2021.05.23 |
댓글