Notice
Recent Posts
Recent Comments
Link
관리 메뉴

nomad-programmer

[Development/CMake] CMake 본문

Development/CMake

[Development/CMake] CMake

scii 2024. 9. 22. 01:19

CMake란, Make의 빌드 관리 시스템을 만들기 위한 오픈소스 프로젝트이다.

CMake는 Makfile 자동 생성 및 관리를 해준다. 지정한 운영체제에 맞는 Makefile을 생성해줘서 소스코드 빌드를 편리하게 할 수 있다. 또한 의존성 정보를 일일이 기술해주지 않아도 되어서 빌드 스크립트 관리가 효율적이다.

Make가 무엇인지 궁금하다면 아래의 링크를 통해 확인할 수 있다.

2024.09.21 - [Development/CMake] - [Development/CMake] Make & Makefile

 

[Development/CMake] Make & Makefile

시작 전, 용어 정리컴파일 (Compile)소스 코드 -> 어셈블리어소스 코드 파일(.cpp)을 목적 파일(.o)로 변환링킹 (Linking)서로 다른 파일에 있는 소스 코드를 한데 묶어 하나의 실행 파일로 만듦Makemake는

nomad-programmer.tistory.com

CMake는 Makefile을 만들기 위한 tool이다. 아래의 그림을 보자.

cmake

CMakeLists.txt파일을 cmake 명령으로 실행하면, Makefile이 생성된다. 생성된 Makefile을 make 명령으로 실행하면 실행파일을 만들어지는 구조이다.

$ cmake CMakeLists.txt

위의 명령을 실행하면 다음과 같은 파일들이 생성된다.

  • Makefile
  • CMakeCache.txt
  • cmake_install.cmake
  • CMakeFiles <디렉토리>
    • 이곳에 목적 파일들이 만들어 짐

cmake 명령은 소스파일을 추가하지 않는 이상 최초로 한번만 실행하면 된다. 만약, 소스파일이 수정됐을 경우 make 명령으로 빌드만 하면 된다.

  • 소스 파일이 추가 된 경우
    • cmake 명령 실행
  • 소스 파일의 수정만 된 경우
    • make 명령으로 빌드만 다시 실행

CMakeLists.txt 작성

  • PROJECT ( )
    • 프로젝트 이름
  • CMAKE_MINIMUIM_REQUIRED ( VERSION <버전> )
    • 빌드에 필요한 cmake 최소 버전
  • SET
    • SET (<변수 이름> <값>)
    • SET (<목록 변수이름> <항목> <항목> ...)
      • ex) SET (SRC_FILES main.cpp A.cpp B.cpp)
    • SET ( CMAKE_CXX_STANDARD <버전> )
      • C++ 표준 버전 설정
    • SET ( RUNTIME_OUTPUT_DIRECTORY 디렉토리1 )
      • 빌드된 실행 바이너리를 저장할 디렉토리 지정
  • INCLUDE_DIRECTORIES ( 디렉토리1, 디렉토리2, ... )
    • 헤더 파일 경로 포함 (컴파일러가 찾을 수 있도록 경로 설정)
  • ADD_COMPILE_OPTIONS ( 옵션1, 옵션2, ... )
    • 소스파일 컴파일 시 추가할 옵션
      • ex) ADD_COMPILE_OPTIONS ( -g -Wall -O2 )
  • ADD_EXECUTABLE ( <실행 파일 이름> <소스 파일> ... )
    • 빌드 최종 결과물 생성 (binary 생성)
      • ex) ADD_EXECUTABLE (test.out main.cpp A.cpp B.cpp)
      • ex) ADD_EXECUTABLE (test.out ${SRC_FILES})
  • ADD_LIBRARY ( <라이브러리 이름> [STATIC|SHARED|MODULE] <소스파일> )
    • 빌드 최종 결과물로 라이브러리 생성
  • ADD_SUBDIRECTORY ( <dir> [binary_dir] [EXCLUDE_FROM_ALL] )
    • 서브 디렉토리를 추가하여 해당 디렉토리 안의 CMakeLists.txt 파일을 호출하고 빌드 시스템에 통합할 때 사용
    • 즉, 다중 디렉토리로 이루어진 프로젝트에서 모듈화된 빌드 지원
    • add_subdirectory는 각 디렉토리마다 독립적인 CMakeLists.txt 파일을 정의하고, 이를 상위 프로젝트에서 통합할 때 사용
      • 여러 팀이 개발하는 큰 프로젝트에서 자주 사용 됨
    • <dir>
      • 서브 디렉토리의 경로 (이 경로에 CMakeLists.txt 파일이 존재해야 함)
    • [binary_dir]
      • 빌드 아티팩트가 생성될 디렉토리 경로 (옵션)
    • [EXCLUDE_FROM_ALL]
      • 이 플래그를 사용하면 make all 명령어를 실행할 때 해당 디렉토리의 타겟을 제외함
  • CMAKE_C_COMPILER
    • 컴파일 및 링크 과정에서 사용할 컴파일러의 경로 지정
      • ex) SET (CMAKE_C_COMPILER "gcc")
  • MESSAGE ( )
    • 콘솔에 메시지 출력
  • INSTALL ( )
    • make install 실행 시 할 동작 지정
  • TARGET_LINK_LIBRARIES ( <target> <item1> <item2> ...)
    • 컴파일 된 실행 파일이나 라이브러리에 외부 라이브러리 또는 다른 타겟을 링크할 때 사용 
    • <target>
      • 라이브러리를 링크할 대상(타겟), 실행 파일 또는 라이브러리 이름
    • <item1>, <item2>
      • 링크할 라이브러리나 타겟 이름들

