본문 바로가기
CMake

[CMake] Tutorial (2) - Library 추가

by 별준 2021. 10. 28.

References

Contents

  • add_library()
  • option()
  • add_subdirectory()
  • target_link_libraries()

이번 글에서는 프로젝트에 라이브러리를 추가하여 사용하는 것에 대해서 살펴보겠습니다.

라이브러리는 특정 코드를 모아놓은 것이라고 보면 되는데, 예를 들어 c언어의 수학 관련 함수들을 모아놓은 <math.h> 를 생각하면 될 것 같습니다. 물론 실행파일에 다 때려넣어도 되지만, 그렇게 되면 코드가 변경될 때마다 전체를 컴파일해야하기 때문에, 라이브러리로 쪼개서 변경된 부분만 컴파일하여 속도가 빠르게 하는 것이 좋습니다.

 

그리고, 이번 예제부터는 결과는 리눅스 환경 기준으로 보여드리려고합니다. 비주얼 스튜디오를 실행해서 빌드하는 것 말고는 큰 차이는 없으니, 윈도우 환경이라면 make가 아닌 비주얼 스튜디오를 실행하여 빌드 및 실행하시면 됩니다 !

 

먼저 tutorial/step3의 코드를 준비합니다. 다만 이번 글에서 사용할 CMakeLists.txt는 Step3과 조금 다르므로 코드만 가져오도록 합니다 !

https://github.com/Kitware/CMake/tree/master/Help/guide/tutorial/Step3

 

GitHub - Kitware/CMake: Mirror of CMake upstream repository

Mirror of CMake upstream repository. Contribute to Kitware/CMake development by creating an account on GitHub.

github.com

 

이번 예제에서 사용되는 라이브러리는 MathFunctions이며, 프로젝트 디렉토리의 서브 디렉토리에 존재합니다.

이 MathFunctions 디렉토리에는 header file인 MathFunctions.h와 source file인 mysqrt.cxx가 있으며, sqrt 함수와 같은 기능을 하는 mysqrt 함수가 선언 및 정의되어 있습니다.

그리고, 서브 디렉토리에도 CMakeLists.txt가 있는데, 내용은 다음과 같습니다.

add_library(MathFunctions mysqrt.cxx)

 

  • add_library()

add_library 명령어는 라이브러리를 생성하는 역할을 합니다. 

따라서 위 명령은 mysqrt.cxx 소스 파일로 MathFunctions라는 라이브러리를 생성하도록 합니다. 


add_library는 아래의 폼으로 사용됩니다.

add_executable 명령어를 통해서 간단한 실행파일을 생성하는 것과 유사한데, targetName은 라이브러리를 참조하기 위해 CMakeLists.txt 내에서 사용되며, 기본적으로 이 이름으로 라이브러리 파일이 생성됩니다.

 

STATIC / SHARED / MODULE

라이브러리를 생성할 때 생성할 라이브러리의 타입입니다.

 

STATIC은 정적 라이브러리을 뜻하며, 윈도우에서는 빌드시 이 라이브러리는 targetName.lib로 생성되고 리눅스에서는 libtargetName.a로 생성됩니다.

SHARED는 shared 또는 dynamically linked library(dll)로 라이브러리를 생성합니다. 윈도우에서 기본 라이브러리 이름은 tagetName.dll이 되고, 리눅스에서는 libratgetName.so 가 됩니다.

MODULE 라이브러리는 다른 타겟에 link되지는 않지만, dlopen과 같은 기능을 사용하여 런타임에 동적으로 load가 될 수 있는 플러그인을 뜻합니다.

 

이 타입 키워드는 생략이 가능합니다. 이 키워드가 명시적으로 지정되지 않은 경우에는 'BUILD_SHARED_LIBS' 변수의 현재 값이 ON인지 여부에 따라 STATIC 또는 SHARED로 지정됩니다. 이 변수의 값은 cmake 커맨드를 실행할 때 -D옵션으로 설정가능 합니다.

$ cmake -DBUILD_SHARED_LIBS=YES ..

 

EXCLUDE_FROM_ALL

이 옵션은 기본 빌드 대상(ALL)에서 제외시키는 역할을 합니다. 일반적으로 빌드 시 대상을 지정하지 않으면(ALL_BUILD) 기본적으로 모든 타겟이 빌드됩니다. 하지만 이 옵션을 추가하면 기본 ALL 빌드 대상에 포함되지 않습니다. 따라서 이 옵션이 추가된 타겟을 빌드하려면, 빌드 커맨드를 통해 명시적으로 해당 타겟을 빌드시키거나, 기본 ALL 빌드에 포함된 타겟에 종속적인 경우에만 빌드됩니다.


이렇게 서브 디렉토리에서 생성된 라이브러리를 사용하기 위해서는 프로젝트 최상위에 존재하는 CMakeLists.txt에서 서브 디렉토리를 추가해주어야 합니다. 이는 add_subdirectory() 명령어를 통해서 추가할 수 있습니다.

 

차근차근 최상위 CMakeLists.txt를 살펴보겠습니다. (CMake github에서 제공하는 것과 아주 조금 다르니 유의하시기 바랍니다.)

