마비노기의 등장인물에 대한 내용은 메이크 문서 참고하십시오.
1. 개요
1976년 Stuart Feldman이 개발한 빌드 자동화 소프트웨어.Makefile
이라고 불리는 설정 파일을 읽어 소스 코드와 라이브러리로부터 실행파일을 생성하는 작업을 자동화 한다.처음에는 UNIX 계열 운영체제를 대상으로 만들어졌으며, 주로 C언어 컴파일러인
CC
와 함께 작동한다.2. 필요성
CLI 환경에서 컴파일을 할 경우, 소스 코드 파일의 수가 많아지면 입력해야 할 텍스트의 양이 늘어난다.예를 들어 C언어로 3개의 소스 코드 파일
test.h
, test.c
, main.c
로 구성된 프로그램을 빌드한다고 가정한다. (test.c
와 main.c
파일은 각각 test.h
를 포함한다.)이 경우 유닉스에서 C언어 컴파일러를 이용하여 최종 출력물인 main 바이너리 파일을 만들기 위해서 입력해야 하는 커맨드는 아래와 같다.
#!syntax shell
cc -c test.c
cc -c main.c
cc -o main main.o test.o
-c
는 소스 파일을 컴파일하여 .o
형식의 목적 파일로 만드는 옵션이고, -o
는 목적 파일을 링킹하여 하나의 실행 파일로 만드는 옵션이다.[1] 즉, 이 커맨드는 test.o
파일과 main.o
파일을 생성한 후 그 파일들을 링킹하여 main이라는 실행 파일로 출력하라는 뜻이다. 고작 파일 세 개 컴파일하는 데 비슷한 문장을 여러 번 입력해야 하니 참으로 귀찮은 일이 아닐 수 없다. 이러한 빌드 과정을 자동화하기 위해 나온 소프트웨어가 바로 Make이다.3. 사용 예제
위에서 예로 든 세 개의 파일이 아래와 같이 작성되었다고 해 보자.#!syntax cpp
//========= test.h =========//
#ifndef __TEST_H__
#define __TEST_H__
void TestFunc();
#endif
#!syntax cpp
//========= test.c =========//
#include <stdio.h>
#include "test.h"
void TestFunc() {
printf("TestFunc()\n");
}
#!syntax cpp
//========= main.c =========//
#include <stdio.h>
#include "test.h"
int main() {
printf("main()\n");
TestFunc();
return 0;
}
이 파일들을 하나로 묶어 main 바이너리 파일을 한 번에 생성하려면 Make가 필요한데, Make는 해당 폴더 내의 Makefile이라는 빌드 자동화 스크립트 파일을 탐색하게 된다. 이는 VC++ 컴파일러에 쓰이는 .vcxproj 파일과 동일한 역할이라고 할 수 있다. 소스 파일이 있는 폴더 안에 아래와 같은 코드를 입력한 후 Makefile이라는 이름으로 저장하자.
main: test.o main.o
gcc -std=c11 -g -o main test.o main.o
test.o: test.h test.c
gcc -std=c11 -g -c test.c
main.o: test.h main.c
gcc -std=c11 -g -c main.c
-std=c11
은 C언어의 표준을 C11로 설정하고, -g
는 디버깅이 가능하도록 컴파일 시 디버깅 정보를 삽입하는 명령이다. 보면 알겠지만 위에서 보인 컴파일 커맨드 라인 한 줄 한 줄을 그대로 옮겨 놓았을 뿐이다. 그리고 목적 파일의 이름은 헤더/소스 파일의 이름과 같아야 인식이 된다. Makefile에서는 이를 Rule Block이라고 한다. 단, Makefile을 작성할 때는 몇 가지 원칙이 있는데 간단한 것만 소개하면 다음과 같다.- 최종 출력물의 링킹을 명령하는 라인(
main: test.o main.o
)은 맨 위에 와야 한다. 만약test.o: test.h test.c
라인이 맨 위에 오면test.o
파일만 생성하고 끝나 버린다. - 하나의 Rule Block은 다음과 같은 구조를 지켜야 한다.
{{{[Target]: [Dependency]
}}}* Target은 빌드할 대상의 이름, Dependency는 빌드하기 위해 필요한 헤더/소스 파일들(의존성), Command는 빌드 커맨드를 의미한다. 그리고 Command 문장의 앞 부분은 반드시 Tab으로 들여쓰기를 해야 한다.
그러나 이것도 역시 만족스럽지 않다. 빌드해야 할 파일이 늘어날 경우 입력해야 할 커맨드의 개수가 늘어나면 자동화의 의미가 없다. 아래와 같이 매크로를 사용하면 코드 타이핑 횟수를 줄이고 재사용성을 높일 수 있다.
CC = gcc
CFLAGS = -std=c11 -g
OBJ = main.o test.o
TARGET = main
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) -o $@ $(OBJ)
all: $(TARGET)
clean:
rm -f *.o
rm -f $(TARGET)
# del /q /s *.exe # 윈도우 전용
- CC: 컴파일을 수행할 C 컴파일러의 이름. C++ 컴파일러를 사용할 경우 CC를 CXX로 변경한다.
- CFLAGS: 컴파일 옵션. C++ 컴파일러는 CXXFLAGS를 사용한다.
- $@: 해당 Rule Block의 Target을 나타내는 자동 변수
프로그래머마다 조금씩 차이는 있겠지만 대체로 이와 비슷한 형태로 Makefile을 작성한다. CC와 CFLAGS는 Make 자체에 포함된 내장 변수이고, 나머지는 임의로 선언한 사용자 변수이다. 먼저 OBJ에 실행 파일을 만드는 데 필요한 목적 파일을 모두 기술하고, TARGET에는 최종 실행 파일의 명칭을 입력하였다. 그리고 Rule Block의 구조에 맞춰 -o 옵션을 주면, 'make all'을 입력할 시 각각의 소스 파일에 대한 -c 옵션과 목적 파일에 대한 -o 옵션이 컴파일 옵션에 맞춰 자동으로 처리되는 결과를 보여준다. 이 때 매크로는 모두 $(...)의 형태로 사용해야 한다.
all에는 Target의 이름을 지정하여, Rule Block이 여러 개일 경우 그것들을 순서대로 한 번에 실행할 수 있다. 여기서는 Rule Block이 한 개이므로 실행 시 그냥 make라 입력해도 무방하다. clean에는 rm -f 명령어[2]를 지정해서 'make clean' 명령을 통해 .o로 끝나는 모든 목적 파일과 Target으로 지정된 실행 파일을 삭제하도록 한다.
이제 터미널 창에서 'make all'을 입력하면 아래와 같은 문장이 출력된다.
gcc -std=c11 -g -c -o main.o main.c
gcc -std=c11 -g -c -o test.o test.c
gcc -std=c11 -g -o main main.o test.o
위에서는 기술하지 않았지만, main.o와 test.o를 생성하기 위해 main.c, test.c 파일의 컴파일(-c)이 자동으로 진행되는 것을 볼 수 있다. 빌드 커맨드 명령을 Rule Block마다 하나하나 입력할 필요 없이, 상황에 맞춰 OBJ 또는 TARGET의 값만 바꿔주면 된다. 또한 프로젝트 전체가 아닌 특정 파일, 예를 들어 test.o 파일만 빌드하고 싶다면 'make test.o'를 입력하여 해당 파일만 개별적으로 빌드할 수도 있다.4. 파생 소프트웨어
- BSD Make
- GNU Make
- Microsoft nmake
BSD 계열 운영체제에 내장되어 있다.
Visual Studio에서도 컴파일시에는 내부적 컴파일러/링커 동작에 make 명령어를 사용한다. 비주얼 스튜디오 인스톨시 같이 설치되는 x86(x64) Native 명령 프롬프트를 사용하면 nmake 명령어를 통해 컴파일이 가능한데, 가끔씩 멀티플랫폼 오픈소스 프로젝트중에 비주얼 스튜디오 설정 파일(sln, vcxproj 등)을 넣어주지 않고 nmake를 사용해 컴파일 하라고 하는 경우도 있으므로 오픈소스를 자주 사용하는 경우 숙지해두는 것이 좋다. 또한 비주얼 스튜디오에서도 CMake 파일의 내보내기/불러오기 기능으로 상호 변환이 가능하다.[3]
4.1. CMake
Make는 많은 수의 파일들을 간편하게 빌드하는 데 있어 아주 요긴한 프로그램이지만, 프로젝트의 규모가 워낙 거대해져 Makefile로는 감당이 안 되거나 크로스플랫폼 빌드를 하기엔 Make만으로는 부족함이 많았다. 이후 Kitware라는 회사에서 크로스플랫폼과 빌드 스크립트 자동 생성 기능을 지원하는 소프트웨어 CMake를 출시한 이후 이러한 문제가 해소되었다. GPL을 따르는 Make와는 달리 CMake는 BSD 라이선스를 따르며, 윈도우와 macOS 및 리눅스를 모두 지원한다.CMake는 지정한 폴더 내의 CMakeLists.txt 파일을 탐색한다. 사용된 소스 파일은 위에서 쓰인 것과 같으며(test.h, test.c, main.c), CMakeLists.txt 파일의 기본적인 템플릿은 아래와 같다.
# 이 CMakeLists.txt 파일을 실행할 수 있는 CMake의 최소 버전
cmake_minimum_required(VERSION 3.10)
# 프로젝트명
project(TestProject)
# 컴파일에 사용될 소스 파일: 헤더 파일은 없어도 무방하지만,
# Precompiled Header를 사용하거나 IDE Integration을 사용하는 경우를 위해 헤더도 포함시켜 주는 것이 좋다.
set(HEADERS main.h)
set(SOURCES main.c test.c)
# 실행 가능한 바이너리를 빌드하기 위한 바이너리의 이름과 소스를 지정
add_executable(main ${SOURCES} ${HEADERS})
# GCC/Clang의 -std=c11
set_property(TARGET main PROPERTY C_STANDARD 11)
# C/C++ 컴파일러에 전달할 컴파일 옵션 설정
if(MSVC)
target_compile_definitions(main PRIVATE /W4 /WX /Za /permissive- /EHsc)
else()
target_compile_definitions(main PRIVATE -Wall -Wextra -pedantic -Werror)
endif()
# 바이너리 파일 'main'을 컴파일할 때 포함할 라이브러리
target_link_libraries(main PRIVATE m)
이렇게 작성을 하고 'cmake .'를 입력하면 Makefile이 자동으로 생성되는 것을 확인할 수 있다. 단, 이 명령어는 해당 OS의 디폴트 컴파일러를 호출하기 때문에[4], 별도의 컴파일러를 사용하고 싶다면 -G 옵션을 사용해야 한다.[5]...(전략)...
# The main all target
all: cmake_check_build_system
$(CMAKE_COMMAND) -E cmake_progress_start ...(생략)...
$(MAKE) -f CMakeFiles\Makefile2 all
$(CMAKE_COMMAND) -E cmake_progress_start ...(생략)...
.PHONY : all
# The main clean target
clean:
$(MAKE) -f CMakeFiles\Makefile2 clean
.PHONY : clean
...(후략)...
CMake를 통해 만들어진 Makefile에는 굉장히 복잡한 정보들이 생성되어 있으며, 위에서 예시로 든 Makefile처럼 make all과 make clean 옵션도 포함되어 있다. 이렇게 cmake 명령을 한 번 사용하고 나면 다음부터는 기존의 Make처럼 'make all' 명령어만으로 계속 빌드를 할 수 있다.[6] CMake로 생성한 Makefile을 실행하면 CMakeLists.txt에 지정된 소스 파일에 의존성 변화가 생겼는지를 먼저 검사하고, 변화가 생겼을 경우 해당 소스 파일의 의존성을 추적하여 Makefile을 재생성한 다음 컴파일을 진행한다. 이 덕분에 소스 파일의 개수나 사용하는 라이브러리 파일의 수가 증가해도 CMakeLists.txt만 수정해 주면 간편한 빌드가 가능하다.윈도우에 한정된 문제로, CMake는 시스템에 등록된 환경 변수 경로에 sh.exe 파일이 있으면 정상 작동이 이루어지지 않는다. sh.exe 파일은 MSYS 1.0으로 설치되는 MinGW에 포함된 셸 프로그램인데, MSYS 1.0과 CMake를 함께 사용해야 하는 상황이 발생할 수 있다. 이럴 때는 CMake 빌드 옵션을 제공하는 IDE의 경우 SHELL=cmd를 추가하면 되고, IDE 환경이 아닐 경우엔 sh.exe 에러와 함께 생성되는 CMakeCache.txt 파일의 //Path 항목에
CMAKE_SH:FILEPATH=CMAKE_SH-NOTFOUND
를 추가하면 된다.5. 관련 문서
6. 외부 링크
- Make 공식 사이트
- Shake: 하스켈로 만든 빌드 도구
[1] 목적 파일 대신 소스 파일이 오면
-c
옵션이 자동으로 먼저 수행된 후 링킹을 한다.[2] 윈도우에서는 del /q /s 명령어로 대체.[3] MSDN - Visual Studio에서 C++ 플랫폼 간 프로젝트 만들기[4] 윈도우는 MSVC, macOS는 LLVM/Clang, 유닉스 및 리눅스는 GCC[5] cmake -G "MinGW Makefiles", cmake -G "Unix Makefiles", cmake -G "Xcode", cmake -G "Visual Studio 16 2019" -A x64 등등[6] 윈도우의 경우 'devenv XXX.sln /build (Debug 또는 Release)'