밥한그릇배따시게
SW 개발자 블로그
밥한그릇배따시게
전체 방문자
오늘
어제
  • 분류 전체보기 (71)
    • Notice (공지) (2)
    • IT (58)
      • 학과 공부 (13)
      • Algorithm (1)
      • 42Seoul (20)
      • 데이터 과학 & 인공지능 (5)
      • go 언어 (3)
      • 블록체인 (2)
      • 왕초보 강의 (5)
      • ETC (4)
    • About Me (5)
      • 회고록 (1)
      • 일상 (4)
    • English (6)
      • 회화 (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • til
  • get_next_line
  • GIT
  • 라이브러리
  • KOCW
  • c언어
  • FD
  • 스피킹
  • m1
  • GNL
  • 계절학기
  • 영어
  • 영어표현
  • 왕초보
  • cqu
  • 운영체제
  • 컴퓨터공학
  • 오류
  • Github
  • parser
  • vscode
  • 전공공부
  • IT
  • 온라인 어학연수
  • 42본과정
  • 영어공부
  • 회화
  • 42seoul
  • Libft
  • 컴파일러

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
밥한그릇배따시게

SW 개발자 블로그

IT/42Seoul

Makefile 정리1

2021. 5. 25. 12:49

참고 링크 : https://modoocode.com/311 의 내용을 요약 정리해보았습니다.

 

 


* 컴파일 (compile)

: 소스 코드 -> 어셈블리어(컴퓨터가 이해할 수 있는 언어)

성공적으로 컴파일 시, main.o라는 목적 파일(object file)이 생성됨

 

예제 코드)

#include "bar.h"

#include "foo.h"

int main() {

foo();

bar();

}

 


* 링킹 (linking)

: 서로 다른 파일에 흩어져 있는 함수나 클래스들을 한데 묶어서 링크해줌, 컴파일러에 object 파일을 전달

위 코드를 컴파일 해도, main.o 외에 foo.o bar.o가 있어야 실제 함수가 작동함 (main.o 에는 foo와 bar의 코드가 없고 함수의 호출만 정의되어 있음)

따라서 main.o foo.o bar.o를 링킹하여 이 파일들이 모두 하나로 합쳐져야 프로그렘이 제작됨

 

 

 


* make 를 쓰는 이유?

: 일부 파일을 수정할 경우 필요한 명령만 빠르게 컴파일 할 수 있음

* make

: 주어진 쉘 명령어들을 조건에 맞게 실행하는 프로그램

 

Makefile : 이때 어떠한 조건으로 명령어를 실행할 지 담은 파일, make 실행 시 Makefile을 읽어들임

 

 


* Makefile의 3요소

1) target (make할 파일)

2) recipes (실행할 명령어) :

주어진 타겟을 make 할 때 실행할 명령어들의 나열. (주의사항: 명령어 쓸 때, tab 1번의 들여쓰기 필수)

3) prerequisites (필요 조건들) :

주어진 타겟을 make할 때 사용될 파일들의 목록. 의존 파일 (dependency)이라고도 함 (해당 타겟 처리하기 위해 건드려야 할 파일들을 써놓음)

 

만일 주어진 파일들의 수정 시간 보다 타겟이 더 나중에 수정되었다면 해당 타겟의 명령어를 실행하지 않음 (이미 이전에 타겟이 만들어져있다고 간주)

ex) 이를 통해, 수정되지 않은 타겟들은 굳이 다시 컴파일 하지 않고 놔둘 수 있음.

ex) Makefile 형식 !!

target … : prerequisites …

(탭)recipe

…

…

 

 

 


* 변수

: 변수 사용 시 $()안에 사용하고자 하는 변수의 이름을 지정하면 됨

 

ex01)

CC = g++

foo.o : foo.h foo.cc

         ($)CC -c foo.cc

 

ex02)

CC = g++

foo.o : foo.h foo.cc

          g++ -c foo.cc

-> ex01과 ex02의 의미가 같다

 

 

TMI) 변수를 정의하는 또 다른 방법 := 과 기존 = 의 차이

:= 로 변수를 정의할 경우, 해당 시저의 변수의 값만 확인함

반면에, = 로 변수를 정의할 경우, 참조할 값이 정의될 때까지 변수의 값이 결정되지 않음

대부분의 상황에서는 = 나 := 중 아무거나 사용해도 상관 없음

 

ex)

B = $(A)

C = $(B)

A = a

-> A가 실제로 정의될 때까지 B와 C가 결정되지 않는다. 마지막에 A = a 를 통해 A가 a로 대응되어야, C가 a로 결정됨

 

ex)

B := $(A)

A = a

-> := 를 통해 해당 시점에의 변수 값만 확인하므로, B는 그냥 빈 문자열이 됨

- 변수들의 정의 순서에 크게 구애받고 싶지 않다면 = 를 사용하는 것이 편함

- A = 와 같이 자기 자신을 수정하고 싶다면, := 를 사용해야 무한 루프를 피할 수 있음

 

 

TMI)

