我刚刚从Visual Studio解决方案迁移到一个大型项目到CMake,我发现了一个奇怪的行为。我有类似以下结构:
project/CMakeLists.txt
project/code/CMakeLists.txt
project/code/library-1/CMakeLists.txt
project/code/library-1/*.hpp
project/code/library-1/*.cpp
project/code/library-2/CMakeLists.txt
project/code/library-2/*.hpp
project/code/library-2/*.cpp
...
project/code/library-n/CMakeLists.txt
project/code/library-n/*.hpp
project/code/library-n/*.cpp
project/demo/CMakeLists.txt
project/demo/demo-1/CMakeLists.txt
project/demo/demo-1/*.hpp
project/demo/demo-1/*.cpp
project/demo/demo-2/CMakeLists.txt
project/demo/demo-2/*.hpp
project/demo/demo-2/*.cpp
...
project/demo/demo-n/CMakeLists.txt
project/demo/demo-n/*.hpp
project/demo/demo-n/*.cpp
CMakeLists.txt
文件配置编译标志,宏定义等,并使用CMake的add_subdirectory()
来包含库和演示项目定义的目标。code
子文件夹包含一个子文件夹的平面列表,每个子文件夹包含静态库的源代码(以及在CMakeLists.txt
文件中定义的目标)。demo
子文件夹包含一个子文件夹的平面列表。每个包含可执行文件和关联的CMakeLists.txt
文件的源代码。code
子文件夹中的一个或多个不同的库。这个设置非常好。如果我想更改构建选项,我只需要修改根CMakeLists.txt
,并且所有内容都会使用新设置重新编译。如果我在树中的任何地方修改任何源代码,则会重新编译相应的库(如果有的话),并且还会重新构建所有相关的演示程序。
但是,如果我在树中的任何位置修改任何 CMakeLists.txt
文件,则会重新编译整个库和程序树,而不考虑依赖性。为了说明我的意思,这里是CMake构建脚本的一些部分。
project/demo/CMakeLists.txt
# Resolve libraries built in `code` sub-folder.
link_directories(${LIBRARY_OUTPUT_PATH})
set(demo-projects
demo-1
demo-2
...
demo-n
)
foreach(demo-project ${demo-projects})
add_subdirectory(${demo-project})
endforeach()
project/demo/demo-n/CMakeLists.txt
# Find all source code in the same folder.
file(GLOB ${demo-project}_headers
${CMAKE_CURRENT_SOURCE_DIR}/*.hpp
)
file(GLOB ${demo-project}_sources
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)
# Select libraries to link with.
set(${demo-project}_libraries
library-1
library-2
library-5
)
# Build the demo program.
add_executable(${demo-project}
${${demo-project}_headers}
${${demo-project}_sources}
)
if(${demo-project}_libraries)
target_link_libraries(${demo-project} ${${demo-project}_libraries})
endif()
# Manually register some dependencies on other targets.
if(${demo-project}_dependencies)
add_dependencies(${demo-project} ${${demo-project}_dependencies})
endif()
如果我碰巧修改project/demo/demo-n/CMakeLists.txt
以添加额外的库,如下所示:
set(${demo-project}_libraries
library-1
library-2
library-5
library-6
)
然后重新编译项目中所有库和演示程序的整个源代码。为什么会这样?有没有更好的方法来构建我的脚本以避免这种情况?
答案 0 :(得分:4)
您要做的第一件事就是弄清楚会有哪些变化。如果安装了它,可以使用git来帮助你做到这一点。
答案 1 :(得分:2)
确实,如果任何CMakeLists.txt文件发生更改(或对其进行任何输入,例如configure_file调用的“source”文件),则CMake将在构建树的顶层重新运行,并重新生成已更改的解决方案文件和项目文件。
但是,它应该只重新生成与上次运行时不同的文件...所以根据你在问题中显示的内容,我没有很好的解释为什么一切都在重建。
另一方面,CMake确实将其留给Visual Studio来决定在触发“构建解决方案”时重新构建的内容。除了将源和头文件放在正确的项目中,正确设置包含目录,并且信任VS以在头文件和源文件发生更改时正确分析包含和重建内容时,我们不表示VS的任何依赖关系。
您不会显示任何include_directories调用。您是否在顶级CMakeLists.txt文件中创建了这些文件,以便所有子目录具有相同的包含值集?如果是这样,也许那就是触发重建一切的东西。
我们当然尽最大努力使CMake生产构建系统,以最大限度地减少重建时间。
您的项目是否公开?我可以看到它的完整源代码并尝试在我自己的机器上重现问题吗?
答案 2 :(得分:2)
碰巧我的问题是由一个完全不相关的问题引起的。我应用了Bill Hoffman's suggestion并修改了任何" CMakeLists.txt"项目中的文件最终修改了所有生成的Makefile中的CXX_FLAGS
(C ++编译器标志)变量。
我追溯到了我的根源" CMakeLists.txt"文件,其中包含以下内容:
if(MSVC)
# ...
set(CMAKE_CXX_FLAGS_DEBUG
"${CMAKE_CXX_FLAGS_DEBUG} /WX /wd4355" // depends on cached value.
CACHE STRING "Debug compiler flags" FORCE)
# ...
endif()
我将其更改为以下内容。
if(MSVC)
# ...
set(CMAKE_CXX_FLAGS_DEBUG
"/DWIN32 /D_WINDOWS /WX /wd4355" // no longer depends on cached value.
CACHE STRING "Debug compiler flags" FORCE)
# ...
endif()
更新构建脚本时,CMake不再重复/WX /wd4355
标志,并且我的项目在每次修改时都不再从头开始重新编译!
答案 3 :(得分:0)
我不熟悉CMake生成Visual Studio解决方案/项目文件的方式,但我猜它正在为整个项目生成一个(设置?)。由于CMake的工作方式的性质(我找不到这方面的好文档;也许是“掌握CMake”一书?),如果子目录项目文件不是分开的,它会在CMakeLists.txt文件的任何时候重新生成它们改变了。然后,Visual Studio将负责重建整个事情(我猜它会在看到项目文件发生变化时重建它)。
documentation for add_subdirectory确实说
指定源目录中的CMakeLists.txt文件将由CMake立即处理,然后在当前输入文件中继续处理此命令之后。
这似乎表明所有子目录都作为主项目的一部分进行处理。
相同的文档提到在子目录中使用project
命令。它在谈论你何时使用EXCLUDE_FROM_ALL
论证,但似乎可能(值得一试)它无论如何都可能对你的情况有所帮助。例如,如果CMake将演示代码生成为自己的项目,则当演示项目文件发生更改时,主代码将无法重建。
我认为,要获得明确的答案,您可能需要在CMake mailing list上发帖(如果您得到一个好的答案,请在此处发布)。
答案 4 :(得分:0)
您好,您是否尝试删除
link_directories(${LIBRARY_OUTPUT_PATH})
来自的声明
项目/演示/的CMakeLists.txt
我不明白为什么这是必要的。 您要链接的所有目标都应该由 target_link_libraries(...)