原文轉載自:Ricky.K http://www.cnblogs.com/rickyk/p/3877238.htmlhtml
我的一直有一個想法,就是想出一系列關於CMakeLists.txt國外經典例子的實戰解讀。由於國內關於CMake的介紹和用法少之又少,再加上CMake自己對於實踐能力的要求也比較高,過於理論化的學習只會讓讀者停留在Hello World和超級項目之間(其實就是理論知識要麼簡單很容易,單個cpp或者單個lib,或者例子複雜的要死,直接帶動整個超大過程相似KDE這種),因此,我以爲我應該帶領讀者去學習一些首先自己不會太簡單但也不至於複雜到無從下手的經典的國外的example,讓讀者更加深刻學習和了解CMake的同時,也讓我能有更好的學習機會,這也是我開Ricky.K這個bolg,記錄博客寫下工做心得的另外一個重要緣由。所以這個系列我會帶上一系列經典的CMakeLists.txt的經典例子,而且帶上個人我的解讀,讀者若是以爲有錯誤或者有想法能夠跟我留言交流。python
廢話很少說,Ricky就帶領你們學習咱們這個系列第一個題目--Vim補全神級插件YouCompleteMe的CMakeLists.txt.先介紹點以前我介紹這款插件的安裝過程當中遇到的問題,實際做者給出了一個具體的install.sh方便你們進行安裝,你能夠輸入install.sh --help具體來查看,裏面有--clang-completer(使用Clang補全)和--system-libclang(使用系統自帶的libclang.so,這裏須要將LD_LIBRARY_PATH設置好,由於後期的CMake腳本會去find_library這個環境變量裏的libclang.so,在這裏我前期只是簡單的加了點PATH的維護,致使以後find_library一直去找的系統下的libclang,致使一直出錯)還有--omnisharp-completer。linux
其實說白了,安裝這個插件最重要的兩個變量就是在這個插件中CMakeLists.txt中的USE_CLANG_COMPLETER和LIBCLANG_TARGET,前者被打開以後會添加相應的ClangCompleter頭文件和宏USE_CLANG_COMPLETER,後者會再最後target_link給ycm_core.so的時候去加載相應路徑下的libclang.so,其實說白了,你只要將這兩個變量本身維護好,脫離腳本安裝,乃至脫離做者介紹的CMake安裝都沒有任何問題。下面正式進入正題c++
1 # CMake要求的最低版本號 2 cmake_minimum_required( VERSION 2.8 ) 3 4 # 項目名稱 5 project( ycm_support_libs ) 6 # 設置客戶端lib和服務端lib的變量名稱 7 set( CLIENT_LIB "ycm_client_support" ) 8 set( SERVER_LIB "ycm_core" ) 9 10 # 設置Python的版本號變量 11 set( Python_ADDITIONAL_VERSIONS 2.7 2.6 ) 12 # 進行Python包的查找,這裏是REQUIRED表示必須 13 find_package( PythonLibs 2.6 REQUIRED ) 14 15 # 若是Python版本號低於3.0.0就進行FATAL_ERROR的出錯信息 16 if ( NOT PYTHONLIBS_VERSION_STRING VERSION_LESS "3.0.0" ) 17 message( FATAL_ERROR 18 "CMake found python3 libs instead of python2 libs. YCM works only with " 19 "python2.\n" ) 20 endif() 21 22 # 各類option 23 option( USE_DEV_FLAGS "Use compilation flags meant for YCM developers" OFF ) 24 # 這個變量就是我上文講到的很關鍵的一個變量,來判斷當前用戶須要不須要libclang 25 option( USE_CLANG_COMPLETER "Use Clang semantic completer for C/C++/ObjC" OFF ) 26 # install.sh中的--system-libclang就與這個變量進行交互 27 option( USE_SYSTEM_LIBCLANG "Set to ON to use the system libclang library" OFF ) 28 # YCM做者推薦的用法,在這裏直接寫入Clang的相關路徑 注意這裏的CACHE PATH,表示當用戶若是命令行 29 # 進行指定,那優先會去讀用戶的命令行,而不是用這裏的set,而且把相關的值寫入Cache中 30 set( PATH_TO_LLVM_ROOT "" CACHE PATH "Path to the root of a LLVM+Clang binary distribution" ) 31 # YCM做者推薦的另一種安裝方法,直接將libclang.so全路徑寫死 32 set( EXTERNAL_LIBCLANG_PATH "" CACHE PATH "Path to the libclang library to use" ) 33 34 # 若是你使用libclang可是沒有指定用不用系統的libclang,沒有指定llvm_root,沒有指定額外的libclang.so,那麼就會帶你去下載 35 if ( USE_CLANG_COMPLETER AND 36 NOT USE_SYSTEM_LIBCLANG AND 37 NOT PATH_TO_LLVM_ROOT AND 38 NOT EXTERNAL_LIBCLANG_PATH ) 39 message( "Downloading Clang 3.4" ) 40 41 # 這就是llvm官網的3.4下載路徑 42 set( CLANG_URL "http://llvm.org/releases/3.4" ) 43 44 # 若是當前客戶端是蘋果 Mac OS X 45 if ( APPLE ) 46 # 設置Clang的文件夾名稱 47 set( CLANG_DIRNAME "clang+llvm-3.4-x86_64-apple-darwin10.9" ) 48 # 設置Clang的MD5校驗碼 49 set( CLANG_MD5 "4f43ea0e87090ae5e7bec12373ca4927" ) 50 # 設置Clang文件名稱爲以後加上tar.gz 51 set( CLANG_FILENAME "${CLANG_DIRNAME}.tar.gz" ) 52 else() 53 # 若是是64位平臺 54 if ( 64_BIT_PLATFORM ) 55 # 設置Clang的文件夾名稱 56 set( CLANG_DIRNAME "clang+llvm-3.4-x86_64-unknown-ubuntu12.04" ) 57 # 設置Clang的MD5校驗碼 58 set( CLANG_MD5 "6077459d20a7ff412eefc6ce3b9f5c85" ) 59 # 設置Clang文件名稱爲以後加上tar.gz 60 set( CLANG_FILENAME "${CLANG_DIRNAME}.tar.xz" ) 61 else() 62 # 表示此時爲32位的Linux,下載3.3版本 63 message( "No pre-built Clang 3.4 binaries for 32 bit linux, " 64 "downloading Clang 3.3" ) 65 set( CLANG_URL "http://llvm.org/releases/3.3" ) 66 # 設置Clang的文件夾名稱 67 set( CLANG_DIRNAME "clang+llvm-3.3-i386-debian6" ) 68 # 設置Clang的MD5校驗碼 69 set( CLANG_MD5 "415d033b60659433d4631df894673802" ) 70 # 設置Clang文件名稱爲以後加上tar.gz 71 set( CLANG_FILENAME "${CLANG_DIRNAME}.tar.bz2" ) 72 endif() 73 endif() 74 75 # 下載命令 76 file( 77 DOWNLOAD "${CLANG_URL}/${CLANG_FILENAME}" "./${CLANG_FILENAME}" 78 SHOW_PROGRESS EXPECTED_MD5 "${CLANG_MD5}" 79 ) 80 81 # 文件名正則表達式匹配,進行相應的解壓 82 if ( CLANG_FILENAME MATCHES ".+bz2" ) 83 # 執行相關的外部命令 tar 84 execute_process( COMMAND tar -xjf ${CLANG_FILENAME} ) 85 elseif( CLANG_FILENAME MATCHES ".+xz" ) 86 execute_process( COMMAND tar -xJf ${CLANG_FILENAME} ) 87 else() 88 execute_process( COMMAND tar -xzf ${CLANG_FILENAME} ) 89 endif() 90 91 # 設置PATH_TO_LLVM_ROOT的路徑爲當前CMake二進制路徑下的Clang目錄 92 set( PATH_TO_LLVM_ROOT "${CMAKE_CURRENT_BINARY_DIR}/../${CLANG_DIRNAME}" ) 93 endif() 94 95 # 若是設置了PATH_TO_LLVM_ROOT或者用戶使用系統libclang或者有額外的libclang,就開啓USE_CLANG_COMPLETER 96 # 這個變量我上文提過,很關鍵 97 if ( PATH_TO_LLVM_ROOT OR USE_SYSTEM_LIBCLANG OR EXTERNAL_LIBCLANG_PATH ) 98 set( USE_CLANG_COMPLETER TRUE ) 99 endif() 100 101 # 開始使用這個變量,若是用戶肯定使用libclang,可是沒有root沒有系統clang沒有額外clang,那麼 102 # 進行錯誤性提示 103 if ( USE_CLANG_COMPLETER AND 104 NOT PATH_TO_LLVM_ROOT AND 105 NOT USE_SYSTEM_LIBCLANG AND 106 NOT EXTERNAL_LIBCLANG_PATH ) 107 message( FATAL_ERROR 108 "You have not specified which libclang to use. You have several options:\n" 109 " 1. Set PATH_TO_LLVM_ROOT to a path to the root of a LLVM+Clang binary " 110 "distribution. You can download such a binary distro from llvm.org. This " 111 "is the recommended approach.\n" 112 " 2. Set USE_SYSTEM_LIBCLANG to ON; this makes YCM search for the system " 113 "version of libclang.\n" 114 " 3. Set EXTERNAL_LIBCLANG_PATH to a path to whatever " 115 "libclang.[so|dylib|dll] you wish to use.\n" 116 "You HAVE to pick one option. See the docs for more information.") 117 endif() 118 119 # 進行用戶提醒,提醒用戶當前是否使用libclang 120 if ( USE_CLANG_COMPLETER ) 121 message( "Using libclang to provide semantic completion for C/C++/ObjC" ) 122 else() 123 message( "NOT using libclang, no semantic completion for C/C++/ObjC will be " 124 "available" ) 125 endif() 126 127 # 若是設置了root就設置CLANG_INCLUDES_DIR爲root下的include 128 # 不然CLANG_INCLUDES_DIR爲CMake下llvm/include 129 if ( PATH_TO_LLVM_ROOT ) 130 set( CLANG_INCLUDES_DIR "${PATH_TO_LLVM_ROOT}/include" ) 131 else() 132 set( CLANG_INCLUDES_DIR "${CMAKE_SOURCE_DIR}/llvm/include" ) 133 endif() 134 135 # 若是當前的include路徑不是絕對路徑 136 if ( NOT IS_ABSOLUTE "${CLANG_INCLUDES_DIR}" ) 137 # 設置它爲絕對路徑 138 get_filename_component(CLANG_INCLUDES_DIR 139 "${CMAKE_BINARY_DIR}/${CLANG_INCLUDES_DIR}" ABSOLUTE) 140 endif() 141 142 # 若是沒有額外的libclang,可是有root 143 if ( NOT EXTERNAL_LIBCLANG_PATH AND PATH_TO_LLVM_ROOT ) 144 if ( MINGW ) # 若是是MINGW 145 # 設置libclang的尋找路徑(後面的find_library會去尋找) 146 set( LIBCLANG_SEARCH_PATH "${PATH_TO_LLVM_ROOT}/bin" ) 147 else() 148 set( LIBCLANG_SEARCH_PATH "${PATH_TO_LLVM_ROOT}/lib" ) 149 endif() 150 151 # 這裏TEMP會被find_library去尋找clang,和libclang兩個so,而且複製路徑給TEMP 152 find_library( TEMP NAMES clang libclang 153 PATHS ${LIBCLANG_SEARCH_PATH} 154 NO_DEFAULT_PATH ) 155 156 message("temp is ${TEMP}") 157 # 設置額外的libclang爲這個路徑 158 set( EXTERNAL_LIBCLANG_PATH ${TEMP} ) 159 endif() 160 161 # 若是當前爲蘋果,設置額外的flag 162 if ( APPLE ) 163 set( CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem " ) 164 endif() 165 166 # 若是用戶使用系統自帶的boost 167 if ( USE_SYSTEM_BOOST ) 168 # 進行find boost命令,會用到python,filesystem,system,regex,thread等components 169 find_package( Boost REQUIRED COMPONENTS python filesystem system regex thread ) 170 else() 171 # 使用本身的Boost 172 set( Boost_INCLUDE_DIR ${BoostParts_SOURCE_DIR} ) 173 set( Boost_LIBRARIES BoostParts ) 174 endif() 175 176 # 相關頭文件的加入,Boost,Python和Clang 177 include_directories( 178 SYSTEM 179 ${Boost_INCLUDE_DIR} 180 ${PYTHON_INCLUDE_DIRS} 181 ${CLANG_INCLUDES_DIR} 182 ) 183 184 # 全局遞歸查找h,cpp文件給SERVER_SOURCES 185 file( GLOB_RECURSE SERVER_SOURCES *.h *.cpp ) 186 187 # 全局遞歸查找測試相關文件 188 file( GLOB_RECURSE to_remove tests/*.h tests/*.cpp CMakeFiles/*.cpp *client* ) 189 190 if( to_remove ) 191 # 學習list相關的REMOVE_ITEM命令 192 list( REMOVE_ITEM SERVER_SOURCES ${to_remove} ) 193 endif() 194 195 # 這裏就是這個變量最關鍵的地方,它會去include而且開啓宏 196 if ( USE_CLANG_COMPLETER ) 197 include_directories( 198 ${CMAKE_CURRENT_SOURCE_DIR} 199 "${CMAKE_CURRENT_SOURCE_DIR}/ClangCompleter" ) 200 add_definitions( -DUSE_CLANG_COMPLETER ) 201 else() 202 # 不然的話尋找全部ClangCompleter下的頭和源文件進行刪除 203 file( GLOB_RECURSE to_remove_clang ClangCompleter/*.h ClangCompleter/*.cpp ) 204 205 if( to_remove_clang ) 206 list( REMOVE_ITEM SERVER_SOURCES ${to_remove_clang} ) 207 endif() 208 endif() 209 210 # 若是用戶使用額外的libclang或者使用系統自帶的libclang 211 if ( EXTERNAL_LIBCLANG_PATH OR USE_SYSTEM_LIBCLANG ) 212 if ( USE_SYSTEM_LIBCLANG ) # 若是是系統自帶 213 if ( APPLE ) 214 set( ENV_LIB_PATHS ENV DYLD_LIBRARY_PATH ) # 將環境變量下的DYLD_LIBRARY_PATH給ENV_LIB_PATHS 215 elseif ( UNIX ) 216 set( ENV_LIB_PATHS ENV LD_LIBRARY_PATH ) # 這也是我以前講的必定要把你編譯的libclang加入到這個環境變量中,由於它會根據這個去尋找 217 elseif ( WIN32 ) 218 set( ENV_LIB_PATHS ENV PATH ) 219 else () 220 set( ENV_LIB_PATHS "" ) 221 endif() 222 file( GLOB SYS_LLVM_PATHS "/usr/lib/llvm*/lib" ) 223 224 # 進行相關的libclang查找,在你以前給它指定的環境變量中 225 find_library( TEMP clang 226 PATHS 227 ${ENV_LIB_PATHS} 228 /usr/lib 229 /usr/lib/llvm 230 ${SYS_LLVM_PATHS} 231 /Library/Developer/CommandLineTools/usr/lib, 232 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib ) 233 # 將尋找到的變量給EXTERNAL_LIBCLANG_PATH 234 set( EXTERNAL_LIBCLANG_PATH ${TEMP} ) 235 else() 236 if ( NOT APPLE ) 237 # 設置相關rpath 238 set( CMAKE_BUILD_WITH_INSTALL_RPATH TRUE ) 239 # 設置make install以後的rpath 240 set( CMAKE_INSTALL_RPATH "\$ORIGIN" ) 241 endif() 242 endif() 243 244 set( LIBCLANG_TARGET "" ) 245 message( 246 "Using external libclang: ${EXTERNAL_LIBCLANG_PATH}" ) 247 message("libclang_target is ${LIBCLANG_TARGET}") 248 else() 249 set( LIBCLANG_TARGET ) 250 endif() 251 252 # 若是有額外的rpath,在這裏進行設置 253 if ( EXTRA_RPATH ) 254 set( CMAKE_INSTALL_RPATH "${EXTRA_RPATH}:${CMAKE_INSTALL_RPATH}" ) 255 endif() 256 257 # 若是在Linux下須要額外的rt庫 258 if ( UNIX AND NOT APPLE ) 259 set( EXTRA_LIBS rt ) 260 endif() 261 262 # 將目錄下全部的h和cpp給CLIENT_SOURCES 263 file( GLOB CLIENT_SOURCES *.h *.cpp ) 264 # 相應SERVER_SPECIFIC賦值 265 file( GLOB SERVER_SPECIFIC *ycm_core* ) 266 267 if( SERVER_SPECIFIC ) 268 # 移除相關CLIEN_SOURCES下的SERVER_SPECIFIC 269 list( REMOVE_ITEM CLIENT_SOURCES ${SERVER_SPECIFIC} ) 270 endif() 271 272 # 建立client的library,而且是動態庫 273 add_library( ${CLIENT_LIB} SHARED 274 ${CLIENT_SOURCES} 275 ) 276 277 # 將這個庫與以前的rt,Boost,Python進行連接 278 target_link_libraries( ${CLIENT_LIB} 279 ${Boost_LIBRARIES} 280 ${PYTHON_LIBRARIES} 281 ${EXTRA_LIBS} 282 ) 283 284 # 建立server的library,而且是動態庫 285 add_library( ${SERVER_LIB} SHARED 286 ${SERVER_SOURCES} 287 ) 288 289 # 將這個庫與以前的rt,Boost,Python,libclang,而外的server lib進行連接 290 target_link_libraries( ${SERVER_LIB} 291 ${Boost_LIBRARIES} 292 ${PYTHON_LIBRARIES} 293 ${LIBCLANG_TARGET} 294 ${EXTRA_LIBS} 295 ) 296 297 # 若是定義了LIBCLANG_TARGET 298 if( LIBCLANG_TARGET ) 299 if( NOT WIN32 ) 300 # 在非WIN32狀況下增長自定義命令,將libclang.so/dll拷貝到本身目錄下 301 add_custom_command( 302 TARGET ${SERVER_LIB} 303 POST_BUILD 304 COMMAND ${CMAKE_COMMAND} -E copy "${LIBCLANG_TARGET}" "$<TARGET_FILE_DIR:${SERVER_LIB}>" 305 ) 306 else() 307 add_custom_command( 308 TARGET ${SERVER_LIB} 309 POST_BUILD 310 COMMAND ${CMAKE_COMMAND} -E copy "${PATH_TO_LLVM_ROOT}/bin/libclang.dll" "$<TARGET_FILE_DIR:${SERVER_LIB}>") 311 endif() 312 endif() 313 314 # 創建依賴關係,表示這個項目須要這兩個庫共同完成 315 add_custom_target( ${PROJECT_NAME} 316 DEPENDS ${CLIENT_LIB} ${SERVER_LIB} ) 317 318 # Mac下的相關設置,若是是利用rpath的話Mac下仍是會去尋找系統庫,即便用戶顯示指定 319 # 這裏須要改用@loader_path 320 if ( EXTERNAL_LIBCLANG_PATH AND APPLE ) 321 add_custom_command( TARGET ${SERVER_LIB} 322 POST_BUILD 323 COMMAND install_name_tool 324 "-change" 325 "@rpath/libclang.dylib" 326 "@loader_path/libclang.dylib" 327 "$<TARGET_FILE:${SERVER_LIB}>" 328 ) 329 endif() 330 331 # 將這些庫的前綴lib去掉,由於會擾亂Python模塊的查找 332 set_target_properties( ${CLIENT_LIB} PROPERTIES PREFIX "") 333 set_target_properties( ${SERVER_LIB} PROPERTIES PREFIX "") 334 335 if ( WIN32 OR CYGWIN ) 336 # 進行Windows下相關庫的轉移存放 337 set_target_properties( ${CLIENT_LIB} PROPERTIES 338 RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. ) 339 set_target_properties( ${SERVER_LIB} PROPERTIES 340 RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. ) 341 foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) 342 string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) 343 set_target_properties( ${CLIENT_LIB} PROPERTIES 344 RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_SOURCE_DIR}/../.. ) 345 set_target_properties( ${SERVER_LIB} PROPERTIES 346 RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_SOURCE_DIR}/../.. ) 347 endforeach() 348 349 if ( WIN32 ) 350 # 創建後綴名.pyd 351 set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".pyd") 352 set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".pyd") 353 elseif ( CYGWIN ) 354 # CYGIN下後綴爲dll 355 set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".dll") 356 set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".dll") 357 endif() 358 else() 359 # Mac和Linux下都爲.so,雖然Mac下應該默認爲.dylib,但Python識別不了dylib,所以這裏仍是設置成.so 360 set_target_properties( ${CLIENT_LIB} PROPERTIES SUFFIX ".so") 361 set_target_properties( ${SERVER_LIB} PROPERTIES SUFFIX ".so") 362 endif() 363 364 # 設置相關lib的輸出目錄 365 set_target_properties( ${CLIENT_LIB} PROPERTIES 366 LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. ) 367 set_target_properties( ${SERVER_LIB} PROPERTIES 368 LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../.. ) 369 370 if ( USE_DEV_FLAGS AND ( CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG ) AND 371 NOT CMAKE_GENERATOR_IS_XCODE ) 372 # 增長相應flag 373 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror" ) 374 endif() 375 376 # 提出警告在使用C++11特性下 377 if ( USE_DEV_FLAGS AND COMPILER_IS_CLANG AND NOT CMAKE_GENERATOR_IS_XCODE AND 378 NOT SYSTEM_IS_FREEBSD ) 379 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wc++98-compat" ) 380 endif() 381 382 if( SYSTEM_IS_SUNOS ) 383 # SunOS須要-pthreads這個flag才能正常使用 384 set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthreads" ) 385 endif() 386 387 # 增長測試子目錄tests 388 add_subdirectory( tests )