【发布时间】: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