CMake를 사용하여 협업 환경에서 빌드 시스템을 구성할 때, 명료하고 유지 보수 가능한 코드를 작성하는 것이 매우 중요하다. 이는 다양한 개발자가 참여하는 프로젝트에서 일관성 있는 빌드 환경을 유지하고, 코드의 가독성을 높이며, 오류를 최소화하기 위한 것이다. 다음은 협업을 위한 CMake 코드 작성 시 고려해야 할 주요 원칙과 모범 사례들이다.

명확한 프로젝트 구조와 디렉토리 설정

디렉토리 구조의 명확성

협업 프로젝트에서는 디렉토리 구조를 명확하게 정의하는 것이 중요하다. src, include, lib, tests 등의 디렉토리를 사용하여 소스 코드, 헤더 파일, 라이브러리, 테스트 파일 등을 분리한다. 각 디렉토리에는 해당하는 CMakeLists.txt 파일을 두어, 각 부분의 빌드 설정을 분리하고 관리할 수 있도록 한다.

최상위 CMakeLists.txt의 역할

최상위 CMakeLists.txt 파일은 프로젝트의 전반적인 구조를 정의하고, 하위 디렉토리의 CMakeLists.txt 파일을 포함하는 역할을 한다. 예를 들어, add_subdirectory(src)와 같이 명령어를 사용하여 하위 디렉토리의 CMake 설정을 포함한다. 이는 프로젝트 구조를 일관되게 유지하며, 하위 모듈을 독립적으로 관리할 수 있게 한다.

변수와 상수의 사용

전역 변수의 최소화

CMake 스크립트에서 전역 변수의 사용을 최소화하는 것이 좋다. 전역 변수가 많을수록 코드의 복잡성이 증가하고, 의도하지 않은 변수 변경으로 인해 문제가 발생할 가능성이 높아진다. 대신, set 명령어를 통해 지역 변수를 사용하거나, target_compile_definitions와 같은 명령을 통해 각 타겟에 직접 변수를 설정하는 것이 바람직한다.

캐시 변수의 명확한 정의

CMake에서는 캐시 변수를 사용해 빌드 설정을 저장할 수 있다. 캐시 변수는 CACHE 키워드를 사용해 정의하며, 주로 옵션이나 경로를 설정할 때 사용된다. 예를 들어, set(MY_OPTION ON CACHE BOOL "Enable my option")와 같이 명시적으로 정의하고, 필요한 경우 FORCE 옵션을 사용해 값이 변경되도록 할 수 있다. 캐시 변수는 프로젝트의 일관성을 유지하는 데 중요하므로, 문서화와 주석을 통해 그 의미를 명확히 설명하는 것이 필요하다.

모듈화와 코드 재사용

함수와 매크로의 활용

CMake에서는 반복되는 코드를 줄이기 위해 함수와 매크로를 활용할 수 있다. 함수는 특정 작업을 수행하고, 매크로는 텍스트를 치환하는 역할을 한다. 예를 들어, 여러 타겟에 동일한 컴파일 옵션을 설정해야 할 경우, 이를 함수로 정의하여 중복 코드를 줄일 수 있다.

function(set_common_options target)
    target_compile_options(${target} PRIVATE -Wall -Wextra)
endfunction()

set_common_options(my_target)

위 예제에서는 set_common_options라는 함수를 정의하여, my_target에 공통 컴파일 옵션을 설정하는 데 사용 하였다.

모듈 분리와 include() 사용

공통된 설정이나 유틸리티 함수들은 별도의 모듈로 분리해 관리할 수 있다. 이를 위해 CMake에서는 include() 명령어를 사용하여 다른 CMake 파일을 포함할 수 있다. 예를 들어, cmake/Utils.cmake 파일에 유틸리티 함수를 정의하고, 이를 필요할 때마다 include(cmake/Utils.cmake)로 불러올 수 있다. 이는 코드의 재사용성을 높이고, 각 파일의 역할을 명확히 하는 데 유용하다.

종속성 관리와 외부 패키지 포함

find_package와 외부 라이브러리 관리

CMake에서는 외부 라이브러리나 패키지를 포함할 때 find_package 명령어를 사용한다. 이를 통해 프로젝트 간의 종속성을 관리하고, 필요한 라이브러리를 자동으로 검색할 수 있다. find_package 명령은 CMake의 모듈 경로에 존재하는 모듈 파일을 사용하여 라이브러리를 찾는다. 필요한 경우, 사용자 정의 모듈을 작성하여 특정 라이브러리를 찾는 방법을 정의할 수도 있다.

find_package(Boost REQUIRED COMPONENTS system filesystem)
target_link_libraries(my_target PRIVATE Boost::system Boost::filesystem)

이 예제에서는 Boost 라이브러리를 검색하고, 필요한 컴포넌트를 연결 하였다.

타겟 기반 빌드 설정

CMake의 타겟 기반 접근 방식은 협업 환경에서 특히 유용하다. 각 타겟(예: 실행 파일, 라이브러리)에 대해 독립적인 빌드 설정을 적용할 수 있으며, target_include_directories, target_compile_options, target_link_libraries 등을 사용하여 각 타겟의 속성을 관리할 수 있다. 이를 통해 타겟 간의 의존성을 명확히 하고, 특정 타겟에 대한 설정을 중앙에서 관리할 수 있다.

문서화와 주석

CMake 코드의 문서화

협업 프로젝트에서는 CMake 코드의 문서화가 매우 중요하다. 각 변수, 함수, 매크로의 목적과 사용 방법을 명확히 설명하는 주석을 추가해야 한다. 이는 새로운 팀원이 프로젝트에 참여할 때 도움을 주고, 코드 유지 보수 시 오류를 줄이는 데 기여한다.

set(PROJECT_VERSION "1.0.0")

function(set_common_options target)
    target_compile_options(${target} PRIVATE -Wall -Wextra)
endfunction()

CMakeLists.txt 파일의 구조화

CMakeLists.txt 파일을 작성할 때는 논리적인 순서에 따라 코드를 구조화하는 것이 중요하다. 예를 들어, 프로젝트 정보 정의, 타겟 정의, 빌드 옵션 설정, 라이브러리 연결 등의 순서로 코드를 작성하면 가독성이 높아진다. 또한, 각 섹션을 주석으로 구분하여 코드의 목적을 명확히 할 수 있다.


관련 자료: