181

CMake 提供了几种方法来指定目标的源文件。一种是使用通配符(文档),例如:

FILE(GLOB MY_SRCS dir/*)

另一种方法是单独指定每个文件。

首选哪种方式?通配似乎很容易,但我听说它有一些缺点。

4

5 回答 5

209

全面披露:我最初更喜欢 globbing 方法,因为它简单,但多年来我已经认识到明确列出文件对于大型、多开发人员项目不太容易出错。

原答案:


globbing 的优点是:

  • 添加新文件很容易,因为它们只列在一个地方:磁盘上。不通配会产生重复。

  • 您的 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之类的东西在同一个构建目录中尝试旧版本的代码。在这种情况下,您可能需要清理和编译更多内容,以确保您在列表中获得正确的文件。这是一个极端的案例,而且你已经在你的脚趾上,这不是一个真正的问题。

于 2009-06-29T19:32:47.880 回答
126

在 CMake 中指定源文件的最佳方式是明确列出它们

CMake 的创建者自己建议不要使用通配符。

见:https ://cmake.org/cmake/help/v3.15/command/file.html?highlight=glob#file

(我们不建议使用 GLOB 从源代码树中收集源文件列表。如果在添加或删除源代码时没有更改 CMakeLists.txt 文件,则生成的构建系统无法知道何时要求 CMake 重新生成。)

当然,您可能想知道缺点是什么 -请继续阅读!


当 Globbing 失败时:

globbing 的最大缺点是创建/删除文件不会自动更新构建系统。

如果您是添加文件的人,这似乎是一个可以接受的权衡,但是这会给构建您的代码的其他人带来问题,他们从版本控制更新项目,运行构建,然后联系您,抱怨
“构建的破碎的”。

更糟糕的是,失败通常会给出一些链接错误,而这些错误并没有给出问题原因的任何提示,并且会浪费时间来解决问题。

在我从事的一个项目中,我们开始使用 globbing,但在添加新文件时收到了很多抱怨,因此明确列出文件而不是 globbing 就足够了。

这也破坏了常见的 git 工作流程
git bisect以及功能分支之间的切换)。

所以我不能推荐这个,它带来的问题远远超过了便利性,当有人因此无法构建你的软件时,他们可能会浪费很多时间来追踪问题或者干脆放弃。

另一个注意事项,仅仅记住触摸CMakeLists.txt并不总是足够的,对于使用 globbing 的自动构建,我必须在每次构建cmake之前运行,因为自上次构建以来可能已经添加/删除了文件*.

规则的例外情况:

有时使用 globbing 更可取:

  • CMakeLists.txt用于为不使用 CMake 的现有项目设置文件。
    它是获取所有引用源的快速方法(一旦构建系统运行 - 用显式文件列表替换 globbing)。
  • 当 CMake 不用作主要构建系统时,例如,如果您正在使用不使用 CMake 的项目,并且您希望为其维护自己的构建系统。
  • 对于文件列表经常更改以至于维护变得不切实际的任何情况。在这种情况下,它可能很有用,但是您必须接受每次cmake运行以生成构建文件以获得可靠/正确的构建(这违背了 CMake 的意图 - 将配置与构建分离的能力)

*是的,我本可以编写一个代码来比较更新前后磁盘上的文件树,但这不是一个很好的解决方法,最好留给构建系统。

于 2013-08-30T17:29:35.373 回答
39

在 CMake 3.12 中,file(GLOB ...)andfile(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工作可靠,每次重建都需要进行检查。

就个人而言,我认为不必手动管理源文件列表的好处超过了可能的缺点。如果您必须切换回手动列出的文件,只需打印全局源列表并将其粘贴回即可轻松实现。

于 2019-03-21T23:07:04.600 回答
10

您可以安全地 glob(并且可能应该)以保存依赖项的附加文件为代价。

在某处添加这样的功能:

# 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)

然后去 globbing:

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。

于 2016-10-11T06:19:00.567 回答
2

分别指定每个文件!

我使用传统的 CMakeLists.txt 和 python 脚本来更新它。添加文件后,我手动运行 python 脚本。

在这里查看我的答案: https ://stackoverflow.com/a/48318388/3929196

于 2018-01-18T10:19:17.310 回答