【问题标题】:cmake generated header causes double buildcmake生成的标头导致双重构建
【发布时间】:2021-02-20 21:10:37
【问题描述】:

一个非常简单的场景 - 我需要生成头文件,包含它,如果生成的头文件在 cmake 构建时更新,所有依赖的 cpp 单元也必须重新构建。

一个简化的例子:

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/test.h
    COMMAND ...
    DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/test.in"

add_custom_target(test_target DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/test.h)

add_executable(test_exe
    ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
add_dependencies(test_exe test_target)

main.cpp 只是:

#include "test.h"

int main()
{
    return 0;
}

当我进行完全重建时,一切都很好。但是当我更改test.in 时,只运行cmake.exe --build . --target all 只重新生成test.h,但不会重新编译main.cpp。但是当我再次(第二次)运行cmake.exe --build . --target all 时,main.cpp 被重新编译,test_exe 被重新链接。

我做错了什么?

附:如果我明确使用OBJECT_DEPENDS,则没有问题,重建工作正常,但文档说不再需要 - https://cmake.org/cmake/help/v3.20/prop_sf/OBJECT_DEPENDS.html

更新: 我使用 Windows 10、CMake 3.19.2 和 Ninja 1.10.2

解决方案: 使用项目目录中的构建目录,则没有问题。但是如果构建目录在我们的项目目录之外,那就有问题了。

【问题讨论】:

  • 不相关,但是可以直接在add_executable命令中添加目标,不需要add_dependencies
  • 我预见到有关使用add_custom_targetadd_dependencies 的反应,因此决定使用它们。我尝试了所有可能的变体(没有显式依赖,使用静态库)。唯一有效的变体是设置 OBJECT_DEPENDS,但 IMO 它已经过时且不方便。
  • add_dependencies 只给出目标之间的ordering。要使自定义命令生成的标头上的可执行文件依赖,请将该标头添加到add_executable 调用:add_executable(test_exe ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test.h)
  • @Tsyvarev test.h 包含在 main.cpp 中。但是我试过了,还是不行。
  • 可能需要使用什么 CMake 版本、平台和生成器。在您的示例中添加COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/test.in" ${CMAKE_CURRENT_SOURCE_DIR}/test.h 以及cmake_minimum_requiredproject 将在我的Windows 机器上重新编译main.cpp。使用 msbuild 和 Ninja 的 CMake 版本 3.18.2。您是否碰巧合并了多个 CMakeLists.txt 文件以尽量减少问题?

标签: c++ cmake ninja


【解决方案1】:

作为答案发布以显示完整示例

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)

project(example)

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/test.h
    COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/test.in" ${CMAKE_CURRENT_SOURCE_DIR}/test.h
    DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/test.in")

add_custom_target(test_target DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/test.h)

add_executable(test_exe
    ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
add_dependencies(test_exe test_target)

main.cpp

#include "stdio.h"
#include "test.h"

int main()
{
    printf("The value is %d\n", FOO);
    return 0;
}

test.in

#define FOO 4

这适用于使用 CMake 3.19.2 和 Ninja 1.10.2 的 Windows,可以在初始构建后更改 test.in 中的 FOO 定义,并查看生成的可执行文件被重建并查看其值已更改。

用于测试的命令

$ cmake -B build -G Ninja
-- The C compiler identification is Clang 11.0.0 with GNU-like command-line
-- The CXX compiler identification is Clang 11.0.0 with GNU-like command-line
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/LLVM/bin/clang.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/LLVM/bin/clang++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done                            
-- Build files have been written to: <some_path>
                                                      
$ cmake --build build                                 
[3/3] Linking CXX executable test_exe.exe             
                                                         
$ ./build/test_exe.exe                                
The value is 4                                        
                                                          
$ echo "#define FOO 3" > test.in                      
                                                      
$ cmake --build build                                 
[3/3] Linking CXX executable test_exe.exe             
                                                          
$ ./build/test_exe.exe                                
The value is 3    

【讨论】:

  • 我好像找到了原因。如果我从控制台调用cmake -B build -G Ninja,一切都会按预期工作,并且与您的示例完全相同,但是如果我从 QtCreator 生成它,则行为会有所不同——正如我在问题中描述的那样。会挖进去。谢谢!
  • 哇!如果您使用项目目录之外的构建目录,即cmake -B ..\build -G Ninja,那么就会出现问题,但如果我们在项目内部使用构建目录,则没有问题。这是记录在案的行为吗?太奇怪了。
  • >如果您使用项目目录之外的构建目录,即cmake -B ..\build -G Ninja,那么就会出现问题,我无法重现该问题:( 它似乎在内部或外部处理相同的增量项目目录。
猜你喜欢
  • 1970-01-01
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多