본문 바로가기
C | C++

Makefile

by saniii 2022. 4. 12.

 

# make

: 소프트웨어 개발을 위해 유닉스 계열 운영체제에서 주로 사용되는 프로그램 빌드 도구

  • 여러 파일들끼리의 의존성과 각 파일에 필요한 명령을 정의하여 최종 프로그램까지 컴파일 할 수 있다. 
  • 반복적으로 발생하는 컴파일을 자동화한다. 
  • 프로그램의 종속 관계를 빠르게 파악할 수 있다. 
  • 유틸리티 이름 -> make 유틸리티는 Makefile 파일의 내용을 읽어 목표 파일을 생성한다. 
+ make의 man 페이지 description
The purpose of the make utility is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them.
You can use make with any programming language whose compiler can be run with a shell command. In fact, make is not limited to programs. You can use it to describe any task where some files must be updated automatically from others whenever the others change.
  • 프로그램이 여러 개의 모듈로 나누어질 때 서로 관계가 만들어지는데 이때 필요에 의해 하나를 바꿨을 때 연관된 모든 파일이 새롭게 재컴파일되어야한다. 이때 재컴파일할 필요가 있는 파일들만 자동으로 골라서 컴파일 해주는 것이 make가 하는 일이다. (효율적)

 

 

# Makefile 

: 결과 파일을 생성시키기 위한 파일들 간의 관계, 명령어 등을 기술한다. 

  • Makefile을 통하여 library 및 컴파일 환경을 관리 할 수 있다.
  • 프로그램의 일부만 수정하고자 할 때, 작성한 룰에 따라 수정한 부분만 컴파일하여 효율적이다. 

 

 

# Makefile의 기본 구조

  • 목적파일 target : 명령어가 수행되어 나온 결과를 저장할 파일
  • 의존성 dependency, prerequisites : 목적 파일을 만들기 위해 필요한 재료
    • 의존관계에 있는 파일들의 수정 시간이 타겟의 수정 시간보다 이후라면 타겟은 재컴파일해야한다. 
    • 그렇지 않으면 타겟을 재컴파일할 필요가 없다. 
  • 명령어 command, recipes : 실행되어야 하는 명령어
  • 매크로 macro : 중복되는 이름을 특정 단어로 치환 -> 코드를 단순화할 수 있다. 
target  ... :  dependency ...               |
       command ...                                |     의 형식을 따른다. 

※ 명령어는 앞에 꼭 TAB으로 시작해야한다.  (그냥 스페이스하면 에러남)

 

 

# 프로그램이 컴파일 되는 과정 

2020.10.15 - [about COMPUTER/컴퓨터 시스템] - # 04. 프로그램 컴파일 과정 (고급언어가 실행되기 까지)   

실제 프로그램을 제작하기 위해서는 링킹 과정이 필요하다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

# 간단한 Makefile을 통해서 컴파일 되는 과정 살펴보기 

 

1. make main을 실행하면 Makefile에서 타겟이 main인 부분을 찾는다.

2. 타겟이 main일 때 의존관계가 foo.o bar.o main.o에 있음을 알 수 있다. 

3. foo.o bar.o main.o를 타겟으로 하는 부분을 찾는다. 

4. foo.o가 아직 존재하지 않으며 foo.o가 타겟일 때 foo.cc가 필요하다.

5. foo.o에 대하여 command를 따라 컴파일 실행. 

6. bar.o main.o 도 4,5의 과정을 반복

7. main에 대한 command를 따라 컴파일 진행

8. main 생성 

** https://modoocode.com/311 여기 완전 참고 했뜹니다..엉엉....ㅠㅜㅠ

해당 예제는 c++을 기준으로 작성한 것이지만 C로 하면 파일 확장자가 .c, 컴파일러가 gcc로 바뀔뿐 양식은 같뜹니다... 

 

 

여기서 foo.cc만 수정한 후 다시 make main을 실행하면? 

>>

1. make main을 실행하면 Makefile에서 타겟이 main인 부분을 찾는다.

2. 타겟이 main일 때 의존관계가 foo.o bar.o main.o에 있음을 알 수 있다. 

3. foo.o bar.o main.o를 타겟으로 하는 부분을 찾는다. 

