動態鏈接庫( libjthread 也是工程內的一個子項目)找不着,怎麼回事?
make install
安裝的時候,是把動態鏈接庫和執行文件都放在同一個目錄下的
在 CMP0042
更新,也就是Cmake 2.8.1.2之後,如果你聲明的 cmake_minium_required
爲2.8以上, MACOSX_RPATH
會默認啓動,這時候編譯的執行文件在查找鏈接庫的時候會往 @rpath
上搜索,所以就找不到要鏈接的庫( libadk.dylib
在同一個目錄下)。
商業程序如何加載自己的so庫
使用LD_LIBRARY_PATH的缺點是要實現設置LD_LIBRARY_PATH。
什麼是RPATH?
在Linux環境下,使用動態鏈接的程序在運行時會自動鏈接 ld.so
這個庫(OS X上是 dyld
),然後通過 ld.so
來查找鏈接其它的庫。而 RPATH
就是編譯的時候鏈接到執行文件的鏈接庫路徑。OS X在 RPATH
的設置上和Linux還是有點出入的,OS X的 RPATH
採用的是絕對路徑。
ld.so
搜索路徑的優先級是這樣的:
1. RPATH
,編譯鏈接時加入 -rpath
參數指明所謂的 RUNPATH
,這樣可執行文件(或者依賴其他動態鏈接庫的動態鏈接庫)就能告訴 ld.so
到哪裏去搜索對應的動態鏈接庫了。
2. LD_LIBRARY_PATH
,對於沒有設定 RPATH
的可執行文件或者動態鏈接庫,我們可以用 LD_LIBRARY_PATH
這個環境變量通知 ld.so
往哪裏查找鏈接庫。
3. /etc/ld.so.conf
,系統對 ld.so
的路徑配置文件。
4. /usr/lib
、 /lib
和 /usr/local/lib
,系統默認路徑。
Cmake和RPATH
在分發程序的時候,執行文件使用的鏈接庫在系統內不一定會有,或者自帶了的版本不對,一般都會在程序文件夾內都會附帶相應的鏈接庫,所以最好還是把 RPATH
加上。Cmake對RPATH提供了很多選項支持,我們一般只關注這幾個變量就好了: CMAKE_SKIP_BUILD_RPATH
、 CMAKE_BUILD_WITH_INSTALL_RPATH
、 CMAKE_INSTALL_RPATH
和 CMAKE_INSTALL_RPATH_USE_LINK_PATH
。
默認RPATH設置
set(CMAKE_SKIP_BUILD_RPATH FALSE) # 編譯時加上RPATH
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # 編譯時RPATH不使用安裝的RPATH
set(CMAKE_INSTALL_RPATH "") # 安裝RPATH爲空
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # 安裝的執行文件不加上RPATH
Cmake在默認情況下, make install
會把安裝的執行文件的 RPATH
刪掉的,所以就會出現上面我執行安裝好的執行文件報錯的問題。
加上完整的RPATH
Cmake的默認設置我們肯定是不能使用的,我們需要一個安裝的時候也要帶上 RPATH
的設置。
set(INSTALL_LIB_DIR "${PROJECT_BINARY_DIR}/lib") # 假設安裝目錄在編譯目錄的lib子目錄內
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# 確保鏈接庫不在系統默認安裝的目錄上時更改到項目lib上
list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES ${CMAKE_INSTALL_RPATH} isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${INSTALL_LIB_DIR}")
endif("${isSystemDir}" STREQUAL "-1")
一個cmake文件示例
# CMake要求的最低版本號
cmake_minimum_required( VERSION 2.8 )
# 項目名稱
project( ycm_support_libs )
# 設置客戶端lib和服務端lib的變量名稱
set( CLIENT_LIB "ycm_client_support" )
set( SERVER_LIB "ycm_core" )
# 設置Python的版本號變量
set( Python_ADDITIONAL_VERSIONS 2.7 2.6 )
# 進行Python包的查找,這裏是REQUIRED表示必須
find_package( PythonLibs 2.6 REQUIRED )
# 如果Python版本號低於3.0.0就進行FATAL_ERROR的出錯信息
if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" )
message( FATAL_ERROR
"CMake found python3 libs instead of python2 libs. YCM works only with "
"python2.\n" )
endif()
# 各種option
option( USE_DEV_FLAGS "Use compilation flags meant for YCM developers" OFF )
# 這個變量就是我上文講到的很關鍵的一個變量,來判斷當前用戶需要不需要libclang
option( USE_CLANG_COMPLETER "Use Clang semantic completer for C/C++/ObjC" OFF )
# install.sh中的--system-libclang就與這個變量進行交互
option( USE_SYSTEM_LIBCLANG "Set to ON to use the system libclang library" OFF )
# YCM作者推薦的用法,在這裏直接寫入Clang的相關路徑 注意這裏的CACHE PATH,表示當用戶如果命令行
# 進行指定,那優先會去讀用戶的命令行,而不是用這裏的set,並且把相關的值寫入Cache中
set( PATH_TO_LLVM_ROOT "" CACHE PATH "Path to the root of a LLVM+Clang binary distribution" )
# YCM作者推薦的另外一種安裝方法,直接將libclang.so全路徑寫死
set( EXTERNAL_LIBCLANG_PATH "" CACHE PATH "Path to the libclang library to use" )
# 如果你使用libclang但是沒有指定用不用系統的libclang,沒有指定llvm_root,沒有指定額外的libclang.so,那麼就會帶你去下載
if ( USE_CLANG_COMPLETER AND
NOT USE_SYSTEM_LIBCLANG AND
NOT PATH_TO_LLVM_ROOT AND
NOT EXTERNAL_LIBCLANG_PATH )
message( "Downloading Clang 3.4" )
# 這就是llvm官網的3.4下載路徑
set( CLANG_URL "http://llvm.org/releases/3.4" )
# 如果當前客戶端是蘋果 Mac OS X
if ( APPLE )
# 設置Clang的文件夾名稱
set( CLANG_DIRNAME "clang+llvm-3.4-x86_64-apple-darwin10.9" )
# 設置Clang的MD5校驗碼
set( CLANG_MD5 "4f43ea0e87090ae5e7bec12373ca4927" )
# 設置Clang文件名稱爲之後加上tar.gz
set( CLANG_FILENAME "${CLANG_DIRNAME}.tar.gz" )
else()
# 如果是64位平臺
if ( 64_BIT_PLATFORM )
# 設置Clang的文件夾名稱
set( CLANG_DIRNAME "clang+llvm-3.4-x86_64-unknown-ubuntu12.04" )
# 設置Clang的MD5校驗碼
set( CLANG_MD5 "6077459d20a7ff412eefc6ce3b9f5c85" )
# 設置Clang文件名稱爲之後加上tar.gz
set( CLANG_FILENAME "${CLANG_DIRNAME}.tar.xz" )
else()
# 表示此時爲32位的Linux,下載3.3版本
message( "No pre-built Clang 3.4 binaries for 32 bit linux, "
"downloading Clang 3.3" )
set( CLANG_URL "http://llvm.org/releases/3.3" )
# 設置Clang的文件夾名稱
set( CLANG_DIRNAME "clang+llvm-3.3-i386-debian6" )
# 設置Clang的MD5校驗碼
set( CLANG_MD5 "415d033b60659433d4631df894673802" )
# 設置Clang文件名稱爲之後加上tar.gz
set( CLANG_FILENAME "${CLANG_DIRNAME}.tar.bz2" )
endif()
endif()
# 下載命令
file(
DOWNLOAD "${CLANG_URL}/${CLANG_FILENAME}" "./${CLANG_FILENAME}"
SHOW_PROGRESS EXPECTED_MD5 "${CLANG_MD5}"
)
# 文件名正則表達式匹配,進行相應的解壓
if ( CLANG_FILENAME MATCHES ".+bz2" )
# 執行相關的外部命令 tar
execute_process( COMMAND tar -xjf ${CLANG_FILENAME} )
elseif( CLANG_FILENAME MATCHES ".+xz" )
execute_process( COMMAND tar -xJf ${CLANG_FILENAME} )
else()
execute_process( COMMAND tar -xzf ${CLANG_FILENAME} )
endif()
# 設置PATH_TO_LLVM_ROOT的路徑爲當前CMake二進制路徑下的Clang目錄
set( PATH_TO_LLVM_ROOT "${CMAKE_CURRENT_BINARY_DIR}/../${CLANG_DIRNAME}" )
endif()
# 如果設置了PATH_TO_LLVM_ROOT或者用戶使用系統libclang或者有額外的libclang,就開啓USE_CLANG_COMPLETER
# 這個變量我上文提過,很關鍵
if ( PATH_TO_LLVM_ROOT OR USE_SYSTEM_LIBCLANG OR EXTERNAL_LIBCLANG_PATH )
set( USE_CLANG_COMPLETER TRUE )
endif()
# 開始使用這個變量,如果用戶確定使用libclang,但是沒有root沒有系統clang沒有額外clang,那麼
# 進行錯誤性提示
if ( USE_CLANG_COMPLETER AND
NOT PATH_TO_LLVM_ROOT AND
NOT USE_SYSTEM_LIBCLANG AND
NOT EXTERNAL_LIBCLANG_PATH )
message( FATAL_ERROR
"You have not specified which libclang to use. You have several options:\n"
" 1. Set PATH_TO_LLVM_ROOT to a path to the root of a LLVM+Clang binary "
"distribution. You can download such a binary distro from llvm.org. This "
"is the recommended approach.\n"
" 2. Set USE_SYSTEM_LIBCLANG to ON; this makes YCM search for the system "
"version of libclang.\n"
" 3. Set EXTERNAL_LIBCLANG_PATH to a path to whatever "
"libclang.[so|dylib|dll] you wish to use.\n"
"You HAVE to pick one option. See the docs for more information.")
endif()
# 進行用戶提醒,提醒用戶當前是否使用libclang
if ( USE_CLANG_COMPLETER )
message( "Using libclang to provide semantic completion for C/C++/ObjC" )
else()
message( "NOT using libclang, no semantic completion for C/C++/ObjC will be "
"available" )
endif()
# 如果設置了root就設置CLANG_INCLUDES_DIR爲root下的include
# 否則CLANG_INCLUDES_DIR爲CMake下llvm/include
if ( PATH_TO_LLVM_ROOT )
set( CLANG_INCLUDES_DIR "${PATH_TO_LLVM_ROOT}/include" )
else()
set( CLANG_INCLUDES_DIR "${CMAKE_SOURCE_DIR}/llvm/include" )
endif()
# 如果當前的include路徑不是絕對路徑
if ( NOT IS_ABSOLUTE "${CLANG_INCLUDES_DIR}" )
# 設置它爲絕對路徑
get_filename_component(CLANG_INCLUDES_DIR
"${CMAKE_BINARY_DIR}/${CLANG_INCLUDES_DIR}" ABSOLUTE)
endif()
# 如果沒有額外的libclang,但是有root
if ( NOT EXTERNAL_LIBCLANG_PATH AND PATH_TO_LLVM_ROOT )
if ( MINGW ) # 如果是MINGW
# 設置libclang的尋找路徑(後面的find_library會去尋找)
set( LIBCLANG_SEARCH_PATH "${PATH_TO_LLVM_ROOT}/bin" )
else()
set( LIBCLANG_SEARCH_PATH "${PATH_TO_LLVM_ROOT}/lib" )
endif()
# 這裏TEMP會被find_library去尋找clang,和libclang兩個so,並且複製路徑給TEMP
find_library( TEMP NAMES clang libclang
PATHS ${LIBCLANG_SEARCH_PATH}
NO_DEFAULT_PATH )
message("temp is ${TEMP}")
# 設置額外的libclang爲這個路徑
set( EXTERNAL_LIBCLANG_PATH ${TEMP} )
endif()
# 如果當前爲蘋果,設置額外的flag
if ( APPLE )
set( CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem " )
endif()
# 如果用戶使用系統自帶的boost
if ( USE_SYSTEM_BOOST )
# 進行find boost命令,會用到python,filesystem,system,regex,thread等components
find_package( Boost REQUIRED COMPONENTS python filesystem system regex thread )
else()
# 使用自己的Boost
set( Boost_INCLUDE_DIR ${BoostParts_SOURCE_DIR} )
set( Boost_LIBRARIES BoostParts )
endif()
# 相關頭文件的加入,Boost,Python和Clang
include_directories(
SYSTEM
${Boost_INCLUDE_DIR}
${PYTHON_INCLUDE_DIRS}
${CLANG_INCLUDES_DIR}
)
# 全局遞歸查找h,cpp文件給SERVER_SOURCES
file( GLOB_RECURSE SERVER_SOURCES *.h *.cpp )
# 全局遞歸查找測試相關文件
file( GLOB_RECURSE to_remove tests/*.h tests/*.cpp CMakeFiles/*.cpp *client* )
if( to_remove )
# 學習list相關的REMOVE_ITEM命令
list( REMOVE_ITEM SERVER_SOURCES ${to_remove} )
endif()
# 這裏就是這個變量最關鍵的地方,它會去include並且開啓宏
if ( USE_CLANG_COMPLETER )
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
"${CMAKE_CURRENT_SOURCE_DIR}/ClangCompleter" )
add_definitions( -DUSE_CLANG_COMPLETER )
else()
# 否則的話尋找所有ClangCompleter下的頭和源文件進行刪除
file( GLOB_RECURSE to_remove_clang ClangCompleter/*.h ClangCompleter/*.cpp )
if( to_remove_clang )
list( REMOVE_ITEM SERVER_SOURCES ${to_remove_clang} )
endif()
endif()
# 如果用戶使用額外的libclang或者使用系統自帶的libclang
if ( EXTERNAL_LIBCLANG_PATH OR USE_SYSTEM_LIBCLANG )
if ( USE_SYSTEM_LIBCLANG ) # 如果是系統自帶
if ( APPLE )
set( ENV_LIB_PATHS ENV DYLD_LIBRARY_PATH ) # 將環境變量下的DYLD_LIBRARY_PATH給ENV_LIB_PATHS
elseif ( UNIX )
set( ENV_LIB_PATHS ENV LD_LIBRARY_PATH ) # 這也是我之前講的一定要把你編譯的libclang加入到這個環境變量中,因爲它會根據這個去尋找
elseif ( WIN32 )
set( ENV_LIB_PATHS ENV PATH )
else ()
set( ENV_LIB_PATHS "" )
endif()
file( GLOB SYS_LLVM_PATHS "/usr/lib/llvm*/lib" )
# 進行相關的libclang查找,在你之前給它指定的環境變量中
find_library( TEMP clang
PATHS
${ENV_LIB_PATHS}
/usr/lib
/usr/lib/llvm
${SYS_LLVM_PATHS}
/Library/Developer/CommandLineTools/usr/lib,
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib )
# 將尋找到的變量給EXTERNAL_LIBCLANG_PATH
set( EXTERNAL_LIBCLANG_PATH ${TEMP} )
else()
if ( NOT APPLE )
# 設置相關rpath
set( CMAKE_BUILD_WITH_INSTALL_RPATH TRUE )
# 設置make install之後的rpath
set( CMAKE_INSTALL_RPATH "\$ORIGIN" )
endif()
endif()
set( LIBCLANG_TARGET "" )
message(
"Using external libclang: ${EXTERNAL_LIBCLANG_PATH}" )
message("libclang_target is ${LIBCLANG_TARGET}")
else()
set( LIBCLANG_TARGET )
endif()
# 如果有額外的rpath,在這裏進行設置
if ( EXTRA_RPATH )
set( CMAKE_INSTALL_RPATH "${EXTRA_RPATH}:${CMAKE_INSTALL_RPATH}" )
endif()
# 如果在Linux下需要額外的rt庫
if ( UNIX AND NOT APPLE )
set( EXTRA_LIBS rt )
endif()
# 將目錄下所有的h和cpp給CLIENT_SOURCES
file( GLOB CLIENT_SOURCES *.h *.cpp )
# 相應SERVER_SPECIFIC賦值
file( GLOB SERVER_SPECIFIC *ycm_core* )
if( SERVER_SPECIFIC )
# 移除相關CLIEN_SOURCES下的SERVER_SPECIFIC
list( REMOVE_ITEM CLIENT_SOURCES ${SERVER_SPECIFIC} )
endif()
# 創建client的library,並且是動態庫
add_library( ${CLIENT_LIB} SHARED
${CLIENT_SOURCES}
)
# 將這個庫與之前的rt,Boost,Python進行鏈接
target_link_libraries( ${CLIENT_LIB}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}
${EXTRA_LIBS}
)
# 創建server的library,並且是動態庫
add_library( ${SERVER_LIB} SHARED
${SERVER_SOURCES}
)
# 將這個庫與之前的rt,Boost,Python,libclang,而外的server lib進行鏈接
target_link_libraries( ${SERVER_LIB}
${Boost_LIBRARIES}
${PYTHON_LIBRARIES}
${LIBCLANG_TARGET}
${EXTRA_LIBS}
)
# 如果定義了LIBCLANG_TARGET
if( LIBCLANG_TARGET )
if( NOT WIN32 )
# 在非WIN32情況下增加自定義命令,將libclang.so/dll拷貝到自己目錄下
add_custom_command(
TARGET ${SERVER_LIB}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${LIBCLANG_TARGET}" "$<TARGET_FILE_DIR:${SERVER_LIB}>"
)
else()
add_custom_command(
TARGET ${SERVER_LIB}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${PATH_TO_LLVM_ROOT}/bin/libclang.dll" "$<TARGET_FILE_DIR:${SERVER_LIB}>")
endif()
endif()
# 建立依賴關係,表示這個項目需要這兩個庫共同完成
add_custom_target( ${PROJECT_NAME}
DEPENDS ${CLIENT_LIB} ${SERVER_LIB} )
# Mac下的相關設置,如果是利用rpath的話Mac下還是會去尋找系統庫,即使用戶顯示指定
# 這裏需要改用@loader_path
if ( EXTERNAL_LIBCLANG_PATH AND APPLE )
add_custom_command( TARGET ${SERVER_LIB}
POST_BUILD
COMMAND install_name_tool
"-change"
"@rpath/libclang.dylib"
"@loader_path/libclang.dylib"
"$<TARGET_FILE:${SERVER_LIB}>"
)
endif()
# 將這些庫的前綴lib去掉,因爲會擾亂Python模塊的查找
set_target_properties( ${CLIENT_LIB} PROPERTIES PREFIX "")
set_target_properties( ${SERVER_LIB} PROPERTIES PREFIX "")
if ( WIN32 OR CYGWIN )
# 進行Windows下相關庫的轉移存放
set_target_properties( ${CLIENT_LIB} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. )
set_target_properties( ${SERVER_LIB} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. )
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
set_target_properties( ${CLIENT_LIB} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_SOURCE_DIR}/../.. )
set_target_properties( ${SERVER_LIB} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_SOURCE_DIR}/../.. )
endforeach()
if ( WIN32 )
# 建立後綴名.pyd
set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".pyd")
set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".pyd")
elseif ( CYGWIN )
# CYGIN下後綴爲dll
set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".dll")
set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".dll")
endif()
else()
# Mac和Linux下都爲.so,雖然Mac下應該默認爲.dylib,但Python識別不了dylib,因此這裏還是設置成.so
set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".so")
set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".so")
endif()
# 設置相關lib的輸出目錄
set_target_properties( ${CLIENT_LIB} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. )
set_target_properties( ${SERVER_LIB} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. )
if ( USE_DEV_FLAGS AND ( CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG ) AND
NOT CMAKE_GENERATOR_IS_XCODE )
# 增加相應flag
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror" )
endif()
# 提出警告在使用C++11特性下
if ( USE_DEV_FLAGS AND COMPILER_IS_CLANG AND NOT CMAKE_GENERATOR_IS_XCODE AND
NOT SYSTEM_IS_FREEBSD )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wc++98-compat" )
endif()
if( SYSTEM_IS_SUNOS )
# SunOS需要-pthreads這個flag才能正常使用
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthreads" )
endif()
# 增加測試子目錄tests
add_subdirectory( tests )