CMake提供了几种方法来为目标指定源文件。一种是使用通配符(documentation),例如:
FILE(GLOB MY_SRCS dir/*)
另一种方法是分别指定每个文件。
首选哪种方式?窃听似乎很容易,但我听说它有一些缺点。
CMake提供了几种方法来为目标指定源文件。一种是使用通配符(documentation),例如:
FILE(GLOB MY_SRCS dir/*)
另一种方法是分别指定每个文件。
首选哪种方式?窃听似乎很容易,但我听说它有一些缺点。
Answers:
完全公开:我本来是喜欢使用通配方法,因为它简单易行,但多年来,我逐渐认识到,对于大型的,多开发人员的项目,明确列出文件不太容易出错。
原始答案:
遍历的优点是:
添加新文件很容易,因为它们仅在一个位置列出:磁盘上。不阻塞会造成重复。
您的CMakeLists.txt文件将更短。如果您有很多文件,这将是一大优势。不协调会导致您丢失庞大文件列表中的CMake逻辑。
使用硬编码文件列表的优点是:
CMake将正确跟踪磁盘上新文件的依赖关系-如果我们使用glob,则在您运行CMake时第一次没有被glob的文件将不会被拾取
您确保仅添加所需的文件。混淆可能会拾取您不想要的杂散文件。
为了解决第一个问题,您可以简单地“触摸”执行该glob的CMakeLists.txt,方法是使用touch命令或不做任何更改地写入文件。这将迫使CMake重新运行并提取新文件。
要解决第二个问题,您可以将代码仔细地组织到目录中,这可能是您可能要做的。在最坏的情况下,您可以使用以下list(REMOVE_ITEM)
命令来清理文件列表:
file(GLOB to_remove file_to_remove.cpp)
list(REMOVE_ITEM list ${to_remove})
唯一会困扰您的实际情况是,如果您正在使用git-bisect之类的东西在同一build目录中尝试较旧版本的代码。在这种情况下,您可能需要清理和编译多余的内容,以确保列表中包含正确的文件。这是一个极端的情况,而且您已经脚尖了,这并不是一个真正的问题。
Simply "touch" the CMakeLists.txt
如果您是开发人员,那是可以的,但是对于其他构建您的软件的人来说,这确实是一个痛苦的痛点,因为您的构建在更新后会失败,并且负担了他们的调查工作为什么。
在CMake中指定源文件的最佳方法是显式列出它们。
CMake的创建者自己建议不要使用通配符。
请参阅:https://cmake.org/cmake/help/v3.15/command/file.html?highlight = glob#file
(我们不建议您使用GLOB从源代码树中收集源文件列表。如果在添加或删除源文件时CMakeLists.txt文件没有更改,则生成的生成系统将无法确定何时要求CMake重新生成。)
当然,您可能想知道不利之处-继续阅读!
遍历的最大缺点是创建/删除文件不会自动更新构建系统。
如果您是添加文件的人,这似乎是可以接受的折衷方案,但这会给其他人构建代码带来麻烦,他们会从版本控制中更新项目,运行构建,然后与您联系,抱怨
“构建的破碎”。
更糟的是,该故障通常会给出一些链接错误,这些错误不会给问题的原因提供任何提示,并且会浪费时间进行故障排除。
在我从事的一个项目中,我们开始进行通配,但是在添加新文件时收到了很多投诉,因此有足够的理由明确列出文件而不是通配。
这也会破坏常见的git工作流程
(git bisect
以及功能分支之间的切换)。
因此,我不推荐这样做,它所带来的问题远远超过了便利性,当有人因此而无法构建您的软件时,他们可能会浪费大量时间来查找问题或放弃。
另外要注意的是,仅记住触摸CMakeLists.txt
并不总是足够的,对于使用globlob的自动构建,我必须cmake
在每次构建之前运行,因为自上次构建*之后可能已添加/删除了文件。
在某些情况下,最好使用通配符:
CMakeLists.txt
为不使用CMake的现有项目设置文件。cmake
以生成生成文件才能获得可靠/正确的生成(这与CMake的意图背道而驰-将配置从生成中分离出来的能力)。* 是的,我本可以编写一个代码来比较更新前后的磁盘上的文件树,但这并不是一个很好的解决方法,而更好的方法留给了构建系统。
在CMake 3.12中,file(GLOB ...)
和file(GLOB_RECURSE ...)
命令获得了一个CONFIGURE_DEPENDS
选项,如果glob的值更改,该选项将重新运行cmake。因为这是遍历源文件的主要缺点,所以现在可以这样做:
# 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})
但是,有些人仍然建议避免使用源代码。实际上,文档指出:
我们不建议使用GLOB从源代码树中收集源文件列表。...该
CONFIGURE_DEPENDS
标志可能无法在所有生成器上可靠地工作,或者如果将来添加了不支持该标志的新生成器,则使用该标志的项目将被卡住。即使CONFIGURE_DEPENDS
工作可靠,仍然需要对每次重建执行检查。
就个人而言,我认为不必手动管理源文件列表以克服可能的弊端所带来的好处。如果必须切换回手动列出的文件,则只需打印全局源列表并将其粘贴回即可轻松实现。
您可以安全地(可能应该)以附加文件的形式来保存(包括)依赖关系。
在某处添加以下功能:
# 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})
您仍然像以前一样绕着显式依赖项(并触发所有自动构建!),只是它在两个文件中而不是一个文件中。
唯一的过程更改是在创建新文件之后。如果不进行全局设置,则工作流程是从Visual Studio内部修改CMakeLists.txt并进行重建,如果进行全局设置,则显式运行cmake-或仅触摸CMakeLists.txt。
make
给出奇怪的链接器错误的问题。
分别指定每个文件!
我使用常规的CMakeLists.txt和python脚本对其进行更新。添加文件后,我手动运行python脚本。
在这里查看我的答案:https : //stackoverflow.com/a/48318388/3929196