danbibibi
article thumbnail
Published 2024. 2. 13. 16:38
make, Makefile 정리 언어/C, C++

make

  • SW 개발을 위해 유닉스 계열 운영체제에서 사용되는 프로그램 빌드 도구
  • 어떤 파일들을 컴파일 하고, 어떤 방식으로 컴파일 할 지 직접 컴파일러에게 알려줘야 함
  • 프로젝트의 크기가 커지고 파일들이 많아진다면 매번 명령어를 치기는 힘들어짐
  • 이 문제를 해결하기 위해 리눅스에서는 make 라는 프로그램을 제공
  • make 프로그램은 Makefile을 읽어서 주어진 방식대로 명령어를 처리함
  • 즉, make 명령어를 통해 make 프로그램은 Makefile을 읽고, 많은 수의 파일들을 명령어 한번으로 컴파일 할 수 있음 
  • 또한, Increment Build를 수행한다는 장점이 있음 (단순 shell script 작성과의 차이점)

💡 빌드(build)
컴파일 언어를 실행파일로 변환하는 과정 (컴파일 + 링크)
> 컴파일(Compile): 소스 코드를 컴퓨터가 이해할 수 있는 어셈블리어로 변환하는 과정 (오브젝트 파일 생성)
> 링크(link): 오브젝트 파일을 하나의 실행파일로 엮어주는 것

💡 빌더(builder)
빌드를 하는 프로그램 ex) C언어 - gcc / C++ - g++

💡 Incremental build
반복적인 빌드 과정에서 변경된 소스코드에 의존성(Dependency)이 있는 대상들만 추려서 다시 빌드하는 기능
(Makefile 에서 빌드 대상(Target)별로 의존성을 명시하면 자동으로 Incremental build 를 수행하므로 매우 편리)
# make 사용 이전
$ g++ -c main.cc
$ g++ -c foo.cc
$ g++ -c bar.cc
$ g++ main.o foo.o bar.o -o main

# make 사용 이후
# Makefile 작성
$ make

 

 

 

Makefile

  • 프로그램을 빌드하기 위해 make 문법에 맞춰 작성하는 문서
  • 어떠한 조건으로 명령어를 실행할지 작성
  • 3가지 요소(target, recipes, prerequisites) 로 구성
    • target : 빌드 대상 이름. 최종적으로 생성하는 파일명
    • recipe : 빌드 대상을 생성하는 명령어. 여러 줄로 작성 가능. 각 줄 시작은 반드시 Tab 문자로 Indent 삽입
    • prerequisites(Dependncies) : 빌드 대상이 의존하는 Target이나 파일 목록. 여기 나열된 대상을 먼저 만들고, recipe 명령어 실행
# Makefile 구성 요소
target … : prerequisites …
(탭)recipe
				…
				…
                
                
# 기본 패턴
CC = <컴파일러>
CFLAGS = <컴파일 옵션>
LDFLAGS = <링크 옵션>
LDLIBS = <링크 라이브러리 목록>
OBJS = <Object 파일 목록>
TARGET = <빌드 대상 이름>
 
all: $(TARGET)
 
clean:
    rm -f *.o
    rm -f $(TARGET)
 
$(TARGET): $(OBJS)
$(CC) -o $@ $(OBJS)
# Makefile 예시
app.out : main.o foo.o bar.o
	gcc -o app.out main.o foo.o bar.o

main.o : foo.h bar.h main.c
	gcc -c -o main.o main.c

foo.o : foo.h foo.c
	gcc -c -o foo.o foo.c

bar.o : bar.h bar.c
	gcc -c -o bar.o bar.c
    
    
# Target recipe 생략
app.out : main.o foo.o bar.o
	gcc -o gcc.out main.o foo.o bar.o

main.o : foo.h bar.h main.c
	foo.o : foo.h foo.c
	bar.o : bar.h bar.c
    
    
# 한번에 실행파일(app.out) 생성
$ make


# 해당 Target만 빌드
$ make foo.o

 

변수

Recursively expanded variable

  • = 기호를 이용해 변수에 값을 할당
  • 변수에 다른 변수를 참조하고 있다면, 다른 변수가 참조하고 있는 값을 참조
  • 변수 뒤에 다른 것을 추가하는 경우, 무한 반복에 빠짐
foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:;echo $(foo) # Huh? 출력


# 무한 반복
# Makefile:1: *** Recursive variable `foo' references itself (eventually).  Stop.
foo = $(foo) -o

all:;echo $(foo)

 