cmake_minimum_required(VERSION 3.10)

# set the project name and version
project(Tutorial VERSION 1.0)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)

# add the MathFunctions library
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
endif()

# add the executable
add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC MathFunctions)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}/MathFunctions"
                          )

7~8 line은 사용될 C++ 버전을 명시해주는 역할을 합니다. (현재 크게 중요하진 않으니 넘어가도록 하겠습니다.)

 

  • option()

11 line에서 option 명령어를 통해 USE_MYMATH라는 옵션 변수의 값을 ON으로 세팅했습니다. 이 명령어는 옵션 변수를 ON 또는 OFF로 선택하는데, 만약 값을 넣지 않으면 OFF로 설정됩니다.

option 명령은 설정되는 변수값은 해당 변수가 이미 설정되어 있거나, cache variable로 존재한다면, 이 option 명령은 아무것도 하지 않습니다.(CMP0077 참조)

이 값은 build/CMakeCache.txt에서 확인할 수도 있습니다.

 

  • configure_file()

다음으로 이전 게시글에서 봤던 configure_file 명령어를 통해 헤더 파일을 복사해줍니다. 소스가 되는 TutorialConfig.h.in 파일에 아래처럼 마지막 줄이 추가되었습니다.

이전 게시글에서 복사되는 파일에서 CMake 변수값 양끝에 '@'를 추가하면 변수값으로 대체된다고 했습니다.

다른 용법으로는 '#cmakedefine VAR'을 사용하는 방법이 있습니다. 이 방법을 사용하면, 해당 부분은 '#define VAR'로 대체되어서 복사된 파일에 적용됩니다. 

따라서 cmake 후에 생성된 TutorialConfig.h 파일을 살펴보면 다음과 같습니다.

USE_MYMATH 옵션을 ON으로 설정했을 때

만약 해당 VAR(CMake 변수)가 존재하지 않거나 OFF라면, 주석으로 /* undef VAR */이 적히게 됩니다.

USE_MYMATH 옵션을 OFF로 설정했을 때

 

  • add_subdirectory()

add_subdirectory는 프로젝트를 빌드할 때 다른 서브 디렉토리를 가져오는 명령어입니다. 이 서브 디렉토리에는 반드시 그 서브 디렉토리의 CMakeLists.txt 파일이 존재해야하며, add_subdirectory로 서브 디렉토리가 추가된 순간 이 서브 디렉토리의 CMakeLists.txt가 실행됩니다.

우리는 위에서 MathFunctions 라는 폴더에 CMakeLists.txt를 작성했습니다. 바로 MathFunctions라는 라이브러리를 추가하는 명령어를 입력해두었는데, 이 add_subdirectory 명령어를 만나서 MathFunctions 디렉토리의 CMakeLists.txt가 실행되어서 이 CMakeLists.txt의 add_library 명령어가 실행되는 것입니다.

 

 

그 다음으로 add_excutable 명령어를 통해 실행파일 Tutorial을 생성해줍니다.

그리고 실행파일인 Tutorial에서 MathFunctions 라이브러리를 사용하기 위해서는 MathFunctions을 Tutorial에 link 시켜주어야 합니다.

  • target_link_libraries()

라이브러리를 링크시키기 위해서는 target_link_libraries 명령어를 사용합니다.

라이브러리를 링크할 때, 링크 타입 PRIVATE / PUBLIC / INTERFACE 를 선택할 수 있습니다.

이번 예제에서는 MathFunctions 라이브러리를 실행파일에 링크하는 것이기 때문에 링크 타입이 중요하지는 않습니다.

간단하게 설명드리면,

만약 라이브러리 A에 라이브러리 B를 링크할 때,

  • 라이브러리 B를 라이브러리 A의 내부 구현에만 사용될 때 PRIVATE 를 사용
  • 라이브러리 A가 내부적으로 라이브러리 B를 사용할 뿐만 아니라, 라이브러리 A를 참조하는 다른 라이브러리(or 인터페이스)에서도 라이브러리 B를 사용하도록 지정할 때 PUBLIC 사용. 즉, 라이브러리 A는 라이브러리 B가 없이는 사용할 수 없다는 것을 의미하며, 이는 A를 사용하는 모든 것이 B에 직접적인 디펜던시를 갖습니다.
  • INTERFACE는 라이브러리 A를 사용하기 위해서 라이브러리 B의 일부도 사용해야한다는 것을 의미합니다. 즉, 라이브러리 A에서 내부 구현에 라이브러리 B를 사용하지는 않지만, 헤더 파일(인터페이스)에서만 라이브러리 B를 사용한다는 것을 의미합니다.

(기회에 되면 조금 더 자세하게 알아보는 시간을 갖도록 하겠습니다 !)

 

 

마지막으로 target_include_directories 명령어를 통해서 TutorialConfig.h과 MathFunctions 라이브러리의 인터페이스를 담고 있는 MathFunctions.h를 include 하기 위해서 해당 경로들을 Tutorial의 include directory에 추가해줍니다. 


빌드 및 실행

프로젝트를 구성하는 CMakeList.txt의 내용들을 모두 살펴봤습니다.