4. foo.o가 타겟일 때 foo.cc가 필요하다. 이때 foo.o는 이미 존재하지만 foo.cc의 수정시간이 foo.o보다 나중이다...!!

5. foo.o에 대하여 command를 따라 재컴파일 실행. 

6. bar.o main.o가 타겟일 때 각각 bar.cc와 main.cc가 필요하다. 이때 .cc들의 수정시간이 .o보다 이전이다. 

7. bar.o와 main.o는 재컴파일할 필요없음 -> 넘어감 

8. main와 의존관계가 있는 파일 중 foo.o가 재컴파일 되었으므로 command를 따라 재컴파일 진행

9. main 재생성 

** 이것두... https://modoocode.com/311 여기 완전 참고 했뜹니다..엉엉....ㅠㅜㅠ

 

 

# Makefile 똑똑하게 작성하기 

Makfile 파일을 작성해놓고, 그냥 make 만 치시면 make 는 Makefile 의 내용 을 살펴보다가 첫번째 목표파일에 해당되는 것을 실행시키게 됩니다. 따라서 위의 예제에서는 make test라고 해도 같은 결과를 내게 됩니다. 반면 clean에 해당하는 부분을 윗부분에 위치시키면 make는 항상 make clean을 수행하게 됩니다.

 

 

1. 매크로 사용하기 

매크로는 반복되는 단어를 특정 단어로 치환해주는 기능으로 그냥 변수를 지정하고 값을 대입하면 된다.

사용할 때는 $(변수)의 형태로 사용해야한다.  // ${..}, $.. 도 가능하다고는 하는군여 

NAME	= push_swap	

all: $(NAME)
  • 미리 정의되어 있는 매크로들이 있다. make -p 를 실행하여 확인할 수 있다. 
    • 하지만 사용자에 의해 재정의할 수 있다.
  • 내부 매크로
    • 사용자가 재정의 할 수 없으며 매크로를 연산, 처리하는데 사용한다. 
   
   
   
   
   

 

2. 확장자 규칙 (Suffix rule)

 

 

3. label 사용하기

label : make 명령어 뒤에 실행할 명령어를 정의한다. 

clean :
	rm -f $(OBJ)

이렇게 정의하면 make clean 이라고 실행했을때 rm -f $(OBJ)를 실행한다. 

 

4.  .PHONY

** phony : 가짜의, 허위의 

내가 정의한 명령어(label)과 같은 이름의 파일이 (컴파일할 디렉터리 안에) 생성되면 내가 원하는, 정의한대로의 label을 실행하지 않는다. 따라서 이런 상황을 방지하기 위해 PHONY를 등록하여 정의한 명령어와 같은 이름의 파일이 있어도 명령어가 정상 작동하도록 한다. 

all: $(NAME) 

clean :
	rm -f $(OBJ)

fclean : clean
	rm -f $(NAME)

re : fclean all

.PHONY: all clean fclean re

+ PHONY 등록없이 명령어와 같은 이름의 파일이 있을 때 make의 작동 과정

  - 'make (명령어)'를 실행하면 (명령어) 파일은 의존파일이 없고, 존재하는 (명령어) 파일은 수정시간이 최신이니 'make (명령어)' 는 무시하면 되겠군...! 하고 작동을 하지 않는다. 

 

5. 긴 명령어 나눠쓰기

Makefile을 작성 중 긴 라인을 적게 되면 가독성이 떨어진다. 

SRC	= main.c d_list.c d_list2.c libft0.c validation.c operation_p.c operation_s.c operation_r.c operation_rr.c

이럴 때는 \ 를 사용하여 여러 라인으로 나눌 수 있다. 

SRC	= \
	main.c \
        d_list.c \
        d_list2.c \
        libft0.c \
        validation.c \
        operation_p.c \
        operation_s.c \
        operation_r.c \
        operation_rr.c \

 

6. 다중 타겟

Makefile을 통해 여러 개의 결과 프로그램을 만들어야 할 수도 있다.

CLIENT = client
SERVER = server

SRC_CLIENT = client.c ft_atoi.c ft_putnbr.c ft_bzero.c
SRC_SERVER = server.c ft_atoi.c ft_putnbr.c ft_bzero.c