CC, CXXFLAGS 는 Makefile에서 자주 사용되는 변수

CC : 컴파일러 이름

CXXFLAGS : 컴파일러 옵션

 

 


* PHONY :

Makefile에 흔히 추가하는 기능으로 빌드 관련된 파일들 (.o 파일들)을 모두 제거하는 명령 넣음

 

주의)

실제로 clean이라는 파일이 디렉토리에 생성되면, make clean시 문제가 생김

make는 clean의 필요 파일들이 없는데, clean 파일이 있으니까 clean 파일은 항상 최신이므로, recipe를 실행하지 않겠다며 make clean 명령을 무시함

 

-> 이를 방지하기 위해서, clean을 PHONY라고 등록함 (Phony : 가짜의, 허위의)

그리고 make clean을 실행하면, clean 파일의 유무와 상관 없이 언제나 해당 타겟의 명령을 실행하게 됨

 


* 패턴 사용하기 :

실제 프로젝트 시, 엄청나게 많은 파일들을 다루게 됨. 이런 경우 한꺼번에 빌드 방식을 명시햐야함.

이때, "패턴 매칭"을 통해서 특정 조건에 부합하는 파일들에 대해, 간단하게 recipe를 작성할 수 있음

 

1) %.o : 와일드 카드. *.o와도 같음 (.o로 끝나는 파일 이름들을 타겟으로 함)

2) $< : prerequisite 에서 첫 번째 파일의 이름에 대응됨

3) 그 외의 Makefile에서 제공하는 자동변수

            (1) $@ : 타겟 이름에 대응됨

            (2) $< : prerequisite 에서 첫 번째 파일의 이름에 대응됨

            (3) $^ : 의존 파일 목록 전체에 대응됨

            (4) $?: 타겟보다 최신인 의존 파일들에 대응됨

            (5) $+: $^와 비슷하지만, 중복된 파일 이름들까지 모두 포함

 

ex01)

foo.o : foo.h foo.cc

          $(CC) $(CXXFLAGS) -c foo.cc

bar.o : bar.h bar.cc

          $(CC) $(CXXFLAGS) -c bar.cc

 

ex02)

foo.o: foo.cc foo.h

           $(CC) $(CXXFLAGS) -c $<

 

-> ex01의 명령어들에 패턴을 사용하면, ex02와 같이 나타낼 수 있음

 

cf) 패턴은 타겟과 prerequisite 부분에만 사용할 수 있음. recipe 부분에서는 패턴을 사용할 수 없음

따라서 컴파일러에 foo.cc를 전달하기 위해서는 Makefile의 자동 변수를 사용해야함

 

 


 

* 자동으로 prerequisite 만들기:

컴파일 시에 -MD 옵션을 추가해서 컴파일

이를 통해, 목적 파일 말고도 컴파일 한 소스파일을 타겟으로 하는 의존파일 목록을 담은 파일을 생성해줌 (main.d라는 파일을 생성함)

 

cf)

$ cat main.d

main.o: main.cc /usr/include/stdc-predef.h foo.h bar.h

-> main.d를 살펴보면, target : prerequisite 인 것 같은 부분이 생성된다.

이때, /usr/include/stdc-predef.h 파일은 컴파일러가 컴파일 할 때 암묵적으로 참조하는 헤더파일이다.

(-MD를 통해 컴파일러가 생성한 의존파일 목록이므로 포함됨)

 

ex)

CC = g++

CXXFLAGS = -Wall -O2

OBJS = foo.o bar.o main.o

%.o: %.cc %.h

        $(CC) $(CXXFLAGS) -c $<

main : $(OBJS)

        $(CC) $(CXXFLAGS) $(OBJS) -o main

.PHONY: clean

clean:

rm -f $(OBJS) main

include main.d

 

-> 마지막에 include main.d를 넣어서 Makefile에 main.d를 포함시킴

 

ex)

CC = g++

CXXFLAGS = -Wall -O2

OBJS = foo.o bar.o main.o

%.o: %.cc

         $(CC) $(CXXFLAGS) -c $<

main : $(OBJS)

         $(CC) $(CXXFLAGS) $(OBJS) -o main

.PHONY: clean

clean:

rm -f $(OBJS) main

-include $(OBJS:.o=.d)

 

-> %.o: %.cc %.h

 

$(CC) $(CXXFLAGS) -c $< 이 부분을 컴파일러가 생성한 .d 파일로 대체함

-> $(OBJS:.o=.d) 부분은 OBJS 에서 .o 로 끝나는 부분을 .d 로 모두 대체하라는 의미임

즉, 해당 부분은 -include foo.d bar.d main.d 가 됨

 

-> include 될 때 이미 있는 %.o : %.cc 는 어떻게 되느냐? :

같은 타겟에 대해서 여러 의존 파일 목록들이 정해져 있다면 이는 make에 의해 모두 하나로 합쳐짐

-> include 가 아닌 -include의 경우, 포함하고자 하는 파일이 존재하지 않아도 make 메세지를 출력하지 않음

 

