CMake - 라이브러리 타입

mohadang·2022년 8월 13일
0

CMake

목록 보기
21/24
post-thumbnail

정적 라이브러리, 동적 라이브러리

  • 라이브러리는 정적 라이브러리(STATIC)와 동적 라이브러리(SHARED)가 존재한다.
  • cmake를 사용하여 정적 라이브러리와 동적 라이브러리를 만들 수 있다.

cmake의 configuration에 대한 추상화

cmake_minimum_required(VERSION 2.8)
project(foo)

add_library(foo foo.cpp)
add_executable(boo boo.cpp)
target_link_libraries(boo PUBLIC foo)  
# 기본으로 실행하면 "정적 라이브러리"가 생성될 것이다.
> cd library-examples
[library-examples]> rm -rf _builds
[library-examples]> cmake -Hcustom -B_builds
[library-examples]> cmake --build _builds
[library-examples]> ls _builds/libfoo.a _builds/boo
_builds/libfoo.a
_builds/boo

# 그리고 자유롭게 "동적 라이브러리로" 전환 가능
[library-examples]> rm -rf _builds
[library-examples]> cmake -Hcustom -B_builds -DBUILD_SHARED_LIBS=ON   # 동적 라이브러리
[library-examples]> cmake --build _builds
[library-examples]> ls _builds/libfoo.dylib _builds/boo
_builds/libfoo.dylib
_builds/boo  

# 번들
[library-examples]> rm -rf _builds
[library-examples]> cmake -Hcustom -B_builds -DCMAKE_MACOSX_BUNDLE=ON   # 번들
[library-examples]> cmake --build _builds
[library-examples]> ls -d _builds/libfoo.a _builds/boo.app
_builds/libfoo.a
_builds/boo.app  

# 번들과 동적 라이브러리 둘다
[library-examples]> rm -rf _builds
[library-examples]> cmake -Hcustom -B_builds -DCMAKE_MACOSX_BUNDLE=ON -DBUILD_SHARED_LIBS=ON
[library-examples]> cmake --build _builds
[library-examples]> ls -d _builds/libfoo.dylib _builds/boo.app
_builds/libfoo.dylib
_builds/boo.app    

라이브러리 타입 판별

  • add_library는 STATIC, SHARED, MODULE 파라미터를 사용 가능하다. 하지만 STATIC, SHARED, MODULE 중 하나를 사용 하는 순간 cmake는 한가지 타입에 대해서만 빌드 가능하도록 만들어 진다
add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [<source>...])
  • 정적, 동적 라이브러리를 같이 만들 수 있으려면 미리 선언된 BUILD_SHARED_LIBS을 사용해야 한다. BUILD_SHARED_LIBS의 기본값은 STATIC이다

정적 라이브러리와 동적 라이브러리를 분리된 디렉터리에 설치

  • 동적 라이브러리 빌드 & 설치
> cd library-examples

[library-examples]> cmake -H right-way -B _builds/shared \
-DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="`pwd`/_install/configuration-A"

[library-examples]> cmake --build _builds/shared --target install
Scanning dependencies of target foo
[ 50%] Building CXX object CMakeFiles/foo.dir/foo.cpp.o
[100%] Linking CXX shared library libfoo.so
[100%] Built target foo
Install the project...
-- Install configuration: ""
-- Installing: /.../library-examples/_install/configuration-A/lib/libfoo.so

DBUILD_SHARED_LIB 캐시 변수에 ON을 설정 하였다.
  • 정적 라이브러리 빌드 & 설치
[library-examples]> cmake -Hright-way -B_builds/static \
-DCMAKE_INSTALL_PREFIX="`pwd`/_install/configuration-B"

[library-examples]> cmake --build _builds/static --target install
Scanning dependencies of target foo
[ 50%] Building CXX object CMakeFiles/foo.dir/foo.cpp.o
[100%] Linking CXX static library libfoo.a
[100%] Built target foo
Install the project...
-- Install configuration: ""
-- Installing: /.../library-examples/_install/configuration-B/lib/libfoo.a