OBG_CLIENT = $(SRC_CLIENT:.c=.o)
OBG_SERVER = $(SRC_SERVER:.c=.o)

GCC_FLAGS = -Wall -Wextra -Werror
CC = gcc

all : $(SERVER) $(CLIENT)

clean :
	rm -f $(OBG_SERVER) $(OBG_CLIENT) 

fclean : clean
	rm -f $(CLIENT) $(SERVER)

re : fclean all

$(CLIENT) : $(OBG_CLIENT) 
	$(CC) $(GCC_FLAGS) -o $(CLIENT) $(OBG_CLIENT) 

$(SERVER) : $(OBG_SERVER)
	$(CC) $(GCC_FLAGS) -o $(SERVER) $(OBG_SERVER)

%.o : %.c
	$(CC) $(GCC_FLAGS) -c $<

.PHONY : clean fclean all re

 

7. 리링크 

수정되지 않았음에도 재컴파일되면 비효율적이다. 

수정되지 않았음에도 재컴파일되는 것을 relink(리링크)라고 하며  ******

make -t,  touch 유틸리티

 

 

8. 헤더파일 다른 폴더에 따로 쓰기

 

 

9. 다른 라이브러리 파일 같이 컴파일하기 

 

 

 

 

 

 

# 부가적인 폴더 없이 파일로만 이루어져 있을 때 내 Makefile  (만들기 제일 쉬운 거)

NAME	= push_swap	

CC	= gcc
FLG	= -Wall -Wextra -Werror

SRC	= main.c d_list.c d_list2.c libft0.c validation.c operation_p.c operation_s.c operation_r.c operation_rr.c
OBJ	= $(SRC:.c=.o)


all: $(NAME)  #make만 했을 때 수행하는 거

clean :
	rm -f $(OBJ)

fclean : clean
	rm -f $(NAME)

re : fclean all

$(NAME): $(OBJ)
	$(CC) $(FLG) -o $(NAME) $(OBJ)

%.o : %.c
	$(CC) $(FLG) -c $<

.PHONY: all clean fclean re

 

 

 

 

 

 

 

NAME = 
CLIENT = client
SERVER = server

SRC_CLIENT = client.c ft_atoi.c ft_putnbr.c ft_bzero.c
SRC_SERVER = server.c ft_atoi.c ft_putnbr.c ft_bzero.c

OBG_CLIENT = $(SRC_CLIENT:.c=.o)
OBG_SERVER = $(SRC_SERVER:.c=.o)

GCC_FLAGS = -Wall -Wextra -Werror
CC = gcc

all : $(SERVER) $(CLIENT)

clean :
	rm -f $(OBG_SERVER) $(OBG_CLIENT) 

fclean : clean
	rm -f $(CLIENT) $(SERVER)

re : fclean all

$(CLIENT) : $(OBG_CLIENT) 
	$(CC) $(GCC_FLAGS) -o $(CLIENT) $(OBG_CLIENT) 

$(SERVER) : $(OBG_SERVER)
	$(CC) $(GCC_FLAGS) -o $(SERVER) $(OBG_SERVER)

%.o : %.c
	$(CC) $(GCC_FLAGS) -c $<

.PHONY : clean fclean all re

 

 

 

 

 

 

# 컴파일할 파일들을 폴더별로 가지고 있을 때 (다수의 경우)

 

 

 

 

 

 

 

 

 

 

 

 

 

[참고]

https://ko.wikipedia.org/wiki/Make_(%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4) 

https://infinitt.tistory.com/315 

https://xzio.tistory.com/981 

http://ebook.pldworld.com/_eBook/make/make_utility_lecture_(cWyANG).pdf 

https://modoocode.com/311#page-heading-10 

'C | C++' 카테고리의 다른 글

<pthread.h>  (0) 2022.04.30
open | close | read | write (저수준 파일입출력 함수)  (0) 2022.04.24
[0423] C 에러 해결  (0) 2022.04.23
[ C ] 동적할당 malloc(), free()  (0) 2021.12.29
[C++] c++ 이 뭔가요?  (0) 2021.10.20

댓글