CMake add_subdirectory并重新编译

时间:2011-12-12 19:45:25

标签: cmake

我刚刚从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
  1. CMakeLists.txt文件配置编译标志,宏定义等,并使用CMake的add_subdirectory()来包含库和演示项目定义的目标。
  2. code子文件夹包含一个子文件夹的平面列表,每个子文件夹包含静态库的源代码(以及在CMakeLists.txt文件中定义的目标)。
  3. demo子文件夹包含一个子文件夹的平面列表。每个包含可执行文件和关联的CMakeLists.txt文件的源代码。
  4. 每个库都是一个独立的组件,独立于所有其他库和演示项目构建。
  5. 每个演示程序都依赖于code子文件夹中的一个或多个不同的库。
  6. 这个设置非常好。如果我想更改构建选项,我只需要修改根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
    )
    

    然后重新编译项目中所有库和演示程序的整个源代码。为什么会这样?有没有更好的方法来构建我的脚本以避免这种情况?

5 个答案:

答案 0 :(得分:4)

您要做的第一件事就是弄清楚会有哪些变化。如果安装了它,可以使用git来帮助你做到这一点。

  1. 使用源代码构建
  2. 在项目上运行cmake
  3. cd to build directory
  4. 从构建树中创建一个git repo git add。 git commit -m“add build tree”
  5. 更改导致重建的cmake文件
  6. 在构建树上重新运行cmake cmake。
  7. 运行git diff并查看更改内容。

答案 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(...)