cmake
导言
ipcc初赛的项目代码有些混乱,只是简单分类。想好好学习一下cmake和make。规范项目结构,优化编译运行流程,提高效率。
cmake简介¶
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
安装¶
自动生成CMakeLists.txt¶
ccmake¶
打印详细编译信息¶
- 执行命令cmake时追加:
-DCMAKE_VERBOSE_MAKEFILE=ON
- 查看整个编译过程。你可以使用
cmake --trace-expand .
选项来详细追踪 CMake 的执行。 - 在CMakeLists.txt中添加:
set(CMAKE_VERBOSE_MAKEFILE ON)
cmake结束之后,单独运行make
时追加: VERBOSE=1
就会显示最基本的编译命令。加上--dry-run
选项 能快速只打印编译命令。
list all option¶
mkdir build
cd build
cmake ..
cmake -LA | awk '{if(f)print} /-- Cache values/{f=1}'
cmake -LAH # adding -H to show more help information about each option
快速编译¶
0. Fast Recompile¶
由于cmake实际就是将一堆cpp编译成.so或者exe的目标文件;为此CMake生成的Makefile,对每个target提供了fast编译选项, 例如 make cpprinter/fast
。
# target_link_libraries(your_project PRIVATE cpprinter)
# Target rules for targets named cpprinter
# Build rule for target.
cpprinter: cmake_check_build_system
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 cpprinter
.PHONY : cpprinter
# fast build rule for target.
cpprinter/fast:
$(MAKE) $(MAKESILENT) -f _deps/cpp_printer-build/CMakeFiles/cpprinter.dir/build.make _deps/cpp_printer-build/CMakeFiles/cpprinter.dir/build
.PHONY : cpprinter/fast
1. 剔除不必要的组件和功能¶
PyTorch 是一个庞大的项目,可能包含很多你不需要的模块。你可以通过调整 CMake
配置选项来禁用不必要的部分。例如,如果你不需要 GPU 支持,可以禁用 CUDA:
其他常见的选项还包括禁用 distributed
、test
、python
等模块。
2. 使用 Ninja
作为构建系统¶
- 如果你还在使用
make
,可以考虑切换到ninja
,因为ninja
通常比make
更快,尤其是对大型项目。 - 首先确保系统安装了
ninja
,然后在运行cmake
时指定ninja
作为生成器:
Ninja
还能更好地利用并行编译,加速整个过程。
已有大型项目很难自动迁移,各种语法问题
3. 使用 ccache
缓存编译结果¶
ccache
是一个编译结果缓存工具,可以显著加快重复编译的速度。你可以安装并启用 ccache,它会缓存已编译过的目标文件,下次编译时直接复用:
接着重新配置并编译项目。
常用选项¶
-S <path-to-source> = Explicitly specify a source directory.CMakeLists.txt路径
-B <path-to-build> = Explicitly specify a build directory.
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_INSTALL_PREFIX=directory --- Specify for directory the full path name of where you want thetools and libraries to be installed (default /usr/local)
-G <generator>
基于不同平台的makefile生成方式
- -G "Unix Makefiles"
- -G "MinGW"
- 之类的
CMakeList.txt基本语法¶
基础设置¶
# cmake required version
cmake_minimum_required(VERSION 3.0.0)
# more error message
SET( CMAKE_VERBOSE_MAKEFILE on )
#project name
PROJECT(ipcc_test)
# compile
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)
# flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O3 -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3 -pthread -fopenmp")
#head file path
INCLUDE_DIRECTORIES(include)
#source directory
AUX_SOURCE_DIRECTORY(src DIR_SRCS) # 搜集所有在指定路径下的源文件的文件名,将输出结果列表储存在指定的变量DIR_SRCS中
# message
message(STATUS "PROJECT_SOURCE_DIR is ${PROJECT_SOURCE_DIR}")
message(STATUS "PROJECT_BINARY_DIR is ${PROJECT_BINARY_DIR}")
message(STATUS "CMAKE_CURRENT_SOURCE_DIR is ${CMAKE_CURRENT_SOURCE_DIR}")
#output path
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
#set environment variable
SET(TEST ${DIR_SRCS}) # 此处用于显示如何用环境变量对环境变量进行赋值
自定义命令¶
add_custom_target
是 CMake 中用于定义一个自定义目标的命令。这个目标不直接对应于生成一个输出文件,而是可以用于执行自定义命令或操作。下面是你提供的代码的解释和如何使用 add_custom_target
的说明:
- 自定义命令:
-
这个命令会在指定的工作目录下运行
make clean
和make
,并生成cman.ko
文件。 -
自定义目标:
drv
是自定义目标的名称。- 该目标依赖于
${CMAN_DRV_KO}
,这意味着在构建drv
时,会确保cman.ko
已经被生成。 -
如果 cman.ko 不存在或是过期的,CMake 会自动执行上一个自定义命令来生成它。
-
安装目标:
drv-install
是另一个自定义目标,用于安装驱动。- 这个目标也依赖于
${CMAN_DRV_KO}
,并在构建时运行make install
。
add_custom_target
允许你定义在构建过程中特定的自定义操作,比如编译和安装操作,而不直接生成输出文件。通过设置依赖关系,可以确保在运行自定义目标时所需的文件已经生成。这种方法在需要执行多个步骤或使用外部工具时特别有用。
目标设置¶
#add target/final executable file
ADD_EXECUTABLE(./bin/exe ${TEST})
# 指定最后生成类似.so的文件
add_library(${PLUGIN_NAME} SHARED ${CPP_SRCS})
# 继续构建的子目录
add_subdirectory(dir)
链接库¶
# add lib path to search lib file
link_directories(${CMAKE_SOURCE_DIR}/lib)
add_executable(foo ${FOO_SRCS})
#set extern libraries
SET(LIBRARIES LIBRARIESlibm.so)
#add link library
TARGET_LINK_LIBRARIES(../bin/bin ${LIBRARIES})
target_link_libraries(foo bar) # libbar.so is found in ${CMAKE_SOURCE_DIR}/lib
安装到系统¶
# 安装目标, 在构建完成后,运行 `make install` 命令将自动执行 CMake 中定义的安装规则。
## 指定了将 `cman.ko` 文件安装到系统的 `/lib/modules/{architecture}/kernel/drivers` 目录中。
install(FILES ${CMAN_DRV_KO} DESTINATION /lib/modules/${CMAKE_SYSTEM_PROCESSOR}/kernel/drivers)
## DESTINATION include:这表示安装路径将是系统的 include 目录,通常是 `/usr/local/include` 或者根据 `CMAKE_INSTALL_PREFIX` 的设置。
install(DIRECTORY ${CMAN_INCLUDE_DIR}/ DESTINATION include)
## ARCHIVE 表示将该目标作为一个归档文件(通常是静态库,如 .a 文件)进行安装。默认情况下,库文件会安装到 `/usr/local/lib` 或根据 `CMAKE_INSTALL_PREFIX` 的设置。
install(TARGETS cman ARCHIVE)
## EXPORT 关键字用于将目标的信息(例如目标的路径、依赖等)导出到一个 CMake 文件中,这样其他项目就可以通过 find_package(Caffe2Targets) 来引用这些目标。
install(TARGETS cpprinter EXPORT Caffe2Targets)
# 可选:指定安装后的处理
install(CODE "message(STATUS \"Driver installed to /lib/modules/${CMAKE_SYSTEM_PROCESSOR}/kernel/drivers\")")
- 自定义安装路径:如果希望安装到其他目录,可以修改
DESTINATION
参数的值,或者在运行 CMake 时指定CMAKE_INSTALL_PREFIX
变量。例如:
通过这种方式,CMake 生成的 Makefile
将能够支持 make install
命令将文件安装到指定的系统目录。
技巧¶
多级目录编译¶
在CMakeLists.txt
里*.cpp
一定会被变量包含AUX_SOURCE_DIRECTORY
或者FILE
,然后跨层传递add_subdirectory
,变量组合SET
,直至被添加到可执行文件ADD_EXECUTABLE
或者add_library
里
set(exampleDIR "xxx")
# pre-define
set(dir1_srcs)
set(dir2_srcs)
set(dir3_srcs)
# cmake in subdir (重要)
add_subdirectory(${exampleDIR}/subdir)
SET(CPP_SRCS ${dir1_srcs} ${dir2_srcs} ${dir3_srcs})
add_library(${PLUGIN_NAME} SHARED ${CPP_SRCS})
FILE(GLOB _dir1_srcs *.cpp)
LIST(APPEND dir1_srcs ${_dir1_srcs})
# pass to parent(the CMakeLists.txt using add_subdirectory)
SET(dir1_srcs ${dir1_srcs} PARENT_SCOPE)
实例¶
目录下所有源文件编译成单独的可执行文件
实现头文件、执行文件、源代码都分开存储。
file(GLOB files "*/*.cpp")
foreach (file ${files})
message(STATUS "file is ${file}")
string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file})
message(STATUS "file name is ${exe}")
add_executable(${exe} ${file})
TARGET_LINK_LIBRARIES(${exe} ${CONAN_LIBS})
endforeach ()
string(REGEX REPLACE <regular_expression>
<replacement_expression> <output_variable>
<input> [<input>...])
尽可能匹配 <regular_expression>
多次,并用 <replacement_expression>
替换输出中的匹配项。匹配之前将所有 <input>
参数连接在一起。
所述 <replacement_expression>
可以是指使用匹配的括号分隔子表达式 \1 , \2 ,..., \9
。请注意,CMake代码中需要两个反斜杠( \\1
)才能通过参数解析获得反斜杠。同理 \\.
是匹配 .
的意思。
# Find all main*.cpp files and store in list mains
file(GLOB_RECURSE mains RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/main*.cpp")
foreach(mainfile IN LISTS mains)
# Get file name without directory
get_filename_component(mainname ${mainfile} NAME_WE)
add_executable(${mainname} ${mainfile})
endforeach()
将指定的文件编译成单独的可执行文件
运行n次并求平均时间
比较时间的Cmake优化小项目
https://github.com/Kirrito-k423/StencilAcc
需要进一步的研究学习¶
- 学习Quest cmake 添加openmp的编译选项核compile option
参考文献¶
https://www.cnblogs.com/lyq105/archive/2010/12/03/1895067.html
https://blog.csdn.net/ET_Endeavoring/article/details/98989066
———————————————— 版权声明:本文为CSDN博主「商汤科技」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq295109601/article/details/118867192