Linux中運行可執行文件時找不到lib文件

動態鏈接庫( 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 )

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章