Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

cmake - cmake的依赖没有触发(cmake dependency not trigged)

I try to use cmake in my project (actually move from make to cmake). (我尝试在项目中使用cmake(实际上是从make转到cmake)。) I bump into a problem that I can't solve. (我遇到一个我无法解决的问题。) I try to streamline my architecture like this (hope this is not too streamlined). (我试图像这样简化我的架构(希望这不是太简化)。) I use ld -r here as an example custom command that produce files and intermediate files. (我在这里使用ld -r作为示例自定义命令,该命令生成文件和中间文件。)

Here is my file structure. (这是我的文件结构。)

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

The .c files (.c文件)

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

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

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

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

And my CMakeLists.txt files (还有我的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
)

Now the question (现在的问题)

I run the original cmake like this (我像这样运行原始的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

I am happy with that la is created and doing a nm(1) on it shows the v1 global variable. (我对创建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

Now the problem, I touch 1 file l1_f1.c and rebuild (现在的问题,我触摸了1个文件l1_f1.c并重建)

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

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

As we see here la is not rebuild, despite I thought I made a dep for regen it. (正如我们在这里看到的那样,la并没有重建,尽管我以为我已经对其进行了重建。) Obvioulsy I must be wrong, I am new to cmake. (很明显我一定是错的,我是cmake的新手。)

The nm(1) confirm la is not regened. (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

Can someone enlight me how I could get la regened ? (有人能启发我如何重生吗?)

Thanx in advance, cheers. (提前感谢,加油。)


The following is added after Tsyvarev answer proposition. (Tsyvarev命题之后添加以下内容。)

1) Adding file-level dependencies in main CMakeLists.txt prevent initial build. (1)在主CMakeLists.txt中添加文件级依赖项会阻止初始构建。) Note that your initial line DEPENDS libs/l1/l1.o libs/l1/l2.o generate an error as it look file in the source tree instead of the build tree so I changed it to DEPENDS ../build/libs/l1/l1.o ../build/libs/l2/l2.o (请注意,您的第一行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 
)

This give the following output on initial build (这将在初始构建时提供以下输出)

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) rebuilding the project without the file-level dep. (2)重建没有文件级dep的项目。) VY$ cd .. && rm -rf build && mkdir build && cd build && cmake -G Ninja ../libs && ninja (VY $ cd .. && rm -rf生成&& mkdir生成&& cd生成&& cmake -G忍者../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) With this initial build and your file-dep added again in the CMakethen ask for a partial rebuild give this (3)有了这个初始构建,然后在CMake中再次添加了文件dep,然后要求进行部分重新构建)

Initial setup after project build. (项目构建后的初始设置。)

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

So this is v1, touching 1 file to make v2 and rebuild with your file-level dep (这就是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 
)

give this (给这个)

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

As we see here, on first ninja we got a cmake rebuild because I added back the file-level dep. (正如我们在此处看到的,在第一个忍者上,我们进行了一次cmake重建,因为我重新添加了文件级dep。) Then only the l1.o is rebuild not lo then la (那么只有l1.o被重建而不是lo然后la)

Second ninja trig the la rebuild. (第二个忍者触发了la重建。)

It sounds like make basic dependencies actiopn rule is pretty magic with cmake, hope someone could help me here. (听起来像使基本依赖项actiopn规则对于cmake来说是很神奇的,希望有人可以在这里帮助我。)

Cheers. (干杯。)


The following is added after Tsyvarev latest comment. (Tsyvarev最新评论之后添加了以下内容。)

The final fix from Tsyvarev make it working, need the set_source_files_propoerties() (Tsyvarev的最终修复程序使其正常工作,需要set_source_files_propoerties())

The top CMakeLists.txt now look like this (顶部的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
)

And the builds, full and partial, gives the correct answer. (而完整的和部分的构建会给出正确的答案。)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As we see here la is not rebuild, despite I thought I made a dep for regen it. (正如我们在这里看到的那样, la并没有重建,尽管我以为我已经对其进行了重建。)

You don't actually make la dependent from l1.o and other object files. (其实你不使la依赖从l1.o和其他目标文件。)

The line (线)

DEPENDS L1 L2

in the custom command which creates la means only dependencies between the target LA (the target which depends from la ) and the targets L1 and L2 . (在创建la的定制命令中,仅意味着目标 LA (依赖于la目标 )与目标 L1L2之间的依赖关系。) This is described in the documentation for the add_custom_command . (在add_custom_command文档add_custom_command进行了add_custom_command 。) But your custom command itself doesn't have a dependency from l1.o file which you need. (但是您的自定义命令本身没有所需的l1.o 文件依赖l1.o 。)

The dependency between the targets forces only LA target to be built after L1 and L2 . (目标之间的依赖性迫使仅在L1L2之后建立LA目标。)

Correct way is to specify for add_custom_command file -level dependencies. (正确的方法是为add_custom_command 文件级别指定依赖项。)

But, as dependency files are created in other CMakeLists.txt , additional steps are required: (但是,由于在其他 CMakeLists.txt中创建了依赖项文件,因此还需要其他步骤:)

  1. Target -level dependencies. (目标级别的依赖关系。) It involves targets which produces dependency files. (它涉及产生依赖文件的目标。)
  2. Marking the dependency files as GENERATED . (将依赖项文件标记为GENERATED 。) Otherwise CMake would treat these files as already existed on configuration stage. (否则,CMake会将这些文件视为在配置阶段已存在。)

In result: (结果:)

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
)

The rationale behind needs to specify file -level dependencies even if you have target -level ones is that generally CMake has no notion about OUTPUT file for the target. (即使您具有目标级的依赖关系,背后的原理也需要指定文件级的依赖关系,因为通常CMake并没有关于目标的OUTPUT文件的概念。) Such notion exists only for targets create by add_executable or add_library , and in this case DEPENDS from such target actually enriches custom command with both target -level and file -level dependencies. (这种概念仅适用于由add_executableadd_library创建的目标,在这种情况下,来自此类目标的DEPENDS实际上丰富了具有目标级别和文件级别依赖性的自定义命令。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...