function(_get_path_relation path1 path2 result) # 初始值设置为 0,表示没有包含关系 set(${result} 0 PARENT_SCOPE) # 判断 path2 是否包含 path1 string(FIND "${path2}" "${path1}" _idx) if(_idx GREATER -1) # 如果 path2 包含 path1,设置 result 为 2(path2 是父路径) set(${result} 1 PARENT_SCOPE) else() # 如果没有包含关系,检查 path1 是否包含 path2 string(FIND "${path1}" "${path2}" _idx) if(_idx GREATER -1) # 如果 path1 包含 path2,设置 result 为 1(path1 是父路径) set(${result} 2 PARENT_SCOPE) endif() endif() endfunction() # 写成宏是为了能直接修改 list_var 所代表的变量 macro(_handle_path_add list_var path) # 创建一个列表用于记录需要删除的路径 set(need_remove_list) set(need_add 1) # 遍历列表中的路径,检查与新路径的包含关系 foreach(li ${${list_var}}) set(relation 0) # 获取路径之间的包含关系 _get_path_relation(${path} ${li} relation) if (relation EQUAL 2) # 如果新路径是父路径,跳过添加 #message("${path} 被 ${li} 包含,跳过添加") set(need_add 0) break() elseif (relation EQUAL 1) # 如果新路径包含旧路径,记录旧路径需要移除 list(APPEND need_remove_list ${li}) #message(" ${li} 被 ${path} 包含,移除旧的路径 ${li}") endif() endforeach() # 删除已标记的路径 foreach(remove_path ${need_remove_list}) list(REMOVE_ITEM ${list_var} ${remove_path}) #message("已从 ${list_var} 中删除路径:${remove_path}") endforeach() # 如果需要添加新路径,将其添加到列表 if (need_add) list(APPEND ${list_var} ${path}) #message("将路径 ${path} 添加到 ${list_var}") endif() endmacro() function(get_common_path list_var out_var) # 空列表直接返回空 list(LENGTH ${list_var} _path_count) if(_path_count EQUAL 0) set(${out_var} "" PARENT_SCOPE) return() endif() # 取第一个路径作为初始公共路径 list(GET ${list_var} 0 _first_path) string(REPLACE "\\" "/" _first_path "${_first_path}") # 检查路径是否以 / 开头 set(_has_leading_slash 0) if("${_first_path}" MATCHES "^/.*") set(_has_leading_slash 1) endif() # 将路径拆分为部分 string(REPLACE "/" ";" _common_parts "${_first_path}") # 遍历所有路径 foreach(_path IN LISTS ${list_var}) string(REPLACE "\\" "/" _path_norm "${_path}") string(REPLACE "/" ";" _cur_parts "${_path_norm}") # 取两个列表的较小长度,避免越界 list(LENGTH _common_parts _len1) list(LENGTH _cur_parts _len2) if(_len1 LESS _len2) set(_min_len ${_len1}) else() set(_min_len ${_len2}) endif() # 没有可比部分,直接公共路径为空 if(_min_len EQUAL 0) set(_common_parts "") break() endif() math(EXPR _stop "${_min_len} - 1") set(_new_common_parts "") foreach(_idx RANGE 0 ${_stop}) list(GET _common_parts ${_idx} _part1) list(GET _cur_parts ${_idx} _part2) if(_part1 STREQUAL _part2) list(APPEND _new_common_parts "${_part1}") else() break() endif() endforeach() set(_common_parts ${_new_common_parts}) # 已经没有公共部分了,提前结束 list(LENGTH _common_parts _common_len) if(_common_len EQUAL 0) break() endif() endforeach() # 拼回路径 if(_common_parts) list(JOIN _common_parts "/" _common_path) else() set(_common_path "") endif() # 如果原路径有前导 '/', 在拼接回路径时添加它 if(_has_leading_slash) set(_common_path "/${_common_path}") endif() set(${out_var} "${_common_path}" PARENT_SCOPE) endfunction() # 目前是远程开发所以 无需把源码复制一遍 扫描到公共目录直接使用即可 function(_create_linux_driver_proj_ex driver_name _target_list_var_name_) # 此函数用于安装源码 同时创建 component 也可加入到cpack 安装包里 __check_none_extr_params(${ARGC} "${ARGN}" "${ARGV}") __bfs_traverse_targets(_dependency_list_ ${_target_list_var_name_} _relys_) set(_top_level_dir_list) # 计算出公共路径,以及要复制的文件 foreach (_rely_ ${_relys_}) get_property(_attach_include_list_ TARGET ${_rely_} PROPERTY _attach_include_list_) foreach (_rely_dir_ ${_attach_include_list_}) get_filename_component(_base_name_ "${_rely_dir_}" NAME) _handle_path_add(_top_level_dir_list ${_rely_dir_}) endforeach() get_property(_attach_source_list_ TARGET ${_rely_} PROPERTY _attach_source_list_) foreach (_rely_dir_ ${_attach_source_list_}) _handle_path_add(_top_level_dir_list ${_rely_dir_}) endforeach() get_property(_attach_file_list_ TARGET ${_rely_} PROPERTY _attach_file_list_) foreach (_rely_file_ ${_attach_file_list_}) _handle_path_add(_top_level_dir_list ${_rely_file_}) endforeach() endforeach() message("_top_level_dir_list: ${_top_level_dir_list}") # 调用函数计算公共路径 set(common_path) get_common_path(_top_level_dir_list common_path) # 输出结果 message("公共路径: ${common_path}") set(Makefile "# 基础模块指令\n") foreach (_rely_ ${_relys_}) set(_rely_file_list_) get_property(_attach_source_list_ TARGET ${_rely_} PROPERTY _attach_source_list_) foreach (_rely_dir_ ${_attach_source_list_}) file(GLOB_RECURSE _cur_rely_file_list_ "${_rely_dir_}/*.c" "${_rely_dir_}/*.cpp" ) list(APPEND _rely_file_list_ ${_cur_rely_file_list_}) endforeach() get_property(_attach_file_list_ TARGET ${_rely_} PROPERTY _attach_file_list_) list(APPEND _rely_file_list_ ${_attach_file_list_}) #message("_rely_ 【${_rely_}】 attach_source_list:【${_attach_source_list_}】 attach_file_list【${_attach_file_list_}】 ") set(rely_objs "objs_${_rely_} :=") set(new_prefix "") foreach (_rely_file_ ${_rely_file_list_}) file(RELATIVE_PATH _rel_path "${common_path}" "${_rely_file_}") get_filename_component(_dir "${_rel_path}" DIRECTORY) get_filename_component(_name "${_rel_path}" NAME_WLE) if(_dir STREQUAL "") set(_new_path "${new_prefix}${_name}.o") else() set(_new_path "${new_prefix}${_dir}/${_name}.o") endif() # message("src = ${_rely_file_}") # message("dst = ${_new_path}") #message(" ${_new_path}") string(APPEND rely_objs " ${_new_path}") endforeach() string(APPEND Makefile "${rely_objs}\n") endforeach() string(APPEND Makefile "\n# 内核模块组合指令\n") foreach(target ${${_target_list_var_name_}}) set(_temp_list_ ${target}) set(_cur_relys_) # 获取单个目标的relys __bfs_traverse_targets(_dependency_list_ _temp_list_ _cur_relys_) set(target_name "ko_${target}") string(APPEND Makefile "obj-m += ${target_name}.o\n") set(cur_target "${target_name}-y := ") foreach(cur_rely ${_cur_relys_}) string(APPEND cur_target " $(objs_${cur_rely})") endforeach() message("${target} cur_relys ${_cur_relys_}") string(APPEND Makefile "${cur_target}\n") endforeach() string(APPEND Makefile "\n# 综合指令") string(APPEND Makefile " KDIR ?= /lib/modules/$(shell uname -r)/build MKFILE_DIR := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))) OUT := $(MKFILE_DIR)/out all: mkdir -p $(OUT) $(MAKE) -C $(KDIR) M=$(MKFILE_DIR) MO=$(OUT) V=1 modules clean: $(MAKE) -C $(KDIR) M=$(MKFILE_DIR) MO=$(OUT) clean rm -rf $(OUT) compdb: all $(MAKE) -C $(KDIR) M=$(MKFILE_DIR) MO=$(OUT) compile_commands.json ") add_custom_target(${driver_name}_all COMMAND ${CMAKE_MAKE_PROGRAM} -C "${common_path}" all WORKING_DIRECTORY "${common_path}" COMMENT "Build linux driver ${driver_name}" VERBATIM ) add_custom_target(${driver_name}_clean COMMAND ${CMAKE_MAKE_PROGRAM} -C "${common_path}" clean WORKING_DIRECTORY "${common_path}" COMMENT "Clean linux driver ${driver_name}" VERBATIM ) add_custom_target(${driver_name}_compdb COMMAND ${CMAKE_MAKE_PROGRAM} -C "${common_path}" compdb WORKING_DIRECTORY "${common_path}" COMMENT "Generate compile_commands.json for ${driver_name}" VERBATIM ) #要生成 compile_commands.json 来生成对应的cmake选项从而cmake有语法提示 #message("【\n${Makefile}】") #message("Makefile安装在 ${install_prefix}") #file(WRITE "${install_prefix}" "${Makefile}") message("Makefile安装在公共路径下 ${common_path}") file(WRITE "${common_path}/Makefile" "${Makefile}") endfunction() function(_create_linux_driver_proj driver_name) set(_temp_list_ "${ARGN}") _create_linux_driver_proj_ex(${driver_name} _temp_list_) endfunction() # 【内核驱动调试接口与使用方法入门】 https://zhuanlan.zhihu.com/p/1922942186057635216 # 【Linux内核kgdb实战:内核源码级调试工具】 https://zhuanlan.zhihu.com/p/1962870670770835660