使用GLOB指定源文件更好,还是在CMake中单独指定每个文件更好?
CMake提供了几种为目标指定源文件的方法。 一种是使用globbing(),例如:使用GLOB指定源文件更好,还是在CMake中单独指定每个文件更好?,cmake,Cmake,CMake提供了几种为目标指定源文件的方法。 一种是使用globbing(),例如: FILE(GLOB MY_SRCS dir/*) 另一种方法是单独指定每个文件 哪种方式比较好?Globbing似乎很简单,但我听说它有一些缺点。完全披露:我最初更喜欢Globbing方法,因为它简单,但多年来我逐渐认识到,显式列出文件对于大型、多开发人员项目来说不太容易出错 原始答复: 全球化的优势在于: 添加新文件时很容易 仅在一个位置列出:在 磁盘。不全球化会造成 重复 您的CMakeLists.t
FILE(GLOB MY_SRCS dir/*)
另一种方法是单独指定每个文件
哪种方式比较好?Globbing似乎很简单,但我听说它有一些缺点。完全披露:我最初更喜欢Globbing方法,因为它简单,但多年来我逐渐认识到,显式列出文件对于大型、多开发人员项目来说不太容易出错 原始答复:
全球化的优势在于:
- 添加新文件时很容易 仅在一个位置列出:在 磁盘。不全球化会造成 重复
- 您的CMakeLists.txt文件将 更短的这是一个很大的优势,如果你 我有很多文件。不全球化 导致您丢失CMake逻辑 在巨大的文件列表中
- CMake将正确跟踪磁盘上新文件的依赖项-如果我们使用 glob则当您运行CMake时,第一轮未全局化的文件将无法获取 拾起
- 确保只添加所需的文件。地球人可能会捡起流浪汉 您不需要的文件
list(REMOVE_ITEM)
命令清理全局文件列表:
file(GLOB to_remove file_to_remove.cpp)
list(REMOVE_ITEM list ${to_remove})
如果您正在使用类似于在同一构建目录中尝试代码的旧版本的东西,那么这可能会对您造成影响。在这种情况下,您可能需要清理和编译更多的文件,以确保在列表中获得正确的文件。这是一个非常棘手的问题,而且您已经开始注意了,所以这不是一个真正的问题。在CMake中指定源文件的最佳方法是通过明确列出它们 CMake的创建者自己建议不要使用globbing 见: (我们不建议使用GLOB从源代码树中收集源文件列表。如果在添加或删除源时没有更改CMakeLists.txt文件,则生成的生成系统无法知道何时要求CMake重新生成。) 当然,你可能想知道缺点是什么——继续读下去
全球化失败时:
全局绑定的最大缺点是创建/删除文件不会自动更新生成系统 如果您是添加文件的人,这似乎是一个可以接受的折衷方案,但是这会给构建代码的其他人带来问题,他们会从版本控制更新项目,运行build,然后与您联系,抱怨
“构建已损坏”。 更糟糕的是,故障通常会导致一些链接错误,而这些错误不会对问题的原因给出任何提示,排除故障会浪费时间 在我参与的一个项目中,我们开始使用globbing,但当添加新文件时,我们收到了很多抱怨,因此有足够的理由明确列出文件而不是globbing 这也破坏了常见的git工作流程
(
git对分
和功能分支之间的切换)
所以我不能推荐这个,它造成的问题远远超过了便利性,当有人因为这个原因无法构建您的软件时,他们可能会浪费很多时间来跟踪问题,或者干脆放弃
另一个注意事项是,仅仅记住触摸CMakeLists.txt
并不总是足够的,对于使用globbing的自动构建,我必须在每个构建之前运行cmake
,因为自上次构建以来可能已经添加/删除了文件*
该规则的例外情况:
在某些情况下,全球化更可取:
- 用于为不使用CMake的现有项目设置
文件。CMakeLists.txt
这是一种快速获取所有引用源代码的方法(一旦构建系统运行-用显式文件列表替换globbing) - 当CMake未用作主要的生成系统时,例如,如果您正在使用一个不使用CMake的项目,并且您希望为其维护自己的生成系统
- 对于文件列表经常更改以致无法维护的任何情况。在这种情况下,它可能是有用的,但是您必须接受每次运行
生成构建文件,以获得可靠/正确的构建(这与cmake的意图背道而驰,即从构建中分离配置的能力)cmake
*是的,我本可以编写一个代码来比较更新前后磁盘上的文件树,但这不是一个很好的解决方法,最好留给构建系统来处理。您可以安全地全局(可能也应该这样做),而代价是增加一个文件来保存依赖项 在某处添加如下函数:
# Compare the new contents with the existing file, if it exists and is the
# same we don't want to trigger a make by changing its timestamp.
function(update_file path content)
set(old_content "")
if(EXISTS "${path}")
file(READ "${path}" old_content)
endif()
if(NOT old_content STREQUAL content)
file(WRITE "${path}" "${content}")
endif()
endfunction(update_file)
# Creates a file called CMakeDeps.cmake next to your CMakeLists.txt with
# the list of dependencies in it - this file should be treated as part of
# CMakeLists.txt (source controlled, etc.).
function(update_deps_file deps)
set(deps_file "CMakeDeps.cmake")
# Normalize the list so it's the same on every machine
list(REMOVE_DUPLICATES deps)
foreach(dep IN LISTS deps)
file(RELATIVE_PATH rel_dep ${CMAKE_CURRENT_SOURCE_DIR} ${dep})
list(APPEND rel_deps ${rel_dep})
endforeach(dep)
list(SORT rel_deps)
# Update the deps file
set(content "# generated by make process\nset(sources ${rel_deps})\n")
update_file(${deps_file} "${content}")
# Include the file so it's tracked as a generation dependency we don't
# need the content.
include(${deps_file})
endfunction(update_deps_file)
然后去环球旅行:
file(GLOB_RECURSE sources LIST_DIRECTORIES false *.h *.cpp)
update_deps_file("${sources}")
add_executable(test ${sources})
像以前一样,您仍然在处理显式依赖项(并触发所有自动构建!),只是它位于两个文件中而不是一个文件中
过程中唯一的更改是在创建新文件之后。如果您不使用glob,则工作流将从Visual Studio内部修改CMakeLists.txt并重新生成,如果使用glob,则显式运行cmake,或者只需触摸CMakeLists.txt。分别指定每个文件 我使用传统的CMakeLists.txt和python脚本来更新它。我在添加文件后手动运行python脚本 请看我的回答: 在CMake 3.12中,获得的命令取决于
CONFIGURE\u
# Whenever this glob's value changes, cmake will rerun and update the build with the # new/removed files. file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp") add_executable(my_target ${sources})