前言
最开始学习C肯定是从最初的gcc编译一个可执行文件学起,那时候我们知道编译的主要工作流程是:源码–》预处理(.i)–》编译(.s)–》汇编(.o)–》链接(exe)–》可执行程序。当源文件较多时,就不适合使用gcc编译代码,就用到了makefile,通过编译顺序等使用Make就可以自动完成编译工作,效率极大提升。
做了sdwan项目,发现openwrt等集成了很多的源码包都是通过cmake编译的,发现比makefile更好用。
介绍
cmake编译流程
- 编写源文件
- 编写CMakeLists.txt
- 根据CMakeLists.txt 生成makefile
- makefile调用gcc完成编译
CMakeLists.txt主要内容
- cmake_minimum_required(VERSION 2.8.0):用于指定cmake所需最低版本;
- project(Project) :用于指定项目名称;
- include_directories() :用于包含头文件目录;
- aux_source_directory(src dir_srcs):用于包含源文件目录;
- set(TEST_MATH) :用于设置环境变量,编译用到的源文件全部都要放到这里;
- add_executable(${PROJECT_NAME} ${TEST_MATH}):用于添加要编译的可执行文件;
- target_link_libraries(${PROJECT_NAME} m):用于添加可执行文件所需要的库;
常用的变量
- CMAKE_MAJOR_VERSION:cmake 主版本号;
- CMAKE_MINOR_VERSION:cmake 次版本号;
- CMAKE_C_FLAGS:设置 C 编译选项;
- CMAKE_CXX_FLAGS:设置 C++ 编译选项;
- PROJECT_SOURCE_DIR:工程的根目录;
- PROJECT_BINARY_DIR:运行 cmake 命令的目录;
- CMAKE_CURRENT_SOURCE_DIR:当前CMakeLists.txt 所在路径;
- CMAKE_CURRENT_BINARY_DIR:目标文件编译目录;
- EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置
- LIBRARY_OUTPUT_PATH:重新定义目标链接库文件的存放位置
简单的示例
# 指定所需版本
cmake_minimum_required(VERSION 2.6)
# 将自定义的CMake模块路径添加到CMake模块搜索路径中, 这是用来包含一些额外的CMake模块,以便在构建过程中使用
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/")
set(PLUGINS_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/sysrepo/plugins/" CACHE PATH "Sysrepo plugins directory.")
project(sysrepo-XXXXdevice)
# 设置库文件的输出目录
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(SOURCES
src/device.c)
if(CMAKE_BUILD_TYPE MATCHES "debug")
add_executable(${CMAKE_PROJECT_NAME} ${SOURCES})
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE DEBUG=1)
install(TARGETS ${CMAKE_PROJECT_NAME}
DESTINATION lib/sysrepo/plugins)
else()
# 添加-g选项到C编译选项中
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
add_library(${CMAKE_PROJECT_NAME} MODULE ${SOURCES})
install(TARGETS ${CMAKE_PROJECT_NAME} LIBRARY
DESTINATION lib/sysrepo/plugins)
endif()
set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME} PREFIX "")
# 使用CMake的Find模块来查找Sysrepo库
find_package(SYSREPO REQUIRED)
# 将Sysrepo库链接到项目中,以便在编译时可以使用Sysrepo的功能
target_link_libraries(${CMAKE_PROJECT_NAME} ${SYSREPO_LIBRARIES})
include_directories(${SYSREPO_INCLUDE_DIRS})
find_package(UCI REQUIRED)
include_directories(${UCI_INCLUDE_DIR})
target_link_libraries(${CMAKE_PROJECT_NAME} ${UCI_LIBRARIES})
# 将项目的目标文件(可执行文件或动态链接库,取决于构建类型)安装到之前定义的插件目录(PLUGINS_DIR)中
install(TARGETS ${CMAKE_PROJECT_NAME} DESTINATION ${PLUGINS_DIR})
这里还用到了其他的模块,目录在CMakeModules中。
cmake_module
这个目录通常包含很多的.cmake文件, 用于找特定的库。以查找uci库为例,在CMakeLists.txt使用:
find_package(UCI REQUIRED)
include_directories(${UCI_INCLUDE_DIR})
target_link_libraries(${CMAKE_PROJECT_NAME} ${UCI_LIBRARIES})
在CMakeModules目录下会有一个文件:FindUCI.cmake, 格式通常是:FindXXX..cmake,内容格式通常如下:
# This is a FindXXX.cmake module for finding the XXX library.
# Define a variable to store the library path.
set(XXX_LIBRARY "")
# Define a variable to store the include directories.
set(XXX_INCLUDE_DIR "")
# Check for the XXX library.
find_library(XXX_LIBRARY
NAMES XXX
PATHS /usr/lib /usr/local/lib
)
# Check for the XXX header files.
find_path(XXX_INCLUDE_DIR
NAMES xxx.h
PATHS /usr/include /usr/local/include
)
# Check if the XXX library and headers were found.
if(XXX_LIBRARY AND XXX_INCLUDE_DIR)
set(XXX_FOUND TRUE)
else()
set(XXX_FOUND FALSE)
endif()
# Provide information about the XXX library.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(XXX
REQUIRED_VARS XXX_LIBRARY XXX_INCLUDE_DIR
)
# If the XXX library was found, set the XXX_FOUND variable to TRUE.
if(XXX_FOUND)
set(XXX_FOUND TRUE)
else()
set(XXX_FOUND FALSE)
endif()
# Export the results to the parent scope (if needed).
if(XXX_FOUND)
set(XXX_FOUND TRUE PARENT_SCOPE)
set(XXX_LIBRARY ${XXX_LIBRARY} PARENT_SCOPE)
set(XXX_INCLUDE_DIR ${XXX_INCLUDE_DIR} PARENT_SCOPE)
endif()
写的uci如下:
# UCI_FOUND - true if library and headers were found
# UCI_INCLUDE_DIRS - include directories
# UCI_LIBRARIES - library directories
find_package(PkgConfig)
pkg_check_modules(PC_UCI QUIET uci)
find_path(UCI_INCLUDE_DIR uci.h
HINTS ${PC_UCI_INCLUDEDIR} ${PC_UCI_INCLUDE_DIRS})
find_library(UCI_LIBRARY NAMES uci libuci
HINTS ${PC_UCI_LIBDIR} ${PC_UCI_LIBRARY_DIRS})
set(UCI_LIBRARIES ${UCI_LIBRARY})
set(UCI_INCLUDE_DIRS ${UCI_INCLUDE_DIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(UCI DEFAULT_MSG UCI_LIBRARY UCI_INCLUDE_DIR)
mark_as_advanced(UCI_INCLUDE_DIR UCI_LIBRARY)