본문 바로가기
C & C++

[C/C++] 개발 환경 구성하기 02 (cmake, libft, google test)

by yhames 2024. 7. 22.
728x90

[C/C++] 개발 환경 구성하기 01 (아키텍처 설계, 브랜치 전략)

[C/C++] 개발 환경 구성하기 02 (cmake, libft, google test)

[C/C++] 개발 환경 구성하기 03 (gcovr, codecov)

[C/C++] 개발 환경 구성하기 04 (github action, norminette, codecov)

[C/C++] 개발 환경 구성하기 05 (miniLibX)

 

지난번에 이어서 개발 환경 세팅에 대한 내용입니다.
 
이번 글에서는 CMakeLists.txt를 작성했던 경험을 공유하려고 합니다.
 
저는 다음 4가지 부분으로 나눠서 진행했습니다.

  • 기본 프로젝트 설정
  • FetchContent 설정 (libft, googletest)
  • 실행 파일 빌드
  • 테스트 실행 파일 빌드

 

기본 프로젝트 설정

먼저 기본 프로젝트 설정입니다.

cmake_minimum_required(VERSION 3.25)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
add_compile_options(-Wall -Wextra -Werror)

set(NAME minirt)
project(${NAME} C CXX)

enable_testing()

 
위 코드는 순서대로 cmake 버전, C 버전, C++ 버전, 그리고 컴파일 옵션을 설정합니다. 저희는 cmake 3.25 버전을 사용했고, 현재 최신 버전은 cmake 공식 사이트에서 확인할 수 있습니다. 42서울에서는 C99를 사용하기 때문에 C99를 사용한다고 명시했고, C++은 google test를 컴파일하기 위해 사용하는 것이기 때문에 C++11를 사용했습니다. 42서울의 CPP_Module0X 과제와는 별개입니다. 또한 과제에서 -Wall -Wextra -Werror 플래그를 사용하도록 요구하기 때문에 컴파일 옵션을 추가했습니다.
 
다음으로 프로젝트명과 프로젝트에 C, C++을 사용한다고 명시합니다.
 
마지막으로 CTest를 사용한다는 것을 명시합니다. CTest는 google test나 unity 같은 다른 테스트 프레임워크와 함께 사용되며, cmake로 구성한 빌드환경에 빌드 및 테스트를 수행하는 도구입니다. VSCode에서 cmake tools를 설치하면 CTest 사용할 수 있습니다.
 


 
CTest를 사용하기 위해서는 VSCode 좌측 네비게이션바에 있는 Testing 탭에서 테스트를 실행하거나 하단의 Run CTest를 클릭하여 실행할 수 있고, Command + Shift + P를 누르고 CMake: Run Tests를 선택해도 동일하게 실행할 수 있습니다.
 

FetchContent 설정

다음으로 FetchContent를 통해 libftgoogle test를 빌드 시점에 가져오도록 설정하겠습니다.
 

include(FetchContent)

FetchContent_Declare(
	googletest
	GIT_REPOSITORY https://github.com/google/googletest.git
	GIT_TAG v1.14.x
)
FetchContent_MakeAvailable(googletest)

FetchContent_Declare(
	libft
	GIT_REPOSITORY https://github.com/shelldivers/libft.git
	GIT_TAG v1.0.0
)
FetchContent_MakeAvailable(libft)
set(CMAKE_LIBFT_SRC_DIR ${CMAKE_BINARY_DIR}/_deps/libft-src)

 
FetchContent를 통해 라이브러리를 가져오기 위해서는 해당 라이브러리가 배포되어 있어야합니다. google test의 경우에는 github에 이미 배포가 되어있기 때문에 해당 레포지토리에서 가져오면 됩니다. 하지만 libft는 본인이 구현한 libft를 github에 배포해야합니다.
 

 
저희는 별도의 레포지토리에 libft를 배포하여 사용했습니다.

현재 public으로 열려있기 때문에 코드를 보셔도 되지만, 해당 코드를 무단으로 사용하거나 cheating을 위해 코드를 복사하는 경우 메모리 누수 등 여러 문제가 있을 수 있습니다.

 

cmake_minimum_required(VERSION 3.25)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)

set(NAME libft)
project(${NAME} C CXX)

file(GLOB_RECURSE SRC_FILES "srcs/*.c")

add_library(${NAME} STATIC ${SRC_FILES})

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/includes)

 
물론 libft 또한 CMakeLists.txt가 필요하며 위와 같이 간단하게 정적 라이브러리를 생성하도록 작성했습니다.
 

실행 파일 빌드

다음으로 실행 파일을 빌드하는 방법입니다.

set(LIB_NAME ${NAME})
set(EXEC_NAME minirt_exec)