하나의 디렉터리에 설치

  • 라이브러리 파일 충돌 문제
    • 리눅스(libfoo.so and libfoo.a)와 MacOS(libfoo.dylib and libfoo.a)에서는 하나의 디렉터리에 설치가 가능
    • 그러나 윈도우는 불가능. 윈도우는 동적 라이브러리 생성할 때도 .lib를 생성하기 떄문에 정적 라이브러리에 대해 overwrite가 발생한다.
    • --target install : 빌드까지 성공하면 설치까지 진행
> cd library-examples
[library-examples]> rmdir _builds _install /S /Q
[library-examples]> cmake -H right-way -B _builds\static -G "Visual Studio 14 2015" \
-DCMAKE_INSTALL_PREFIX=%cd%\_install
[library-examples]> cmake --build _builds\static --config Release --target install
...
-- Install configuration: "Release"
-- Installing: C:/.../library-examples/_install/lib/foo.lib    # 정적 라이브러리

[library-examples]> cmake -H right-way -B _builds\shared -G "Visual Studio 14 2015" \
-DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=%cd%\_install
[library-examples]> cmake --build _builds\shared --config Release --target install
...
-- Install configuration: "Release"
-- Installing: C:/.../library-examples/_install/lib/foo.lib    # 정적 라이브러리 덮어 씌워짐
-- Installing: C:/.../library-examples/_install/bin/foo.dll
  • config 파일 레벨에서 충돌이 발생하지 않더라고 config 단계에서 충돌이 발생 한다.
  • 동적 라이브러리 빌드 과정에서 barTargets-release.cmake 스크립트 생성됨
> cd library-examples
[library-examples]> rm -rf _install _builds
[library-examples]> cmake -Hbar -B_builds/shared -DBUILD_SHARED_LIBS=ON \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="`pwd`/_install"
[library-examples]> cmake --build _builds/shared --target install
[library-examples]> grep lib/libbar.so -IR _install
_install/lib/cmake/bar/barTargets-release.cmake:  \    # 생성
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libbar.so"
_install/lib/cmake/bar/barTargets-release.cmake:list \    # 생성
(APPEND _IMPORT_CHECK_FILES_FOR_bar::bar "${_IMPORT_PREFIX}/lib/libbar.so" )
Config for static variant will have the same barTargets-release.cmake name:
  • 정적 라이브러리 빌드 과정에서 barTargets-release.cmake 스크립트 또 생성됨, 이전에 생성된 동적 라이브러리와 중복됨
[library-examples]> cmake -Hbar -B_builds/static -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="`pwd`/_install"
[library-examples]> cmake --build _builds/static --target install
[library-examples]> grep lib/libbar.a -IR _install
_install/lib/cmake/bar/barTargets-release.cmake:  \    # 중복 생성, overwrite
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libbar.a"
_install/lib/cmake/bar/barTargets-release.cmake:list \    # 중복 생성, overwrite
(APPEND _IMPORT_CHECK_FILES_FOR_bar::bar "${_IMPORT_PREFIX}/lib/libbar.a" )
Now since configuration files for shared variant are overwritten there is no way to \
load libbar.so using find_package(bar CONFIG REQUIRED).
  • 충돌 회피를 위해서 이런식으로 스크립트 작성 가능하지만 권고하지 않는다.
option(FOO_STATIC_LIB "Build static library" ON)

if(FOO_STATIC_LIB)
  add_library(foo_static STATIC foo.cpp)
else()
  add_library(foo_shared SHARED foo.cpp)
endif()
  • 이 방식 보다는 add_library(foo foo.cpp) + BUILD_SHARED_LIBS 조합을 사용하는것이 더 좋은 방법이다.
# 이런 식으로 사용하면 안된다. 이런식으로 처리하면 빌드도 두번 해야한다.

# Don't do that!
add_library(foo_static STATIC foo.cpp)
add_library(foo_shared SHARED foo.cpp) 

PIC 충돌

# use_bar

cmake_minimum_required(VERSION 2.8)
project(use_bar)

# use_bar는 bar 라이브러리를 찾아서 사용 한다.
# bar 라이브러리는 정적 라이브러리이다.
find_package(bar CONFIG REQUIRED)    

