首页 > 解决方案 > 未触发 cmake 依赖项

问题描述

我尝试在我的项目中使用 cmake(实际上是从 make 转移到 cmake)。我遇到了一个我无法解决的问题。我尝试像这样精简我的架构(希望这不是太精简)。我在这里使用 ld -r 作为生成文件和中间文件的示例自定义命令。

这是我的文件结构。

.
`-- libs
    |-- CMakeLists.txt
    |-- l1
    |   |-- CMakeLists.txt
    |   |-- l1_f1.c
    |   `-- l1_f2.c
    `-- l2
        |-- CMakeLists.txt
        |-- l2_f1.c
        `-- l2_f2.c

.c 文件

./libs/l1/l1_f1.c::
#include <stdio.h>
int v1;
int l1_f1(){ printf("In l1_f1()\n");}

./libs/l1/l1_f2.c::
#include <stdio.h>
int l1_f2(){ printf("In l1_f2()\n");}

./libs/l2/l2_f1.c::
#include <stdio.h>
int l2_f1(){ printf("In l2_f1()\n");}

./libs/l2/l2_f2.c::
#include <stdio.h>
int l2_f2(){ printf("In l2_f2()\n");}

还有我的 CMakeLists.txt 文件

./libs/CMakeLists.txt::
cmake_minimum_required(VERSION 3.10)
project(PL VERSION 1.0)

add_subdirectory(../libs/l1 libs/l1)
add_subdirectory(../libs/l2 libs/l2)

add_custom_command(
OUTPUT l.a
COMMAND ld -r  libs/l1/l1.o  libs/l2/l2.o -o l.o
COMMAND rm -f l.a
COMMAND ar r l.a l.o
COMMAND rm -f l.o
DEPENDS L1 L2
)

add_custom_target(
LA
ALL
DEPENDS l.a
)

./libs/l1/CMakeLists.txt::
add_library(l1 OBJECT l1_f1.c l1_f2.c)

add_custom_command(
OUTPUT l1.o
COMMAND ld -r $<TARGET_OBJECTS:l1> -o l1.o
DEPENDS $<TARGET_OBJECTS:l1>
COMMAND_EXPAND_LISTS
)

add_custom_target(
L1
ALL
DEPENDS l1.o
)

./libs/l2/CMakeLists.txt::
add_library(l2 OBJECT l2_f1.c l2_f2.c)

add_custom_command(
OUTPUT l2.o
COMMAND ld -r $<TARGET_OBJECTS:l2> -o l2.o
DEPENDS $<TARGET_OBJECTS:l2>
COMMAND_EXPAND_LISTS
)

add_custom_target(
L2
ALL
DEPENDS l2.o
)

现在的问题

我像这样运行原始的cmake

mkdir build && cd build && cmake -G Ninja ../libs && ninja

-- The C compiler identification is GNU 7.4.0
-- The CXX compiler identification is GNU 7.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/g++
-- Check for working CXX compiler: /usr/bin/g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/phi/p2/build
[7/7] Generating l.a
ar: creating l.a

我很高兴 la 被创建并在其上执行 nm(1) 显示 v1 全局变量。

nm l.a

l.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
0000000000000013 T l1_f2
0000000000000026 T l2_f1
0000000000000039 T l2_f2
                 U puts
0000000000000004 C v1

现在的问题,我触摸 1 文件 l1_f1.c 并重建

VY$ sed -i 's/v1/v2/' ../libs/l1/l1_f1.c

VY$ ninja
[2/2] Generating l1.o

正如我们在这里看到的,尽管我认为我为 regen 创建了一个 dep,但 la 并没有重建。Obvioulsy我一定是错的,我是cmake的新手。

nm(1) 确认 la 未再生。

VY$ nm l.a libs/l1/l1.o

l.a:

l.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
0000000000000013 T l1_f2
0000000000000026 T l2_f1
0000000000000039 T l2_f2
                 U puts
0000000000000004 C v1

libs/l1/l1.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
0000000000000013 T l1_f2
                 U puts
0000000000000004 C v2

有人可以启发我如何获得再生吗?

提前谢谢,干杯。


以下是在 Tsyvarev 回答命题之后添加的。

1) 在主 CMakeLists.txt 中添加文件级依赖项会阻止初始构建。请注意,您的初始行DEPENDS libs/l1/l1.o libs/l1/l2.o会生成错误,因为它在源树而不是构建树中查找文件,因此我将其更改为DEPENDS ../build/libs/l1/l1.o ../build/libs/l2/l2.o

