1. Makefile 구성요소
Target(만들려는 녀석), Dependency(만들기위한 재료), Command(명령어) ,Macro(작성의 편리성)
해당설명은 이 블로그를 참고하자!![1]
Makefile에서 반복되는 구조인 Rule block의 구조는 다음과 같습니다.[2]
<Target>: <Dependencies> <Recipe> |
위의 명칭은 GNU make 공식 매뉴얼에서 그대로 들고 온 것인데, 쉽게 설명해서 다음과 같은 의미입니다.
- Target: 빌드 대상 이름. 통상 이 Rule에서 최종적으로 생성해내는 파일명을 써 줍니다.
- Dependencies: 빌드 대상이 의존하는 Target이나 파일 목록. 여기에 나열된 대상들을 먼저 만들고 빌드 대상을 생성합니다.
- Recipe: 빌드 대상을 생성하는 명령. 여러 줄로 작성할 수 있으며, 각 줄 시작에 반드시 Tab문자로 된 Indent가 있어야 합니다.
2. makefile의 장점
- 컴파일할 때 사용하는 반복되는 명령을 매크로처럼 사용하기위해
- 목적파일(.o)을 보다 쉽게 관리하기 위함
- 종속성(Denpendency)를 이용한 incremental build
3. relink
"코드가 바뀌지 않은 상태에서 make 명령어를 다시 실행 했을 때 최신이라 libft.a를 다시 만들지 않으면 relink 되지 않은 거랍니다. rush평가 때 도비님이 알려주셨어요"[4]
(Google에서는 찾기가 힘들다..)
4. Incremental build
Incremental build란 반복적인 빌드 과정에서 변경된 소스코드에 의존성(Dependency)이 있는 대상들만 추려서 다시 빌드하는 기능
(SW 설계모델을 말하는 것 같다.)
"이 제품은 여러 구성 요소로 분해되며 각 구성 요소는 개별적으로 설계 및 제작됩니다 (빌드라고 함). 각 구성 요소는 완료되면 클라이언트에 전달됩니다. 이를 통해 제품을 부분적으로 활용할 수 있으며 개발 시간이 오래 걸리지 않습니다."[5]
5. .PHONY
CC = gcc
CFLAGS = -Wall -Wextra -Werror
NAME = testing
SRCS = main.c
OBJS = main.o
all : $(NAME)
$(OBJS) :
$(CC) $(CFLAGS) -c $(SRCS) -o $(OBJS)
$(NAME) : $(SRCS) $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(NAME)
# .PHONY: all clean fclean
clean :
rm -f main.o
fclean : clean
rm -f $(NAME)
.PHONY를 달아주지 않으면 실행명령어 조합이라도, clean과 fclean all을 Target file로 본다.
즉 다시말하면, Makefile와 같은 폴더에 clean이나 fclean의 이름을 가진 파일이 존재하면 명령어를 실행하지않는다.
(fclean의 경우 clean과 fclean의 이름을 가진 두 파일이 존재하더라도, 실행시키면, rm -f $(NAME)은 실행된다. Dependency에 clean이 있어서 그런 것으로 확인되었다. 해결방안은 찾지 못함.)
6. pattern rule
"%" is a macro to make a pattern that we want to watch in both the target and the dependency[2]
Here are some examples of pattern rules actually predefined in make. First, the rule that compiles ‘.c’ files into ‘.o’ files:
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
defines a rule that can make any file x.o from x.c. The recipe uses the automatic variables ‘$@’ and ‘$<’ to substitute the names of the target file and the source file in each case where the rule applies (see Automatic Variables(=내부매크로)).[3]
+위 명령어를 통해서는 어느 폴더에 있던간에 특정 목적파일이 dependency에 존재하게되면, .c -> .o로 컴파일해준다.
전역적으로 작용하는 명령어이다.(printf의 makefile 참고)
Makefile 문법
1. command에는 Dependency가 들어가면 안되고, Target과 같은 줄에 적어준다.
re : clean all (O)
re :
clean all (X)
2. rm -f
쓰기제한이 걸린 파일의 경우 rm을 하면, 삭제할것이냐고 되묻는다.
option "-f"를 걸어주면, 경고메세지 없이 삭제된다. (~= apt install -y)
3. 외워두면 좋은 makefile문법(about pattern rule "%")
3-1. OBJS_ALL = $(SRCS:%.c=%.o)
SRCS로 정의된 매크로에서 각각의 모든 %.c로 끝나는 파일이름을 %.o로 끝나게 만든다.
3-2.
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
모든 .c 파일을 .o 목적파일로 만든다. Dependency에 목적파일이 있다면, .c가 있는 위치폴더에서 .o로 컴파일함.(패시브 같은 느낌...)
(OBJS_ALL)%.o : (SRCS_SUB)%.c 앞에 붙이면, 해당 폴더에서만 목적파일로 컴파일
3-3. $addprefix()
addprefix()를 통해서 내가 원하는 문장을
https://github.com/nadarm/42Seoul-42cursus/blob/master/libft/Makefile
4. makefile 주석처리는 #을 맨 첫글자에 넣어놔야한다.
# %.o : %.c (o)
# xx$(CC) $(CFLAGS) -c $< -o $@ (x)
# %.o : %.c 와 같은 식 (o)
5. https://github.com/exgs/42cursus/tree/master/01.Libft/libft_42cursus
내가 만든 makefile에서 종속성이 존재함에도 문장의 위치가 바뀌면, 컴파일을 하는 것이 달라진다.
1. make all을 했을 때, 종속성에 따라 $(OBJS) : $(SRCS)를 동작한다.
all : $(NAME)
$(OBJS_ALL) : $(SRCS)
$(CC) $(CFLAGS) -c $(SRCS)
$(OBJS) : $(SRCS)
$(CC) $(CFLAGS) -c $(OBJS:%.o=%.c)
$(NAME) : $(OBJS)
ar rc $(NAME) $(OBJS)
ranlib $(NAME)
2. make all을 했을 때, 종속성을 따르지않고 $(OBJS_ALL) : $(SRCS)를 동작한다.
all : $(NAME)
$(OBJS) : $(SRCS)
$(CC) $(CFLAGS) -c $(OBJS:%.o=%.c)
$(OBJS_ALL) : $(SRCS)
$(CC) $(CFLAGS) -c $(SRCS)
$(NAME) : $(OBJS)
ar rc $(NAME) $(OBJS)
ranlib $(NAME)
Makefile:25: warning: overriding commands for target `ft_memset.o'
Makefile:22: warning: ignoring old commands for target `ft_memset.o'
게다가 이런 류의 warning이 뜨는 것으로보아 위치에 관계없이 두 코드가 같이 실행 되는 것 같다.
따라서, Denpendency에 맞게 알아서 목적파일을 생성해주는 아래와 같은 코드를 쓰는 것이 합당해 보인다.
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
정적라이브러리 vs 동적라이브러리[6]
정적링크라이브러리(Static Link Library) - .lib
컴파일 시에 함수가 실행파일에 연결된다. 실행 파일에 함수의 코드가 복사되기 때문에 실행파일의 크기가 커지는 단점이 있지만
실행 파일은 완전한 단독 실행 파일이 된다. 실행파일에 함수의 코드가 포함되어 있기 때문에 컴파일이 끝나면 lib 파일이 없어도
프로그램을 실행 할 수있다.
동적링크라이브러리(Dynamic Link Library) - .dll
정적라이브러리처럼 컴파일 시에 함수가 연결되는 방식이 아닌 런타임시에 함수가 실행파일에 연결된다. 실행파일에는 호출할
함수의 정보만 포함되고 실제 함수 코드는 복사되지 않으므로 실행 파일의 크기가 작아진다. 하지만 실행 파일은 함수에 대한 정보만
가지고 있을 뿐 실제 코드를 가지고 있지 않기 때문에 프로그램 실행시에는 dll 파일이 항상 존재해야 한다.
printf
1. Makefile -C option
다른 폴더에 있는 makefile의 명령어를 실행시킬 수 있는 옵션!
example1 : [link]
example2 : [line]
$(MAKE) (-C) (./dir) (command) : 다른 디렉토리에 있는 make를 이용하는 방법에 대한 글
make의 재귀적 사용(Recursive Use of make)
make의 재귀적 사용이란 make를 makefile에서 하나의 명령으로 사용한다는 것이다. 이 테크닉은 여러분이 더 큰 시스템을 만드는 여러 서브시스템들을 위한 여러 분리된 makefile들을 원할 때 유용하다. 예를 들어서 여러분이 자신의 makefile을 가지고 있는 `subdir' 서브디렉토리를 가지고 있고 상위 디렉토리의 makefile을 사용해서 그 서브 디렉토리에 대해서 make를 실행하고자 한다고 가정하자. 여러분은 다음과 같이 작성해서 이렇게 할 수 있다:
subsystem: cd subdir && $(MAKE)
또는 동일하게 다음과 같이 할 수도 있다 (see section 옵션들의 요약(Summary of Options)):
subsystem: $(MAKE) -C subdir
여러분은 이 예제를 그대로 복사해서 재귀적 make 명령을 작성할 수도 있지만 그들이 작동하는 방식과 이유, 그리고 서브-make가 톱-레벨 make에 어떻게 연결되어 있는가에 대해서 알아야 할 것들이 많이 있다.
여러분의 편의를 위해서 GNU make는 변수 CURDIR를 현재 작업 디렉토리의 경로명으로 설정한다. -C가 쓰였다면 오리지널 디렉토리가 아니라 새로운 디렉토리의 경로를 포함할 것이다. 그값은(CURDIR의 값 ?) 이것이 makefile안에서 설정되었다면 가질 우선순위와 동일하다(디폴트로 환경 변수 CURDIR는 이 값을 오버라이드하지 않을 것이다). 이 변수를 설정하는 것은 make의 작동에 영향을 미치지 않는다.
MAKE 변수가 작동하는 방법(How the MAKE Variable Works)
재귀적인 make 명령들은, 다음에서 볼 수 있듯이 `make' 명령 이름을 명시적으로 쓰지 않고, 항상 MAKE변수를 사용하는 것이 좋다.
make의 재귀적 사용(Recursive Use of make)
make의 재귀적 사용이란 make를 makefile에서 하나의 명령으로 사용한다는 것이다. 이 테크닉은 여러분이 더 큰 시스템을 만드는 여러 서브시스템들을 위한 여러 분리된 makefile들을 원할 때 유용하다. 예를 들어서 여러분이 자신의 makefile을 가지고 있는 `subdir' 서브디렉토리를 가지고 있고 상위 디렉토리의 makefile을 사용해서 그 서브 디렉토리에 대해서 make를 실행하고자 한다고 가정하자. 여러분은 다음과 같이 작성해서 이렇게 할 수 있다:
subsystem: cd subdir && $(MAKE)
또는 동일하게 다음과 같이 할 수도 있다 (see section 옵션들의 요약(Summary of Options)):
subsystem: $(MAKE) -C subdir
여러분은 이 예제를 그대로 복사해서 재귀적 make 명령을 작성할 수도 있지만 그들이 작동하는 방식과 이유, 그리고 서브-make가 톱-레벨 make에 어떻게 연결되어 있는가에 대해서 알아야 할 것들이 많이 있다.
여러분의 편의를 위해서 GNU make는 변수 CURDIR를 현재 작업 디렉토리의 경로명으로 설정한다. -C가 쓰였다면 오리지널 디렉토리가 아니라 새로운 디렉토리의 경로를 포함할 것이다. 그값은(CURDIR의 값 ?) 이것이 makefile안에서 설정되었다면 가질 우선순위와 동일하다(디폴트로 환경 변수 CURDIR는 이 값을 오버라이드하지 않을 것이다). 이 변수를 설정하는 것은 make의 작동에 영향을 미치지 않는다.
MAKE 변수가 작동하는 방법(How the MAKE Variable Works)
재귀적인 make 명령들은, 다음에서 볼 수 있듯이 `make' 명령 이름을 명시적으로 쓰지 않고, 항상 MAKE변수를 사용하는 것이 좋다.
2. 기존에 있는 라이브러리에 목적파일(.o) 추가하기
libft.a에 그대로 목적파일을 추가하는 것은 무리가 있고, 그냥 libft.a에 들어가는 목적파일들을 모아서 한번에 컴파일하는 것이 더 좋다.
여기서 좀 더 ar -x libft.a 명령어를 실행하면, libft.a에 있는 목적파일들이 풀어헤쳐지는 것을 볼 수 있다.[1]
단, ar -x ./libft/libft.a 하면, 목적파일이 현재 디렉토리에 생성된다.
ar 명령어 한국어 번역본
https://m.blog.naver.com/PostView.nhn?
makefile 문자열 만들기
http://blog.naver.com/estern/221578212449
https://stackoverflow.com/questions/3821916/how-to-merge-two-ar-static-libraries-into-one
1. https://nanite.tistory.com/77?category=737396
2. https://en.wikipedia.org/wiki/Makefile
3. https://www.gnu.org/software/make/manual/html_node/Pattern-Examples.html
4. 42born2code.slack.com/archives/CU6MTFBNH/p1583063518096100?thread_ts=1583063198.095000&cid=CU6MTFBNH
'42cursus > libft' 카테고리의 다른 글
Library 연결하기 (0) | 2020.06.30 |
---|