Simply expanded variable

  • := 기호를 이용해 변수에 값을 할당
  • 재귀적으로 작동하지 않으며, 변수에 대입된 값을 그대로 출력
x := hello
x := $(x) world
all:;echo $(x)


# 실행 결과
echo hello world
hello world

 

 

함수

# 기본 문법
$(function arguments)

 

 

리링크

의존성이 변경되었을 때만 타겟을 생성하는 것

 

 

PHONY

  • Target 위치에 작성하지만, 레시피 실행을 위한 이름일 뿐, 실제 파일 이름이 아니라는 것을 알려주기 위해 사용
  • Makefile 에 흔히 추가하는 기능으로 빌드 관련된 파일들 (.o 파일들)을 모두 제거하는 명령을 넣음 
  • 그런데 만약 clean이라는 파일이 디렉토리에 생성된다면, make 는 "clean 의 필요 파일들이 없는데, clean 파일이 있으니까 clean 파일은 항상 최신이네? recipe 를 실행 안해도 되겠네!" 하면서 그냥 make clean 명령을 무시해버림
  • 이와 같은 상황을 막기 위해서 clean 을 PHONY 라고 등록하면, make clean 시 clean 파일의 유무와 상관 없이 언제나 해당 타겟의 명령을 실행
  • 보통 맨 윗줄에 작성하는 것이 편리
# Makefile에 흔히 추가하는 clean 기능
clean:
	rm -f $(OBJS) main


# clean을 PHONY라고 등록
.PHONY: clean
clean:
	rm -f $(OBJS) main

 

 

패턴 사용하기

  • 프로젝트에서 수십 ~ 수백 개의 파일들을 다루는 경우, 각각의 파일들에 대해 모두 빌드 방식을 명시해준다면 Makefile 크기가 매우 커질 수 있음
  • Makefile에서는 패턴 매칭을 통해 특정 조건에 부합하는 파일들에 대해 간단하게 recipe을 작성할 수 있도록 해줌
    • $@ : 타겟 이름에 대응
    • $< : 의존 파일 목록에 첫 번째 파일에 대응
    • $^ : 의존 파일 목록 전체에 대응
    • $? : 타겟 보다 최신인 의존 파일들에 대응
    • $+ : $^ 와 비슷하지만, 중복된 파일 이름들 까지 모두 포함
  • % 기호는 와일드카드와 비슷한 역할

디렉토리 구조

CC = g++
CXXFLAGS = -Wall -O2

SRCDIR = src
BUILDDIR = build

# 패턴 미사용
build/file1.o: src/file1.cc src/file1.h
    $(CC) $(CXXFLAGS) -c src/file1.cc -o build/file1.o

build/file2.o: src/file2.cc src/file2.h
    $(CC) $(CXXFLAGS) -c src/file2.cc -o build/file2.o

build/file3.o: src/file3.cc src/file3.h
    $(CC) $(CXXFLAGS) -c src/file3.cc -o build/file3.o


# 패턴 사용
# % 는 와일드카드 (*와 동일한 역할)
$(BUILDDIR)/%.o: $(SRCDIR)/%.cc $(SRCDIR)/%.h
    $(CC) $(CXXFLAGS) -c $< -o $@

 

 

 

 

자동으로 prerequisite 만들기

# -MD 옵션을 추가해서 컴파일
$ g++ -c -MD main.cc


# main.d 라는 파일 생성됨
$ cat main.d  
main.o: main.cc /usr/include/stdc-predef.h foo.h bar.h


# Makefile
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
컴파일 옵션
-Wall (모든 컴파일 경고를 표시)
-O2 (최적화 레벨 2)

 

 

GNU make 설치

 

[make] Windows 10에서 make 사용하기

Windows 10에서 make 사용하기 Windows에서 GNU make utility를 사용하여 설치하는 방법은 아래와 같다. 설치 링크 http://gnuwin32.sourceforge.net/packages/make.htm Make for Windows • Complete package, except sources Setup 3384653 2

jstar0525.tistory.com

 

'언어 > C, C++' 카테고리의 다른 글

[C/C++] 공백을 포함한 파일을 문자열 변수에 쓰기  (0) 2024.05.27
CMake, CMakeLists.txt 정리  (0) 2024.02.19
[C++] stringstream  (0) 2024.01.02
RapidJSON 정리  (0) 2023.12.27
json-c 정리  (1) 2023.12.26
profile

danbibibi

@danbibibi

꿈을 꾸는 시간은 멈춰 있는 것이 아냐 두려워하지 마 멈추지 마 푸른 꿈속으로