这一段要把内核模块集成到整个工程中进行编译,但是内核模块原来是用makefile编译的,现在需要转换成cmake。经过一天的探索,总共找到3种方式来实现这个目标。下面这是第一种方法,具体思路就是在cmake中使用add_custom_command和add_custom_target的方式来实现编译,也就是把makefile中规定的obj-m:= 和ALL伪目标以及make等操作全部都放到cmake中执行。
这里给出一个通用模板,并且给出了详细注释。
cmake_minimum_required(VERSION 3.0.0)
project(episode LANGUAGES C)
# 1. 查找内核头文件目录
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
find_package(KernelHeaders REQUIRED)
# 2. 将内核头文件目录添加到本次编译的头文件目录,相当于-I
include_directories(${KERNELHEADERS_INCLUDE_DIRS})
# 3. 设置最终的模块文件和目标文件
set(MODULE_FILE "${PROJECT_NAME}.ko")
set(KBUILD_FILE "obj-m := ${PROJECT_NAME}.o")
# 4. 设置某些有特殊要求的目标对象的编译参数,这里是目标文件中有浮点运算,而编译器默认关闭了 msse
set(KBUILD_FILE "${KBUILD_FILE}\nCFLAGS_src/${PROJECT_NAME}.o += -mhard-float -msse -msse2 ")
set(KBUILD_FILE "${KBUILD_FILE}\nCFLAGS_src/episode_space_index.o += -mhard-float -msse -msse2 ")
# 5. 设置模块构建命令,用变量代替
set(KBUILD_CMD $(MAKE) -C ${KERNELHEADERS_DIR} modules M=${CMAKE_CURRENT_BINARY_DIR} src=${CMAKE_CURRENT_SOURCE_DIR})
# 6. 遍历src目录获取所有的.c文件,并将他们添加到sources_absolute列表中,GLOB_RECURSE会遍历匹配src目录的所有文件以及子目录下面的文件
file(GLOB_RECURSE sources_absolute ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
# 7. 针对每一个.c文件,获取其相对路径,放到sources_relative列表中,并将待构建目标文件追加到KBuild文件中,同时在构建目录下创建对应的文件夹用来存放将要生成的目标文件
foreach(file ${sources_absolute})
# 7.1 RELATIVE_PAT推断出指定文件file相对于特定目录的路径,并将该路径赋值给变量${file}
file(RELATIVE_PATH file "${CMAKE_CURRENT_SOURCE_DIR}" "${file}")
# 7.2 提取上一步拿到的变量${file}中的目录,保存到变量file_dir
get_filename_component(file_dir "${file}" DIRECTORY)
# 7.3 提取上一步拿到的变量${file}中的文件名称(不含目录和文件名的后缀),保存到变量file_name_we
get_filename_component(file_name_we "${file}" NAME_WE)
# 7.4 在构建目录下,给上面解析的文件创建对应的目录,用来保存.c生成的.o文件
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${file_dir})
# 7.5 将待构建的目标文件(.o文件)追加到Kbuild文件中
set(KBUILD_FILE "${KBUILD_FILE}\n${PROJECT_NAME}-y += ${file_dir}/${file_name_we}.o")
# 7.6 将源代码文件的相对路径放到sources_relative 列表中
list(APPEND sources_relative "${file_dir}/${file_name_we}.c")
endforeach()
# 8. 追加编译选项到KBuild文件中
set(KBUILD_FILE "${KBUILD_FILE}\nccflags-y := -DMODULE_NAME=\\\"${PROJECT_NAME}\\\"")
# 9. 删除sources_relative列表后面的空格
message(WARNING ${sources_relative})
string(REGEX REPLACE "\n$" "" ${sources_relative} "${sources_relative}")
message(WARNING ${sources_relative})
# 10. 将前面${KBUILD_FILE}缓存的数据写入到Kbuild文件中
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/Kbuild ${KBUILD_FILE})
# 11. 创建一个custom_command命令,用于构建内核模块
add_custom_command(OUTPUT ${MODULE_FILE}
COMMAND ${KBUILD_CMD}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${sources_relative} VERBATIM)
# 12. 创建一个构建目标
add_custom_target(${PROJECT_NAME} ALL DEPENDS ${MODULE_FILE})
需要说明的地方有几个:
1. 这里会遍历src目录下的所有.c文件并将它们分别编译成目标文件,如果有些不想要编译的,可以改名,或者在遍历那块把对应的文件忽略掉;
2. 因为目标文件名称带了src/,所以后面要想对某个目标文件进行设置某些编译参数之类的,需要把src/带上,这里因为有俩目标文件使用了fpu相关计算,需要开启msse等支持。
第二种方法是保留makefile,只是在cmake中使用cmake支持的make命令,设定执行make的目录名称即可。
##############################
### build kernel ####
##############################
SET(SCENE_MODULE_NAME ./episode module/episode.ko)## build kernel module ####
add_custom_target(build_kernel All
COMMAND${CMAKE_MAKE_PROGRAM}&& rm -rf *.o *.mod.c *.symvers *.order *.make *.mod. *.cmd. *.tmp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/episode_module/
COMMENT "Clean kernel module of Episode makefile target")
## make insmod to install module ####
add custom target(insmod COMMAND
COMMAND make insmod
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/episode module/
COMMENT "Oriainal kernel module of Episode makefile target")
#################1############## install module to path ##################################
SET(SCENE MODULE_PATH ${CMAKE_SYSTEM_VERSION})
MESSAGE("The System Version:${SCENE_MODULE_PATH}")
install(PROGRAMSS{SCENE_MODULE_NAME} DESTINATION lib/kernel/${SCENE_MODULE_PATH})
第三种,
cmake_minimum_required(VERSION 3.4.2)
PROJECT(scene_storage)
include(XXX.cmake)
include(./yyy.cmake)
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/episode_module)
##############################
### build kernel ####
##############################
set(KERNEL_DIR /usr/src/linux-headers-${CMAKE_SYSTEM_VERSION})
set(SCENE_MODULE_NAME ${CMAKE_CURRENT_BINARY_DIR}/episode_module/episode.ko)
add_custom_target(episode ALL cp -f ${CMAKE_CURRENT_SOURCE_DIR}/episode_module/*.c ${CMAKE_CURRENT_BINARY_DIR}/episode_module/
COMMAND cp -f ${CMAKE_CURRENT_SOURCE_DIR}/episode_module/*.h ${CMAKE_CURRENT_BINARY_DIR}/episode_module/
COMMAND cp -f ${CMAKE_CURRENT_SOURCE_DIR}/episode_module/Makefile ${CMAKE_CURRENT_BINARY_DIR}/episode_module/
COMMENT "Copy kernel module of Episode makefile target"
COMMAND make -C ${KERNEL_DIR} M=${CMAKE_CURRENT_BINARY_DIR}/episode_module/ modules
COMMENT "Make kernel module of Episode makefile target"
COMMAND rm -rf *.o *.mod.c *.symvers *.order *.makers *.mod .*.cmd .*.tmp
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/episode_module/
COMMENT "Clean kernel module of Episode makefile target"
)
## make insmod to install module ####
add_custom_target(insmod COMMAND
COMMAND make insmod
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/episode_module/
COMMENT "Install kernel module of Episode makefile target")
##############################
## install module to path ####
##############################
SET(SCENE_MODULE_PATH ${CMAKE_SYSTEM_VERSION})
MESSAGE("The System Version:${SCENE_MODULE_PATH}")
install(PROGRAMS ${SCENE_MODULE_NAME} DESTINATION lib/kernel/${SCENE_MODULE_PATH})