예전 블로그에서 옮겨온 글입니다. 지금은 두 번째 회사를 다닌지 2년이 다 되어가네요... 감개무량
회사를 다닌지 6개월, 제일 많이 성장한 부분은 패키징, 또는 아키텍처 설계다.
Python은 무적의 import로 어떤 폴더에 있는 어떤 코드의 어떤 모듈, 어떤 클래스, 어떤 메소드도 불러올 수 있지만, C++은 그게 아니라서 공부 초반에 고생을 했던 기억이 있다.
사실 지금도 초반이다...ㅠ
최근 들어 가장 고생한건, CMake를 여러 계층으로 패키징된 라이브러리에 적용하는 것이었다. GNU make를 쓸 때는 폴더 이름을 그냥 쓰면 되는 것이었는데, 도대체 튜토리얼들에서 얘기하듯이 그냥 CMakeLists.txt
를 폴더에 만들어주기만 하는 것만으로는 빌드가 되지 않는 것이다;;
이 문제점을 해결하기 위해 내가 BioCpp 레포지토리에 작성한 CMakeLists.txt
구조를 소개해볼까 한다.
디렉토리 구조 예시
일단 BioCpp
의 디렉토리 구조는 이렇게 생겨먹었다.
.
├─CMakeLists.txt <- (0) Main CMakeLists.txt
├─data
│ .gitkeep
├─inc
│ └─biocpp
│ algorithms.h
│ biocpp.h
│ io.h
│ molecules.h
├─src
│ │ CMakeLists.txt <- (1) 1st Subdirectory CMakeLists.txt
│ ├─algorithms
│ │ alignment.cc
│ │ CMakeLists.txt <- (2) 2nd Subdirectory CMakeLists.txt
│ ├─io
│ │ CMakeLists.txt
│ │ files.cc
│ └─molecules
│ CMakeLists.txt
│ mol_seq.cc
└─tests
│ CMakeLists.txt
└─molecules
CMakeLists.txt
mol_seq_unittest.cc
여기서 data
는 여러 상황에서 필요한 rawdata를 넣기 위해 .gitkeep
으로 폴더만 유지하고 있고, inc/biocpp
에 헤더들이, src
에 한 층의 폴더가 있고, 그 안에 소스코드들이, 그리고 tests
폴더에 테스트가 src
와 같은 구조로 있는 형식이다.
디렉토리에 있는 CMakeLists.txt
를 보면, 가장 바깥 폴더와, src
와 tests
등 여러 *.cc
소스 파일들이 있는 모든 폴더에 CMakeLists.txt
를 만들어준 걸 볼 수 있다.
이 때, (0)과 (1), (2)로 표시해둔 CMakeLists.txt
의 예시를 보자. 단, 거꾸로 보자.
(2) 2nd Subdirectory CMakeLists.txt
project(algorithms)
file(GLOB SOURCES *.cc)
add_library(${PROJECT_NAME} OBJECT ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/inc)
가장 안쪽의 CMakeLists.txt
는 그 폴더에 있는 소스파일들로 라이브러리를 만드는 역할을 한다. 명령어를 설명하자면 다음과 같다.
project(프로젝트명 또는 라이브러리명)
: CMake에서는 하나의CMakeLists.txt
가 하나의 프로젝트를 만든다고 보면 된다. 여기서는algorithms
라는 폴더의 소스들을 빌드하므로, 폴더와 동일한 이름의 프로젝트 이름을 지정해주었다. 폴더와 다른 이름이더라도, 잘만 정해두고 후술한 곳에서 사용만 잘 하면 된다.file(GLOB SOURCES *.cc)
:SOURCES
란 변수에*.cc
확장자를 갖는 모든 폴더 내 소스파일들을 저장해준다.add_library(라이브러리명 OBJECT 소스파일명)
: 라이브러리를 소스파일들로 빌드해준다. 이 때${PROJECT_NAME}
과${SOURCES}
를 써주면 위에서 지정한 프로젝트명의 라이브러리를 위에서 지정한대로 해당 폴더의 소스파일들로 빌드해주게 한 것이다.OBJECT
라 함은 후술할 mainCMakeLists.txt
에서 이 라이브러리를 object로 삼아 빌드를 한 번 더 할 것임을 예고해준 것이다.target_include_directories(라이브러리명 PUBLIC 헤더폴더위치)
: 라이브러리 빌드에 필요한 헤더파일들의 폴더를 지정해준다. 헤더 폴더 위치에 쓰인 ${CMAKE_SOURCE_DIR}는 가장 바깥 폴더의 (0)CMakeLists.txt
가 있는 위치이다.
(1) 1st Subdirectory CMakeLists.txt
add_subdirectory(algorithms)
add_subdirectory(io)
add_subdirectory(molecules)
중간 폴더의 CMakeLists.txt
는 여러 subdirectory로 통하는 관문 역할을 한다. 이 폴더에는 소스파일이 따로 없어서 add_library()
는 필요하지 않다.
add_subdirectory(폴더명)
: 빌드에 포함시킬 폴더들을 연결해준다. 라이브러리명이 아닌 폴더명임에 주의하자.
(0) Main CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(
biocpp
VERSION 0.1.0
DESCRIPTION "Bioinformatics Library for C++"
HOMEPAGE_URL https://github.com/panda5176/biocpp
LANGUAGES CXX
)
add_subdirectory(src)
add_subdirectory(tests)
set(LIBRARIES algorithms io molecules)
foreach(LIBRARY ${LIBRARIES})
set(LIB_OBJS ${LIB_OBJS} $<TARGET_OBJECTS:${LIBRARY}>)
endforeach()
add_library(${PROJECT_NAME} STATIC ${LIB_OBJS})
# 또는 add_executable(${PROJECT_NAME} STATIC biocpp.cc ${LIB_OBJS})
가장 바깥 폴더에 있는 CMakeLists.txt
에서는 필요한 메타데이터를 나열하고, 여태까지 빌드했던 라이브러리들을 모아서 최종 라이브러리 또는 실행파일을 만든다.
cmake_minimum_required(VERSION 버전)
: CMake를 위한 최소 버전을 명시해준다. 나의 경우3.0
이상이면 돌아가는 걸로 기억하는데, 다른 프로젝트 등에서 자주 사용하는target_include_directories()
명령어가3.12
를 요구해서 나도 저렇게 써둔 것 같다.아님말고project(프로젝트명 버전 설명 URL주소 언어)
: 위에서도 나왔지만, 추가적인 파라미터들을 mainCMakeLists.txt
에 써주면 좋다. 다들 별로 쓸모없는 것 같지만, CMake 공식문서를 보면 얘네를 변수로 써먹기도 한다(!).add_subdirectory(폴더명)
: 역시 폴더명을 써준다.set(변수명 이름1 이름2 이름3 ...)
: 이름들을 변수명에 통째로 저장해준다. 여기서는OBJECT
로 만들어준 라이브러리들을 저장했다.foreach(변수명 반복하고싶은변수명)
...endforeach()
: 반복문이다. 반복하고싶은 변수명들을 돌면서 변수명에 저장해서 뱉어준다.$<TARGET_OBJECTS:${변수명}>
:TARGET_OBJECTS
는 앞서OBJECT
로 설정해준 라이브러리들을 하나씩 가져와서LIB_OBJS
에 차례차례 저장해주고 있다. 단순히LIBRARIES
를add_library()
에 넣어주면 그 변수들은 라이브러리로 인식되는게 아니라서,OBJECT
임을 이런 방식으로 밝혀주어야한다.add_library(프로젝트명 STATIC OBJECT라이브러리들)
:OBJECT
로 지정한 라이브러리들을 섞어서 최종 라이브러리를 만든다. 만들어진 라이브러리는 빌드한 폴더의lib프로젝트명.a
로 저장된다.add_executable(프로젝트명 STATIC main소스코드 OBJECT라이브러리들)
: 또는main()
함수가 있는 소스코드와 라이브러리들을 이용해 실행가능한 파일, 즉*.exe
등을 만들 수 있다.
완성한 lib프로젝트명.a
는 빌드 대상 폴더에서 가져와서 다음과 같이 사용할 수 있다.
$ cmake CMakeLists.txt -B {빌드할 폴더} -G {make generator, 나의 경우 "MinGW Makefiles"}
$ make -C {빌드한 폴더}
$ g++ -o biocpp.test.exe biocpp.test.cc -I inc -L build -l biocpp
build
폴더에 있는 libbiocpp.a
라이브러리 파일과 inc
폴더의 헤더들을 이용해 biocpp.test.cc
소스파일을 컴파일하여 biocpp.test.exe
실행파일을 만든다고 보면 된다.
혹시 GNU make의 MakeFile을 직접 짜보았다면, 결과적으로는 이게 더 단순한 코드임을 알 수 있다. 고생한만큼 편리한 보상이 따를 것이다... 몇 안되는 국내 C++ 사용자 화이팅! 게임업계는 C++ 많이 쓰지만 아무튼 거기도 화이팅!ㅋㅋㅋ
References
'개발 > C++' 카테고리의 다른 글
OpenGL & C++ - 셰이더 컴파일에러 잡다가 CS 공부 왕창한 썰 (0) | 2022.09.13 |
---|
댓글