참고 링크 : https://www.youtube.com/watch?v=jnJL6ppn26Q 의 내용을 요약해서 정리한 내용입니다.
* makefile이 필요한 이유
: 파일이 많아질수록 컴파일이 복잡해지기 때문에
1) 반복되는 컴파일 작업이 지겹고 시간이 오래 걸려서
2) 수정된 파일만 컴파일 할 수 있어서
3) 대규모 프로젝트, 공동 프로젝트에서 반드시 필요
* 컴파일 과정
: 소스파일(*.c) -> 목적파일(*.o) -> 실행파일(a.out) (바이너리)
- 소스파일: vi, vscode 등의 편집기로 편집. 인간이 이해할 수 있는 프로그래밍 언어
- 목적파일: object 파일, 컴파일러를 통해 번역된 기계어
- 실행파일: 기계어 + 라이브러리를 묶는 "링킹"과정을 거쳐 만들어진 파일
cf) main함수에서 쓰이는 함수들의 헤더파일을 만드는 이유: main에서 호출하는 함수가 어딨는지 알아야하기 때문에
* 컴파일 옵션
- -c 옵션 : 소스파일(.c)로 목적파일(.o)을 생성
ex) gcc -c main.c kor.c usa.c
-> kor.o, main.o usa.o 가 생성됨
- 생성된 목적파일(.o)을 링커 과정을 통해 실행파일로 생성
ex) gcc -o app.out kor.o main.o usa.o
-> 실행파일 app.out이 생성됨
- 소스파일(.c)를 한번에 실행파일로 바꾸는 법
ex) gcc -o app.out kor.c main.c usa.c
-> 컴파일러가 알아서 .c(소스파일)를 .o(목적파일)로 만들고, 링커를 걸어서 실행파일로 바로 바꿔줌
-> 이 경우, 디렉토리에 .o 파일이 안남고, 실행파일 app.out만 추가됨 (.o 파일을 생성하지 X)
* Makefile의 구조
TARGET : DEPENDENCY
(tab넣음) command
-> TARGET(만들고자 하는 파일)을 만들기 위해서는 DEPENDENCY 파일들이 필요. 이 둘을 : 으로 구분
-> DEPENDENCY에 command를 사용하여 명령을 실행시키고, 이를 통해 TARGET 파일을 생성함
cf) Makefile은 make라는 명령어로 실행시킬 수 있다!
* 코드 예시 (개념의 적용 순서대로 하나하나 예시가 있음)
ex01) 기본적인(명시적인) 코드
// main.c kor.c usa.c
// -> main.o kor.o usa.o
// --> binary (app.out)
app.out : main.o kor.o usa.o
gcc -o app.out main.o kor.o usa.o
main.o :
gcc -c main.c
kor.o :
gcc -c kor.c
usa.o :
gcc -c usa.c
ex02) all 추가
// all이란? 최종적으로 니가 만들고 싶은게 뭐냐를 명시하는 것
// all 옵션이 없는 경우, 제일 첫번째 Target만 실행시키고 종료 (즉 main.o가 위에 있으면 app.out 생성 없이 main.o만 실행시키고 끝남)
// all 옵션을 주면, 순서가 어딜가나 상관없이, 실행시키고자 하는 타겟을 찾아 실행 (타겟의 dependency를 만들어 결과물을 만드는 순서대로 잘 실행됨)
all : app.out
main.o :
gcc -c main.c
kor.o :
gcc -c kor.c
usa.o :
gcc -c usa.c
app.out : main.o kor.o usa.o
gcc -o app.out main.o kor.o usa.o
ex03) 컴파일러, 실행파일을 변수로 설정
// 컴파일러를 원하는 걸로 바꿀 수 있게, 환경변수로 빼주면 좋다
// 그렇게 되면, g++로 컴파일러를 바꿔도, 명령어 전체에서 gcc를 찾아가며 일일이 바꿔줄 필요가 없음
// 이렇게 변수를 추가하면, 다른 프로그램에서 다시 활용하기도, 수정하기도 쉬워짐 (유연해짐)
CC = gcc
TARGET = app.out
all : ($TARGET)
main.o :
$(CC) -c main.c
kor.o :
$(CC) -c kor.c
usa.o :
$(CC) -c usa.c
($TARGET) : main.o kor.o usa.o
$(CC) -o ($TARGET) main.o kor.o usa.o
ex04) 의존 파일 변수 추가
// 실행에 필요한 파일들이 추가되더라도, 일일이 코드에 추가하지 않고 한번에 바꿀 수 있게 함
// 환경파일에서 추가된 소스만 넣으면, 깔끔하게 빌드될 수 있게, OBJS 변수 사용
CC = gcc
TARGET = app.out
OBJS = main.o kor.o usa.o
all : ($TARGET)
($TARGET) : ($OBJS)
$(CC) -o ($TARGET) ($OBJS)
main.o :
$(CC) -c main.c
kor.o :
$(CC) -c kor.c
usa.o :
$(CC) -c usa.c
ex05) $@, $^, .c.o, $<
// 타겟이라는 변수를 command에서 다시 언급하는 대신 $@ 씀
// Object 파일들을 $^를 통해 dependency 파일로 치환함
// .c.o를 통해, 파일 추가 시 새로 또 코드를 적는 것을 방지함
// -> makefile이 위치한 공간의 .c 파일을 모두 읽어 .o 파일로 바꿔준다는 뜻
CC = gcc
TARGET = app.out
OBJS = main.o kor.o usa.o
all : ($TARGET)
($TARGET) : ($OBJS)
$(CC) -o ($TARGET) ($OBJS)
.c.o:
$(CC) -c -o $@ $<
// $@를 통해 .o 파일을 타겟으로 잡아줌
// $<를 통해 .c 라는 소스 파일을 잡아줌
ex06) CFLAGS, LDFLAGS
// CFLAGS - 컴파일 옵션
// ex) -Wall (warning이나 에러 등을 상세하게 출력), -g(디버깅 모드)
// LDFLAGS - 링크 옵션, 바이너리를 만들 때 참조할 옵션. 보통 라이브러리 관련 된 것들이 들어감
// ex) -lopenssl(openssl라이브러리), -lc(c라이브러리)
CC = gcc
TARGET = app.out
OBJS = main.o kor.o usa.o
CFLAGS = -Wall -g
LDFLAGS = -lc
all : ($TARGET)
($TARGET) : ($OBJS)
$(CC) ($LDFLAGS) -o ($TARGET) ($OBJS)
.c.o:
$(CC) ($CFLAGS) -c -o $@ $<
ex07) clean 추가
// 실행 후 생성된 .o (object)파일과 실행파일(binary)를 지워주는 명령어 추가
// make clean 실행 시 지워짐
CC = gcc
TARGET = app.out
OBJS = main.o kor.o usa.o
CFLAGS = -Wall -g
LDFLAGS = -lc
all : ($TARGET)
($TARGET) : ($OBJS)
$(CC) ($LDFLAGS) -o ($TARGET) ($OBJS)
.c.o:
$(CC) ($CFLAGS) -c -o $@ $<
clean :
rm -f $(OBJS) $(TARGET)
ex08) main.c 파일을 수정 후, 다시 메인 함수 실행
타임 스탬프를 읽어, 수정된 파일만 읽어들여 다시 컴파일함 (효율성 up)
cf) 한번 makefile 잘 만들어두면, 소스파일 변수만 수정해서 잘 재탕해서 평생 쓸 수 있음
'IT > 42Seoul' 카테고리의 다른 글
[Libft] C 언어 라이브러리 구현_BONUS_보너스 함수 구현2 (0) | 2021.05.30 |
---|---|
[Libft] C 언어 라이브러리 구현_BONUS_보너스 함수 구현1 (0) | 2021.05.30 |
Makefile 정리1 (0) | 2021.05.25 |
TIP : 코드 리뷰 잘 하는 법 (0) | 2021.05.24 |
[Libft] C 언어 라이브러리 구현_Part2_추가함수 구현3 (0) | 2021.05.23 |