# bar 정적 라이브러리 사용, cmake 실행시 문제 없음
add_library(use_bar_static STATIC use_bar.cpp)  
target_link_libraries(use_bar_static PUBLIC bar::bar)

# bar 동적 라이브러리 사용, cmake 실행시 문제 발생
add_library(use_bar_shared SHARED use_bar.cpp)
target_link_libraries(use_bar_shared PUBLIC bar::bar)
# bar 라이브러리를 빌드 한다. bar는 static 라이브러리이다.
[library-examples]> rm -rf _builds _install
[library-examples]> cmake -H bar -B _builds -DCMAKE_INSTALL_PREFIX="`pwd`/_install"
[library-examples]> cmake --build _builds --target install

# use_bar를 빌드한다.
[library-examples]> rm -rf _builds
[library-examples]> cmake -H use_bar -B _builds -DCMAKE_PREFIX_PATH="`pwd`/_install"
[library-examples]> cmake --build _builds
Scanning dependencies of target use_bar_shared
[ 25%] Building CXX object CMakeFiles/use_bar_shared.dir/use_bar.cpp.o
[ 50%] Linking CXX shared library libuse_bar_shared.so
/usr/bin/ld: /.../library-examples/_install/lib/libbar.a(bar.cpp.o):
    relocation R_X86_64_PC32 against symbol `_Z4bar1v' can not be used when   # 라이브러리 충돌
    making a shared object; recompile with -fPIC  

링크 참조 중복 문제

  • 한 프로젝트에 유형만 다른 같은 라이브러리를 생성하면 링크 참조 중복 문제가 발생할 수 있다.
# bar/CMakeLists.txt

# 2 개의 라이브러리를 추가
# 이런 방식으로 사용하면 링크 참조 중복 문제가 생겨서 어떤 것을 참조할지 모른다
add_library(bar_static STATIC bar.cpp) # bar_static : 정적 라이브러리
add_library(bar_shared SHARED bar.cpp) # bar_shared : 동적 라이브러리
# baz/CMakeLists.txt

add_library(baz SHARED baz.cpp)   # 어떤 이유로 baz를 동적 라이브러리로 선언
target_link_libraries(baz PUBLIC bar_shared)    # baz는 bar_shared를 참조
# CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(foo)

add_subdirectory(bar)   # bar 프로젝트 추가
add_subdirectory(baz)   # bar 프로젝트 추가

add_executable(foo foo.cpp)
target_link_libraries(foo PUBLIC bar_static baz)    # bar_static, baz(동적) 링크
# 빌드 후 어떤 라이브러리를 참조 하는지 확인

[library-examples]> rm -rf _builds
[library-examples]> cmake -Hdup -B_builds
[library-examples]> cmake --build _builds

#[[ 동적 baz와 동적 bar 라이브러리가 연결되어 있다.
  > ldd _builds/foo
  ...
  libbaz.so => /.../library-examples/_builds/baz/libbaz.so (0x00007f6d2f2a4000)
  libbar_shared.so => /.../library-examples/_builds/bar/libbar_shared.so (0x00007f6d2e927000    
  
  bar_static 링크를 참조하도록 하였지만 bar_shared가 참조되었다
]]

권고

  • 라이브러리가 다른 유형을 가지지 않도록 설계할 경우에만 STATIC/SHARED/MODULE을 사용하십시오. 하지만 그 이외에 일반적인 경우라면 사용하면 안된다.
  • 정적과 동적 빌드 모두를 지원하기 위해 STATIC/SHARED/MODULE 중 어떤것도 지정하면 안됨, 그렇지 않을 경우 수많은 문제 발생 가능할 수 있음
    • 파일 덮어쓰기
    • config시 스크립트 덮어쓰기 문제
    • 라이브러리 참조 중복 문제
  • BUILD_SHARED_LIBS를 이용해서 정적과 동적라이브러리 지원 가능하도록 설계 해야함
  • 정적 라이브러리와 동적 라이브러리 설치 디렉터리를 분리 해야함
  • CMake 작성시 동적 라이브러리와 정적 라이브러리를 둘다 생성할 수 있도록 제공하는것이 일반적이다
profile
mohadang

0개의 댓글