# build minirt library
file(GLOB_RECURSE SRC_FILES "srcs/*.c")
add_library(${LIB_NAME} STATIC ${SRC_FILES})
target_link_libraries(${LIB_NAME} libft)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/includes ${CMAKE_LIBFT_SRC_DIR}/includes)

# build minirt executable
add_executable(${EXEC_NAME} "minirt.c")
target_link_libraries(${EXEC_NAME} ${LIB_NAME} libft)

 
실행파일을 만들기 전에 먼저 srcs 경로의 소스파일을 가져와 정적 라이브러리 형태로 컴파일 했습니다. gtest의 경우에는 별도의 main 함수가 존재하기 때문에 실행 파일의 main 함수와 분리해야합니다. 테스트 실행 파일을 빌드할 때 해당 정적 라이브러리를 사용해서 빌드하게 됩니다.
 
실행 파일은 main 함수가 정의되어 있는 minirt.c를 포함하여 컴파일하고, libminirtlibft 정적 라이브러리를 함께 빌드합니다.
 

테스트 실행 파일 빌드

마지막으로 테스트 실행 파일을 빌드하는 코드입니다.

set(LIB_NAME ${NAME})
set(TEST_NAME minirt_test)

# build minirt library
file(GLOB_RECURSE SRC_FILES "srcs/*.c")
add_library(${LIB_NAME} STATIC ${SRC_FILES})
target_link_libraries(${LIB_NAME} libft)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/includes ${CMAKE_LIBFT_SRC_DIR}/includes)

# build minirt test executable
file(GLOB_RECURSE TEST_SRC_FILES "test/*.cpp")
add_executable(${TEST_NAME} ${TEST_SRC_FILES})
target_link_libraries(${TEST_NAME} ${LIB_NAME} libft gtest_main)

# add tests
add_test(
	NAME ${TEST_NAME}
	COMMAND ${TEST_NAME}
)

 
minirt_exec에서 사용한 libftlibminirt 정적 라이브러리를 동일하게 사용합니다. 추가로 gtest의 main 함수가 포함되어있는 gtest_main 정적 라이브러리를 함께 컴파일 했습니다. 마지막으로 add_test() 설정을 통해 테스트 실행 파일을 CTest에 추가했습니다. add_test()에 테스트 실행 파일을 추가하면 위에서 설명한 것과 같이 CTest를 통해 테스트를 실행할 수 있습니다.
 

#include "gtest/gtest.h"

int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

 
gtest_main의 경우 main 함수가 포함된 gtest 입니다. gtest는 위와 같이 main 함수를 직접 정의해야하지만, 테스트 초기화 및 실행에 대해 추가적인 설정을 할 수 있습니다. gtest_main는 간단한 main() 함수를 포함하기 때문에 테스트를 위한 기본적인 설정만 필요한 경우 간편하게 사용할 수 있습니다.
 
저희는 일단 간편하게 gtest_main을 사용하기로 했습니다.
 

정리

다음은 전체 CMakeLists.txt 코드입니다. 

cmake_minimum_required(VERSION 3.25)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
add_compile_options(-Wall -Wextra -Werror)

set(NAME minirt)
project(${NAME} C CXX)

set(LIB_NAME ${NAME})
set(EXEC_NAME minirt_exec)
set(TEST_NAME minirt_test)

# CTest
enable_testing()

include(FetchContent)

FetchContent_Declare(
	googletest
	GIT_REPOSITORY https://github.com/google/googletest.git
	GIT_TAG v1.14.x
)
FetchContent_MakeAvailable(googletest)

FetchContent_Declare(
	libft
	GIT_REPOSITORY https://github.com/shelldivers/libft.git
	GIT_TAG v1.0.0
)
FetchContent_MakeAvailable(libft)
set(CMAKE_LIBFT_SRC_DIR ${CMAKE_BINARY_DIR}/_deps/libft-src)

# build minirt library
file(GLOB_RECURSE SRC_FILES "srcs/*.c")
add_library(${LIB_NAME} STATIC ${SRC_FILES})
target_link_libraries(${LIB_NAME} libft)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/includes ${CMAKE_LIBFT_SRC_DIR}/includes)

# build minirt executable
add_executable(${EXEC_NAME} "minirt.c")
target_link_libraries(${EXEC_NAME} ${LIB_NAME} libft)

# build minirt test executable
file(GLOB_RECURSE TEST_SRC_FILES "test/*.cpp")
add_executable(${TEST_NAME} ${TEST_SRC_FILES})
target_link_libraries(${TEST_NAME} ${LIB_NAME} libft gtest_main)

# add tests
add_test(
	NAME ${TEST_NAME}
	COMMAND ${TEST_NAME}
)

 
 
다음에는 gcovr를 사용하여 테스트 커버리지 레포트를 생성하는 것과 codecov를 활용하여 시각화하는 방법에 대해 공유하겠습니다.

반응형