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

1. 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 를 수행하므로 매우 편리)
<bash />
# 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

 

 

 

2. Makefile

  • 프로그램을 빌드하기 위해 make 문법에 맞춰 작성하는 문서
  • 어떠한 조건으로 명령어를 실행할지 작성
  • 3가지 요소(target, recipes, prerequisites) 로 구성
    • target : 빌드 대상 이름. 최종적으로 생성하는 파일명
    • recipe : 빌드 대상을 생성하는 명령어. 여러 줄로 작성 가능. 각 줄 시작은 반드시 Tab 문자로 Indent 삽입
    • prerequisites(Dependncies) : 빌드 대상이 의존하는 Target이나 파일 목록. 여기 나열된 대상을 먼저 만들고, recipe 명령어 실행
<bash />
# 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)
<bash />
# 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

 

2.1. 변수

2.1.1. Recursively expanded variable

  • = 기호를 이용해 변수에 값을 할당
  • 변수에 다른 변수를 참조하고 있다면, 다른 변수가 참조하고 있는 값을 참조
  • 변수 뒤에 다른 것을 추가하는 경우, 무한 반복에 빠짐
<bash />
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)

 

2.1.2. Simply expanded variable

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

 

 

2.2. 함수

<bash />
# 기본 문법 $(function arguments)

 

 

2.3. 리링크

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

 

 

2.4. PHONY

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

 

 

2.5. 패턴 사용하기

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

디렉토리 구조

<bash />
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 $@

 

 

 

 

2.6. 자동으로 prerequisite 만들기

<bash />
# -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)

 

 

3. 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

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