浅谈如何使用clang替换gcc进行编译(2)
本来想直接把这一部分内容放到那一部分的,但是感觉篇幅有点太长了,就拆出新的一部分来发了。
接着上边的内容来说,本文会从一个相对比较复杂的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就可以顺利进行了,当然这里会进行一下版本和环境的检测。