【问题标题】:cmake dependency not trigged未触发 cmake 依赖项
【发布时间】:2019-12-01 22:01:27
【问题描述】:

我尝试在我的项目中使用 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

我很高兴创建 l.a 并对其执行 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

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

nm(1) 确认 l.a 没有再生。

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

谁能启发我如何让 l.a 重生?

提前谢谢,干杯。


在 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 被重建,而不是 l.o 然后 l.a.

第二个忍者触发洛杉矶重建。

听起来使基本依赖actiopn规则对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

【问题讨论】:

  • 有什么理由使用自定义目标而不是add_executable/add_library?
  • 看起来您正在尝试手动执行编译/链接步骤,而 CMake 命令为您处理所有这些混乱。我建议花一些时间使用 CMake tutorial。您可以使用add_library()/add_executable(),然后使用target_link_libraries() 进行链接...
  • 我使用这个例子有两个原因,1) 是为了了解 cmake 如何处理依赖关系和触发操作,2) 这个设置符合我在从 make 到 cmake 的大型项目迁移中的一些需求(如果可行)其中许多“有点”子库是通过 ld -r 构建的,而最终的大子库是通过最终的 ld -r 完成的,然后最终 .o 上的 ar(1) 可能看起来很奇怪,但确实存在。我应该准确地说我只在 unix/linux 上工作,没有到 MS/android 的端口。我知道 cmake 很好地处理 cc/ld 但我想要实现的比这更复杂一些。我只需要知道如何触发最终的 ld -r。干杯。

标签: cmake dependencies


【解决方案1】:

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

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

线

DEPENDS L1 L2

在创建l.a 的自定义命令中仅表示目标 LA(依赖于l.a 的目标)和目标 @987654330 之间的依赖关系@ 和 L2。这在documentation 中针对add_custom_command 进行了描述。但是您的自定义命令本身并不依赖于您需要的 l1.o file。

targets 之间的依赖关系只强制在L1 和L2 之后构建LA 目标。

正确的方法是为add_custom_command指定文件级别的依赖关系。

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

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

结果:

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 文件没有概念。这样的概念只存在于由add_executable 或add_library 创建的目标,在这种情况下,来自此类目标的DEPENDS 实际上丰富了具有 target-level 和 file 的自定义命令> 级别的依赖关系。

【讨论】:

  • Thanx Tsyvarev,它没有回答,没有什么比你的回答更好,但你指出了一些关于目标级别与文件级别依赖关系的词汇。像您一样设置文件级 dep 会阻止初始构建工作,然后如果初始构建设置然后后续构建需要 2 忍者运行才能完全重建。可能我不应该将 cmake 用于这种用途?
  • 我将编辑我的问题以添加您提出的更改以及为什么它不起作用
  • 我忘了为依赖文件设置 GENERATED 属性。关于更喜欢对 DEPEND 文件使用绝对路径。现在答案应该可以了。
  • Thanx Tsyvarev,set_source_files_properties() 是我学习曲线中缺失的部分。今天早上我确实玩了一点,但从来没有成功过。我使用此链接收敛到您仍然有错别字的解决方案。我概括了 BIN DIR 的用法,也在 ld -r 中,你的错字是第二个 BIN DIR 是 /l2/l2.o 而不是 /l1/l2.o,并且在 PROP GENED 之后最终类型是 TRUE...我会解决我的问题以反映这一点。我将答案 +1 连同已解决的检查一起退回。再次感谢。
  • 我用来帮助的链接是samthursfield.wordpress.com/2015/11/21/…
猜你喜欢
  • 2017-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多