cf)

맨 처음에 make를 할 때에는 .d 파일들이 제대로 생성되지 않은 상태이기 때문에, include 가 아무런 .d 파일들을 포함하지 않음

물론 큰 문제가 없는게, 어차피 .o 파일들도 make가 %.o:%.cc 부분의 명령어들을 실행하면서 컴파일 하기 때문에, make를 하게 될 때에는 제대로 .d 파일들을 로드할 수 있음

 

 


* 최종 정리

 

1) 헤더파일들을 분리하지 않는 경우

Makefile, obj, src/bar.cc + src/bar.h + src/foo.cc + src/foo.h + src/main.cc 의 프로젝트 구조를 가정함

이와 같은 구조에서 항상 사용할 수 있는 만능 Makefile은 다음과 같음

 

(중요)

CC = g++

# C++ 컴파일러 옵션

CXXFLAGS = -Wall -02

# 링커 옵션

LDFLAGS =

# 소스 파일 디렉토리

SRC_DIR = ./src

# 오브젝트 파일 디렉토리

OBJ_DIR = ./obj

# 생성하고자 하는 파일 이름

TARGET = main

# Make 할 소스 파일들

# wildcard 로 SRC_DIR 에서 *.cc로 된 파일 목록을 뽑아낸 뒤에

# notdir로 파일 이름만 뽑아낸다.

# (e.g SRCS는 foo.cc bar..cc main.cc가 된다.)

SRCS = $(notdir $(wildcard $(SRC_DIR)/*.cc))

OBJS = $(SRCS:.cc=.o)

# OBJS 안의 object 파일들 이름 앞에 $(OBJS_DIR)/ 을 붙인다

# patsubjst 함수 사용, $(patsubst 패턴,치환 후 형태,변수)와 같은 꼴로 사용

# $(OBJS) 안에 있는 모든 %.o 패턴을 $(OBJ_DIR)/%.o 로 치환해라

OBJECTS = $(patsubjst %.o, $(OBJ_DIR)/%.o, $(OBJS))

DEPS = $(OBJECTS: .o=.d)

all: main

$(OBJ_DIR)/%.o : $(SRC_DIR)/%.cc

          $(CC) $(CXXFLAGS) -c $< -o $@ -MD $(LDFLAGS)

$(TARGET) : $(OBJECTS)

           $(CC) $(CXXFLAGS) $(OBJECTS) -o $(TARGET) $(LDFLAGS)

.PHONY: clean all

clean:

rm -f $(OBJECTS) $(DEPS) $(TARGET)

-include $(DEPS)

 

 

 

2) 헤더 파일들을 따로 뽑는 경우

파일 구조: include (.h파일들 모아둔 폴더), Makefile, obj, src(.cc 모아둔 폴더)

 

 

# 헤더파일 경로

INCLUDE = -Iinclude/

컴파일러 옵션에 -Iinclude/ 를 추가해주면 됨. 여기서 include 는 헤더파일 경로를 의미함

 

 


 

* 멀티 코어를 활용해서 Make 속도를 올리자 :

그냥 make를 실행하게 되면 1개의 쓰레드만 실행되어서 속도가 꽤 느림. 멀티 코어 cpu를 사용하는 컴퓨터에서는 make를 여러 개의 쓰레드에서 돌릴 수 있음

이를 위해선 인자로 -j 뒤에 몇 개의 쓰레드를 사용할 지 숫자를 적어서 전달하면 됨

 

ex) make -j8

-> make가 8개의 쓰레드에 나뉘어 실행. make 속도가 월등히 향상됨

코어 개수 + 1 만큼의 쓰레드를 생성해서 돌리는 것이 가장 속도가 빠름

만약 내 컴퓨터의 코어 개수를 모른다면? (리눅스의 경우)

 

ex) make -j$(nproc

-> $(proc) 이 알아서 내 컴퓨터의 현재 코어 개수로 치환됨

 


 

저작자표시

'IT > 42Seoul' 카테고리의 다른 글

[Libft] C 언어 라이브러리 구현_BONUS_보너스 함수 구현1  (0) 2021.05.30
Makefile 정리2  (0) 2021.05.25
TIP : 코드 리뷰 잘 하는 법  (0) 2021.05.24
[Libft] C 언어 라이브러리 구현_Part2_추가함수 구현3  (0) 2021.05.23
[Libft] C 언어 라이브러리 구현_Part2_추가함수 구현2  (0) 2021.05.23
    'IT/42Seoul' 카테고리의 다른 글
    • [Libft] C 언어 라이브러리 구현_BONUS_보너스 함수 구현1
    • Makefile 정리2
    • TIP : 코드 리뷰 잘 하는 법
    • [Libft] C 언어 라이브러리 구현_Part2_추가함수 구현3
    밥한그릇배따시게
    밥한그릇배따시게
    공부하고 정리한 내용 중, 공유할만한 내용들을 포스팅합니다. / 소프트웨어 학사 (2025년도 2월 졸업)

    티스토리툴바