cmake_minimum_required(VERSION 3.10)
project(PL VERSION 1.0)

add_subdirectory(../libs/l1 libs/l1)
add_subdirectory(../libs/l2 libs/l2)

add_custom_command(
OUTPUT l.a
COMMAND ld -r  libs/l1/l1.o  libs/l2/l2.o -o l.o
COMMAND rm -f l.a
COMMAND ar r l.a l.o
COMMAND rm -f l.o

DEPENDS ../build/libs/l1/l1.o ../build/libs/l2/l2.o
DEPENDS L1 L2  
)

add_custom_target(
LA
ALL
DEPENDS l.a 
)

这在初始构建时给出以下输出

VY$ cd .. && rm -rf build && mkdir build && cd build && cmake -G Ninja ../libs && ninja


-- The C compiler identification is GNU 7.4.0
-- The CXX compiler identification is GNU 7.4.0
... [removed output]
-- Configuring done
-- Generating done
-- Build files have been written to: /home/phi/p2/build
ninja: error: '/home/phi/p2/build/libs/l1/l1.o', needed by 'l.a', missing and no known rule to make it

2)在没有文件级dep的情况下重建项目。VY$ cd .. && rm -rf build && mkdir build && cd build && cmake -G Ninja ../libs && ninja

-- The C compiler identification is GNU 7.4.0
-- The CXX compiler identification is GNU 7.4.0
... [removed output]     
-- Configuring done
-- Generating done
-- Build files have been written to: /home/phi/p2/build
[7/7] Generating l.a
ar: creating l.a

3)有了这个初始构建,并且在 CMake 中再次添加了您的文件-dep,然后要求进行部分重建,给这个

项目构建后的初始设置。

VY$ ninja;nm libs/l1/CMakeFiles/l1.dir/l1_f1.c.o ; nm l.a
ninja: no work to do.
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
                 U puts
0000000000000004 C v1

l.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
0000000000000013 T l1_f2
0000000000000026 T l2_f1
0000000000000039 T l2_f2
                 U puts
0000000000000004 C v1

所以这是 v1,触摸 1 个文件以制作 v2 并使用您的文件级 dep 重建

cmake_minimum_required(VERSION 3.10)
project(PL VERSION 1.0)

add_subdirectory(../libs/l1 libs/l1)
add_subdirectory(../libs/l2 libs/l2)

add_custom_command(
OUTPUT l.a
COMMAND ld -r  libs/l1/l1.o  libs/l2/l2.o -o l.o
COMMAND rm -f l.a
COMMAND ar r l.a l.o
COMMAND rm -f l.o

DEPENDS ../build/libs/l1/l1.o ../build/libs/l2/l2.o
DEPENDS L1 L2  
)

add_custom_target(
LA
ALL
DEPENDS l.a 
)

给这个

VY$ ninja
[0/1] Re-running CMake...
-- Configuring done
-- Generating done
-- Build files have been written to: /home/phi/p2/build
[2/2] Generating l1.o

VY$ nm libs/l1/CMakeFiles/l1.dir/l1_f1.c.o ; nm l.a
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
                 U puts
0000000000000004 C v2

l.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
0000000000000013 T l1_f2
0000000000000026 T l2_f1
0000000000000039 T l2_f2
                 U puts
0000000000000004 C v1

VY$ ninja
[1/1] Generating l.a
ar: creating l.a

VY$ nm libs/l1/CMakeFiles/l1.dir/l1_f1.c.o ; nm l.a
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
                 U puts
0000000000000004 C v2

l.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
0000000000000013 T l1_f2
0000000000000026 T l2_f1
0000000000000039 T l2_f2
                 U puts
0000000000000004 C v2

正如我们在这里看到的,在第一个 ninja 中,我们进行了 cmake 重建,因为我添加了文件级 dep。然后只有 l1.o 是重建而不是 lo 然后 la

第二个忍者触发了重建。

听起来使基本依赖动作规则对 cmake 来说非常神奇,希望有人可以在这里帮助我。

干杯。


以下是在 Tsyvarev 最新评论之后添加的。

Tsyvarev 的最终修复使其工作,需要 set_source_files_propoerties()

顶部的 CMakeLists.txt 现在看起来像这样

cmake_minimum_required(VERSION 3.10)
project(PL VERSION 1.0)

