本來想直接把這一部分內容放到那一部分的,但是感覺篇幅有點太長了,就拆出新的一部分來發了。
接着上邊的內容來說,本文會從一個相對比較復雜的project出發,來介紹使用clang來切換gcc的過程。
因為之前說想找到一個合適的project和一個合適的過程來介紹,后邊就想到或許可以嘗試下OpenCV來進行。選擇OpenCV的理由主要為:
- 足夠主流,能滿足一部分人的實際需要,有真實價值
- OpenCV是一個大眾(與小眾相對)的軟件,在寫CMakeLists.txt的時候,考慮到了不同編譯器,不同platform的需求,難度適中
這里我沒有經過太仔細的挑選,選的是master分支,看了下版本號,居然已經到了4.5.3版本,如果我的印象沒錯的話,我接觸的第一個OpenCV版本還是2.4版本,變化真快。 下載代碼:wget -O opencv.zip https://github.com/opencv/opencv/archive/master.zip 解壓等過程不提
略過上邊的廢話,我想把我的思路分成兩節,第一節是一個不考慮OpenCV特性的解決思路,第二節是OpenCV推薦的方式。這里的OpenCV特性,主要指的是OpenCV足夠“大眾”,它本身對多compiler、多platform進行了支持,不僅對host代碼進行了支持,甚至對於類似於需要交叉編譯的環境也進行了支持,這個良好的平台和編譯器移植性,是其他軟件不具備的,第一節就是從這種一般軟件入手來說明OpenCV的移植問題。
第一節 普通軟件的cmake編譯移植
按照OpenCV官網的介紹,整個正常流程如下(https://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html):
# Install minimal prerequisites (Ubuntu 18.04 as reference) sudo apt update && sudo apt install -y cmake g++ wget unzip # Download and unpack sources wget -O opencv.zip https://github.com/opencv/opencv/archive/master.zip unzip opencv.zip # Create build directory mkdir -p build && cd build # Configure cmake ../opencv-master # Build cmake --build .
請務必直接按照這個流程走一遍,確認系統默認環境下有什么問題,先解決,再考慮如何進行交叉編譯器或者clang什么的事情,gcc使用的默認C標准是C89,而clang使用的是C99,一般的差異就是這么多,剩下的option和鏈接問題基本上對照着都有解決方案,最害怕一個人編譯用GCC都編譯不過的東西讓你改換Clang,用nvcc編譯不過的CUDA程序也讓你換Clang。
這里不考慮OpenCV本身的特性,考慮如何切換compiler。大概辦法有兩種,一種是直接指定,一種是在cmake執行的時候指定。
直接指定就是自己修改CMakeList.txt。打開這個文件,發現OpenCV的CMakeLists.txt開頭就有這兩個選項,直接修改:
# ----------------------------------------------------------------------------
# Root CMake file for OpenCV
#
# From the off-tree build directory, invoke:
# $ cmake <PATH_TO_OPENCV_ROOT>
#
# ----------------------------------------------------------------------------
# Disable in-source builds to prevent source tree corruption.
if(" ${CMAKE_SOURCE_DIR}" STREQUAL " ${CMAKE_BINARY_DIR}")
message(FATAL_ERROR "
FATAL: In-source builds are not allowed.
You should create a separate directory for build files.
")
endif()
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
include(cmake/OpenCVMinDepVersions.cmake)
然后按照上邊的進行編譯就好。當然有人會嫌改CMakeLists.txt有風險,在cmake的時候,使用-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++作用也是一樣,這里簡單說明一下,這兩個選項是cmake自己所有的,因此沒有這兩個選項的CMakeLists.txt文件,建議搜下是否其包含的.cmake文件有對應的配置,如果確保都沒有的話,請放心的加。
當然這種切換其實是有風險的,畢竟編譯器不是完全相同,中間大概率會遇到一些問題。比如版本,我使用gcc去編譯就沒有問題,而在使用clang7.0的時候就遇到了avx512指令部分不支持的情況,切換到高版本的clang就得以解決,如果你的項目必須使用低版本的話,就需要對比一下這個地方有哪些功能還沒有support,需要人肉去diff和打patch。
這里也介紹一點點經驗,確定問題的真正來源,需要從make文件入手。在經歷的cmake ..這一步驟后,往往會生成很多的文件,我這里拿OpenCV的dnn模塊介紹一下。

想確定dnn的問題,那么久需要切換到./modules/dnn/CMakeFiles/目錄下查找問題,這種目錄,基本的總控(或者說驅動)文件就是build.make文件,這里opencv_dnn又分成了 opencv_dnn.dir opencv_perf_dnn.dir opencv_test_dnn.dir這三個自文件,到了opencv_dnn.dir下邊,可以清晰的看到

居然有這么多文件,顧名思義,build.make驅動整個build過程,一般會依賴depend.make flags.make,link.txt是鏈接命令來源,想知道你對CmakeLists.txt的修改是否生效,請在這里最終確認,這里也可以方便的幫你確認最終的編譯命令。比如build.make中的編譯命令之一:
modules/dnn/CMakeFiles/opencv_dnn.dir/misc/caffe/opencv-caffe.pb.cc.o: ../modules/dnn/misc/caffe/opencv-caffe.pb.cc
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/home/daily_learning/opencv-master/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Building CXX object modules/dnn/CMakeFiles/opencv_dnn.dir/misc/caffe/opencv-caffe.pb.cc.o"
cd /home/daily_learning/opencv-master/build/modules/dnn && /home/daily_learning/oldLLVM/build/bin/clang++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -Wno-inconsistent-missing-override -o CMakeFiles/opencv_dnn.dir/misc/caffe/opencv-caffe.pb.cc.o -c /home/daily_learning/opencv-master/modules/dnn/misc/caffe/opencv-caffe.pb.cc
這個文件的最后甚至都清晰的告訴你了如何進行link:
lib/libopencv_dnn.so.4.5.3: modules/dnn/CMakeFiles/opencv_dnn.dir/link.txt
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/home/daily_learning/opencv-master/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_109) "Linking CXX shared library ../../lib/libopencv_dnn.so"
cd /home/daily_learning/opencv-master/build/modules/dnn && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/opencv_dnn.dir/link.txt --verbose=$(VERBOSE)
cd /home/daily_learning/opencv-master/build/modules/dnn && $(CMAKE_COMMAND) -E cmake_symlink_library ../../lib/libopencv_dnn.so.4.5.3 ../../lib/libopencv_dnn.so.4.5 ../../lib/libopencv_dnn.so
到對應link.txt中去查找link命令就好
第二節 OpenCV推薦的編譯思路
看下源碼目錄下的cmake文件夾,你會有很多新的發現:

這里清清楚楚的寫明白了OpenCVDetectCXXCompiler.cmake和OpenCVCompilerOptions.cmake這兩個文件。
# Compilers:
# - CV_GCC - GNU compiler (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# - CV_CLANG - Clang-compatible compiler (CMAKE_CXX_COMPILER_ID MATCHES "Clang" - Clang or AppleClang, see CMP0025)
# - CV_ICC - Intel compiler
# - MSVC - Microsoft Visual Compiler (CMake variable)
# - MINGW / CYGWIN / CMAKE_COMPILER_IS_MINGW / CMAKE_COMPILER_IS_CYGWIN (CMake original variables)
#
# CPU Platforms:
# - X86 / X86_64
# - ARM - ARM CPU, not defined for AArch64
# - AARCH64 - ARMv8+ (64-bit)
# - PPC64 / PPC64LE - PowerPC
# - MIPS
#
# OS:
# - WIN32 - Windows | MINGW
# - UNIX - Linux | MacOSX | ANDROID
# - ANDROID
# - IOS
# - APPLE - MacOSX | iOS
# ----------------------------------------------------------------------------
ocv_declare_removed_variables(MINGW64 MSVC64)
# do not use (CMake variables): CMAKE_CL_64
if(NOT DEFINED CV_GCC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(CV_GCC 1)
endif()
if(NOT DEFINED CV_CLANG AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Clang or AppleClang (see CMP0025)
set(CV_CLANG 1)
set(CMAKE_COMPILER_IS_CLANGCXX 1) # TODO next release: remove this
set(CMAKE_COMPILER_IS_CLANGCC 1) # TODO next release: remove this
endif()
function(access_CMAKE_COMPILER_IS_CLANGCXX)
if(NOT OPENCV_SUPPRESS_DEPRECATIONS)
message(WARNING "DEPRECATED: CMAKE_COMPILER_IS_CLANGCXX support is deprecated in OpenCV.
Consider using:
- CV_GCC # GCC
- CV_CLANG # Clang or AppleClang (see CMP0025)
")
endif()
endfunction()
variable_watch(CMAKE_COMPILER_IS_CLANGCXX access_CMAKE_COMPILER_IS_CLANGCXX)
variable_watch(CMAKE_COMPILER_IS_CLANGCC access_CMAKE_COMPILER_IS_CLANGCXX)
因此只需要配置下
set(CMAKE_CXX_COMPILER_ID "clang")
set(CV_CLANG 1)
再進行cmake就可以順利進行了,當然這里會進行一下版本和環境的檢測。