그러면 Makefile로 작성했던 것을 CMakeLists.txt 파일로 작성해보자. 파일 구조는 다음과 같다.

파일 구조

기존에 작성했던 Makefile의 내용은 다음과 같다.

CXX = g++

# C++ 컴파일러 옵션
CXXFLAGS = -std=c++17 -Wall -O2

# 링커 옵션
LDFLAGS = 

# 헤더 파일 경로
INCLUDE = -Iinclude/

# 소스 파일 디렉토리
SRC_DIR = ./src

# 빌드 파일 디렉토리
BUILD_DIR = ./build

# 생성하고자 하는 실행 파일 이름
TARGET = main

# Make할 소스 파일들
# wildcard로 sRC_DIR에서 *.cpp로 된 파일들 목록을 가져온 뒤 그것을 notdir로 파일명만 추출
# ex) $(notdir $(wildcard $(SRC_DIR)/*.cpp)) -> main.cpp Base.cpp A.cpp B.cpp
SRCS = $(notdir $(wildcard $(SRC_DIR)/*.cpp))

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

# OBJS안의 object 파일들 이름 앞에 $(BUILD_DIR)/을 붙인다.
OBJECTS = $(patsubst %.o, $(BUILD_DIR)/%.o, $(OBJS))
DEPS = $(OBJECTS:.o=.d)

all: main

$(BUILD_DIR)/%.o : $(SRC_DIR)/%.cpp
	$(CXX) $(CXXFLAGS) $(INCLUDE) -c $< -o $@ -MD $(LDFLAGS)

$(TARGET) : $(OBJECTS)
	$(CXX) $(CXXFLAGS) $(OBJECTS) -o $(TARGET) $(LDFLAGS)

.PHONY: clean all
clean:
	rm -f $(OBJECTS) $(DEPS) $(TARGET)

-include $(DEPS)

이것을 CMakeLists.txt 파일로 작성하면 다음과 같다.

# 최소 cmake 버전 설정
cmake_minimum_required(VERSION 3.10)

# 프로젝트 이름 및 c++ 버전 설정
project(my_project VERSION 1.0.0 LANGUAGES CXX)

# c++17 표준 사용
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)

add_compile_options(-Wall -O2 -g)

# include 디렉토리 추가 (헤더 파일 경로)
include_directories(include)

# 소스 파일 목록 설정
set(SOURCES
    src/main.cpp
    src/A.cpp
    src/B.cpp
    src/Base.cpp
)

# 실행 파일 생성
add_executable(${PROJECT_NAME} ${SOURCES})

훨씬 간단해졌다. 그러면 CMakeList.txt 파일을 실행해보자.

# build 디렉토리 없으면 생성.
$ cd build 

# cmake 명령으로 Makefile 파일 생성
$ cmake ..

# make 명령으로 실행 파일 생성
$ make -j$(nproc)

cmake 와 make 명령으로 실행파일을 생성한 모습


외부 라이브러리 사용

규모가 큰 프로젝트를 작업할 때, 하나의 파일이 아닌 라이브러리들로 쪼개서 작업한다. 그 이유는 하나의 파일에서 작업했을 경우, 수정이 생기면 그 큰 소스코드를 다시 컴파일 해야 하는데 이러면 컴파일 시간이 너무 오래 걸려 개발 속도가 느려지기 때문이다. 따라서 라이브러리로 쪼개서 수정된 부분만 다시 컴파일 하는 구조로 프로젝트를 진행한다. 즉, 다음과 같은 구조로 진행된다.

라이브러리로 쪼개서 프로젝트를 관리하게되면 바뀐 부분만 컴파일하고 링킹하면 되므로 컴파일 시간이 비약적으로 빨라지며 개발 속도 또한 빨라진다. 그리고 테스트하기도 용이하다.

이러한 구조로 된 큰 프로젝트인 경우, make로만 관리하면 힘들 것이다. 이때 힘을 발휘하는 것이 바로 "cmake" 이다.

테스트를 위한 라이브러리 작성을 해보자.

// addition.h

#ifndef __ADDITION_H__
#define __ADDITION_H__

typedef double (*FuncPtr)(double, double);

double Add(double a, double b);
void ShowValues(FuncPtr func, double a, double b);

#endif // __ADDITION_H__
// addition.cpp

#include "addition.h"

#include <iostream>


double Add(double a, double b)
{
    return a + b;
}

void ShowValues(FuncPtr func, double a, double b)
{
    double result = func(a, b);
    std::cout<<std::endl;
    std::cout << "외부 라이브러리에서 호출한 함수" << std::endl;
    std::cout << "Result: " << result << std::endl;
}
// main.cpp

#include "A.h"
#include "B.h"
#include "Base.h"
#include "addition.h"

int main() {
  Base* a = new A_SPACE::AClass(1, 2);
  Base* b = new B_SPACE::BClass(3, 4);

  a->ShowValues();
  b->ShowValues();

  delete a;
  delete b;

  ShowValues(Add, 5, 6);

  return 0;
}

나머지 파일들의 내용은 아래의 링크로 가면 볼 수 있다.

2024.09.21 - [Development/CMake] - [Development/CMake] Make & Makefile

 

[Development/CMake] Make & Makefile

시작 전, 용어 정리컴파일 (Compile)소스 코드 -> 어셈블리어소스 코드 파일(.cpp)을 목적 파일(.o)로 변환링킹 (Linking)서로 다른 파일에 있는 소스 코드를 한데 묶어 하나의 실행 파일로 만듦Makemake는

nomad-programmer.tistory.com

현재 디렉토리 & 파일의 구조는 다음과 같다.

디렉토리&파일 구조

라이브러리의 CMakeLists.txt 파일의 내용은 아래와 같다.

cmake_minimum_required(VERSION 3.10)

project(AddFunc)

add_library(addition STATIC addition/src/addition.cpp)

target_include_directories(addition PUBLIC ${CMAKE_SOURCE_DIR}/lib/addition/include)

target_compile_options(addition PRIVATE -Wall -Werror)

최상위의 CMakeLists.txt 파일의 내용은 아래와 같다.

# 최소 cmake 버전 설정
cmake_minimum_required(VERSION 3.10)

# 프로젝트 이름 및 c++ 버전 설정
project(my_project VERSION 1.0.0 LANGUAGES CXX)

# lib 디렉토리 추가 (라이브러리 빌드)
# lib 디렉토리 내의 CMakeLists.txt 파일을 참조하여 addition 라이브러리를 빌드
add_subdirectory(lib)

# c++17 표준 사용
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)

add_compile_options(-Wall -O2 -g)

# include 디렉토리 추가 (헤더 파일 경로)
include_directories(include)

# 소스 파일 목록 설정
set(SOURCES
    src/main.cpp
    src/A.cpp
    src/B.cpp
    src/Base.cpp
)

# 실행 파일 생성
add_executable(${PROJECT_NAME} ${SOURCES})

# 라이브러리 링크   
target_link_libraries(${PROJECT_NAME} addition)

# 실행 파일이 생성될 디렉토리 설정
#set_target_properties(${PROJECT_NAME} PROPERTIES
#    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
#)

이제 실행파일을 생성해보자.

$ mkdir build
$ cd build
$ cmake ..
$ make

cmake & make 를 이용하여 실행파일을 만들어 실행한 모습

이처럼 cmake를 이용하면 외부 라이브러리도 쉽게 적용하여 컴파일하고 링킹하여 실행 파일을 생성할 수 있다.

'Development > CMake' 카테고리의 다른 글

[Development/CMake] Make & Makefile  (1) 2024.09.21
[DevOps/CMake] cmake 튜토리얼 사이트  (0) 2023.01.15
Comments