add_subdirectory(../libs/l1 libs/l1)
add_subdirectory(../libs/l2 libs/l2)

add_custom_command(
OUTPUT l.a
COMMAND ld -r ${CMAKE_CURRENT_BINARY_DIR}/libs/l1/l1.o
              ${CMAKE_CURRENT_BINARY_DIR}/libs/l2/l2.o  -o l.o
COMMAND rm -f l.a
COMMAND ar r l.a l.o 
COMMAND rm -f l.o

DEPENDS # Use absolute paths for files for prevent confusion
        ${CMAKE_CURRENT_BINARY_DIR}/libs/l1/l1.o 
        ${CMAKE_CURRENT_BINARY_DIR}/libs/l2/l2.o
        # Target-level dependencies are needed too
        L1 L2
)

set_source_files_properties(
    ${CMAKE_CURRENT_BINARY_DIR}/libs/l1/l1.o 
    ${CMAKE_CURRENT_BINARY_DIR}/libs/l2/l2.o
    PROPERTIES GENERATED TRUE
)

add_custom_target(
LA
ALL
DEPENDS l.a
)

完整和部分的构建给出了正确的答案。

VY$ cd .. && rm -rf build && mkdir build && cd build && cmake -G Ninja ../libs && ninja


-- The C compiler identification is GNU 7.4.0
-- The CXX compiler identification is GNU 7.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/g++
-- Check for working CXX compiler: /usr/bin/g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/phi/p2/build
[7/7] Generating l.a
ar: creating l.a

VY$ ninja;nm libs/l1/CMakeFiles/l1.dir/l1_f1.c.o ; nm l.a
ninja: no work to do.
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
                 U puts
0000000000000004 C v1

l.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
0000000000000013 T l1_f2
0000000000000026 T l2_f1
0000000000000039 T l2_f2
                 U puts
0000000000000004 C v1

VY$ sed -i 's/v1/v2/' ../libs/l1/l1_f1.c

VY$ ninja;nm libs/l1/CMakeFiles/l1.dir/l1_f1.c.o ; nm l.a
[3/3] Generating l.a
ar: creating l.a
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
                 U puts
0000000000000004 C v2

l.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T l1_f1
0000000000000013 T l1_f2
0000000000000026 T l2_f1
0000000000000039 T l2_f2
                 U puts
0000000000000004 C v2

标签: cmakedependencies

解决方案


正如我们在这里看到的,l.a不是重建,尽管我认为我为 regen 做了一个 dep。

您实际上并不l.a依赖于l1.o和其他目标文件。

线

DEPENDS L1 L2

在创建的自定义命令中,l.a仅表示目标 LA(依赖于的目标l.a)与目标 L1L2. 这在. _ add_custom_command但是您的自定义命令本身并不依赖于您需要的l1.o 文件。

目标之间的依赖关系仅强制目标在和LA之后构建。L1L2

正确的方法是指定add_custom_command 文件级依赖项。

但是,由于依赖文件是在other CMakeLists.txt中创建的,因此需要额外的步骤:

  1. 目标级别的依赖关系。它涉及生成依赖文件的目标。
  2. 将依赖文件标记为GENERATED. 否则 CMake 会将这些文件视为在配置阶段已经存在。

结果:

add_custom_command(
    OUTPUT l.a
    COMMAND ld -r  libs/l1/l1.o  libs/l2/l2.o -o l.o
    COMMAND rm -f l.a
    COMMAND ar r l.a l.o
    COMMAND rm -f l.o
    DEPENDS
        # Use absolute paths for files for prevent confusion
        ${CMAKE_CURRENT_BINARY_DIR}/libs/l1/l1.o 
        ${CMAKE_CURRENT_BINARY_DIR}/libs/l2/l2.o
        # Target-level dependencies are needed too
        L1 L2
)

set_source_files_properties(
    ${CMAKE_CURRENT_BINARY_DIR}/libs/l1/l1.o 
    ${CMAKE_CURRENT_BINARY_DIR}/libs/l2/l2.o
    PROPERTIES GENERATED TRUE
)

即使您有目标级别的依赖项,其背后的基本原理也需要指定文件级别的依赖项,因为通常 CMake 对目标的 OUTPUT 文件没有概念。这样的概念只存在于由or创建的目标,在这种情况下,来自这样的目标实际上丰富了具有目标级别和文件级别依赖关系的自定义命令。add_executableadd_libraryDEPENDS


推荐阅读