정적 라이브러리, 동적 라이브러리
- 라이브러리는 정적 라이브러리(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 작성시 동적 라이브러리와 정적 라이브러리를 둘다 생성할 수 있도록 제공하는것이 일반적이다