그럼 이제 실제로 빌드 파일이 잘 생성되고, 의도한 대로 동작하는지 살펴보겠습니다.

cmake를 실행하고, 생성된 파일은 다음과 같습니다.

라이브러리로 추가된 서브 디렉토리가 build 디렉토리의 하위에 생성되었습니다.

빌드를 진행해보면,

MathFunctions 라이브러리를 생성할 때, 타입을 명시하지 않았기 때문에 STATIC으로 설정되어 'libMathFunctions.a'가 생성되고, MathFunctions가 정상적으로 빌드가 된 것을 확인할 수 있습니다.

build 디렉토리의 하위 디렉토리인 MathFunctions 디렉토리의 안을 살펴보면, libMathFunctions.a가 있는 것을 확인할 수 있습니다. 만약 윈도우에서 빌드했다면, build - MathFunctions - Debug(or Release) 에 MathFunctions.lib 파일이 생성되었을 것입니다.

 

생성된 실행 파일 Tutorial을 실행해보면, 다음의 결과를 얻을 수 있습니다.


+) USE_MYMATH option

아까 위에서 option 명령어를 사용할 때 정의하는 옵션 변수가 존재하면 option 커맨드는 동작하지 않는다고 했습니다. 

실제로 그런지 아래 명령어를 통해 체크해보도록 하겠습니다.

cmake .. -DUSE_MYMATH=OFF

생성된 TutorialConfig.h 에 USE_MYMATH가 define 되지 않은 것을 확인할 수 있습니다.

그리고 빌드를 하면,

링크 에러가 발생합니다.

 

이는 CMakeLists.txt(17 ~ 20 line)를 살펴보면 왜 그런지 알 수 있습니다.

# add the MathFunctions library
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
endif()

MathFunctions 라이브러리를 생성하기 위한 add_subdirectory 명령어가 USE_MYMATH 값이 ON일 때만 실행되기 때문입니다. 따라서 -D 옵션으로 USE_MYMATH 값을 OFF로 설정해버리면 add_subdirectory 명령어가 실행되지 않고 MathFunctions 라이브러리를 생성하지 않게 됩니다.

하지만 target_link_directories 명령어를 통해 실행파일(타겟) Tutorial은 존재하지 않는 MathFunctions를 링크하고 있기 때문에 링크 에러가 발생한 것입니다.


즉, 방금 사용한 CMakeLists.txt는 MathFunctions 라이브러리를 조건부로 사용할 수 있도록 고려되지 않았습니다.

cmake_minimum_required(VERSION 3.10)

# set the project name and version
project(Tutorial VERSION 1.0)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)

# add the MathFunctions library
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
endif()

# add the executable
add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC MathFunctions)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}/MathFunctions"
                          )

이는 USE_MYMATH를 통해서 조건부로 MathFunctions 라이브러리는 생성하거나 하지 않을 수 있지만(18 ~ 20 line), 나머지 부분(line 25 ~ 32)은 고려되지 않은 것입니다. 즉, target_link_libraries와 target_include_directories도 조건부로 설정되어야 합니다.

 

따라서, line 17부터를 아래처럼 변경하도록 하겠습니다.

if(USE_MYMATH)
  add_subdirectory(MathFunctions)
  list(APPEND EXTRA_LIBS MathFunctions)
  list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

# add the executable
add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           ${EXTRA_INCLUDES}
                           )

조건부로 include directory와 link target을 설정할 수 있도록, list 변수를 사용하였습니다.

line 3 ~ 4에서 list 변수 EXTRA_LIBS와 EXTRA_INCLUDES를 추가해서 라이브러리 이름과 include directory를 값으로 설정한 것입니다. 따라서, EXTRA_LIBS와 EXTRA_INCLUDES는 USE_MYMATH가 ON이라면 적절한 값이 추가되고, 그렇지 않다면 값이 추가되지 않습니다. (빈 list가 되는 것입니다.)

 

이제 target_link_libraries에는 EXTRA_LIBS를 링크하도록 하고, target_include_directories에는 EXTRA_INCLUDES를 추가하도록 하면, USE_MYMATH에 따라서 적절하게 링크 및 디렉토리 추가가 가능하게 됩니다.

다시 USE_MYMATH를 ON으로 설정하고 빌드하면,

MathFunctions 라이브러리를 사용하는 Tutorial을 빌드할 수 있습니다.

 

+) 추가로 USE_MYMATH를 OFF로 cmake, 빌드 한 후에 다시 아무런 옵션없이 cmake를 실행하면 option 명령어 때문에 USE_MYMATH가 ON으로 설정될 것 같지만 그렇지 않습니다. 이는 CMakeCache.txt에 USE_MYMATH 값이 그대로 남아 있기 때문입니다.

 

'CMake' 카테고리의 다른 글

[CMake] Flow Control - if  (0) 2021.10.30
[CMake] Lists  (0) 2021.10.29
[CMake] String Handling (string 명령어)  (0) 2021.10.29
[CMake] Variable (변수)  (0) 2021.10.29
[CMake] Tutorial (1) - Start CMake  (0) 2021.10.28

댓글