将具有循环链接依赖的makefile转换为CMake

Posted

技术标签:

【中文标题】将具有循环链接依赖的makefile转换为CMake【英文标题】:Converting makefile with circular link dependency to CMake 【发布时间】:2016-08-02 10:20:09 【问题描述】:

我有多个实现(.c 或 .cpp 文件),它们实现了同一个头文件的不同功能。我有用于构建的 makefile 方法,我想用 cmake 重新实现它。(请注意,我不能更改实现文件或头文件。如果可以,我不会按原样设计和实现解决方案。)我准备了更小的案例来指出我的问题。

head.h:

#ifndef __HEAD__
#define __HEAD__

int foo(float e, float b);
void boo(float *eo, float *eh);
void setup();
int load(int* n);

#endif

impl1.c:

#include "head.h"

void boo(float *eo, float *eh)

  *eo = 0.4f;
  *eh = 2.3f;

impl2.c:

#include <stdio.h>
#include "head.h"
void setup()

  printf("hello!\n");
  int m = 13;
  int* n = &m;
  load(n);

impl3.c:

#include "head.h"
int load(int* n)

  n = 0;
  return 0;

main.cpp:

#include "head.h"
int main()

  setup();
  return 0;

int foo(float e, float b)

  float i, j;
  boo(&i, &j);
  return 4;

makefile:(这是构建和链接)

# C compiler
CC = g++
CC_FLAGS = -g -O2

main: impl1.o impl2.o impl3.o
    $(CC) $(CC_FLAGS) main.cpp impl1.o impl2.o impl3.o -o main

%.o: %.[ch]
    $(CC) $(CC_FLAGS) $< -c

impl1.o: impl1.c
    $(CC) $(CC_FLAGS) impl1.c -c

impl2.o: impl2.c
    $(CC) $(CC_FLAGS) impl2.c -c

impl3.o: impl3.c
    $(CC) $(CC_FLAGS) impl3.c -c

clean:
    rm -f *.o *~ main *.linkinfo

使用 makefile,执行以下命令:

g++ -g -O2 impl1.c -c
g++ -g -O2 impl2.c -c
g++ -g -O2 impl3.c -c
g++ -g -O2 main.cpp impl1.o impl2.o impl3.o -o main

到目前为止我对 cmake 的尝试:(您可以按照评论观察我的失败尝试)

add_executable($PROJECT_NAME impl1.c impl2.c impl3.c main.cpp)

# add_library(impl1 OBJECT impl1.c)
# add_library(impl2 OBJECT impl2.c)
# add_library(impl3 OBJECT impl3.c)
# add_executable($PROJECT_NAME main.cpp $<TARGET_OBJECTS:impl3> $<TARGET_OBJECTS:impl2> $<TARGET_OBJECTS:impl1>)

# add_library(impl1 STATIC impl1.c)
# add_library(impl2 STATIC impl2.c)
# add_library(impl3 STATIC impl3.c)
# add_executable(main main.cpp)
#
# set(LIBS impl1 impl2 impl3)
#
# target_link_libraries(main $LIBS $LIBS)

# add_library(headextra impl1.c impl2.c impl3.c)
# add_executable($PROJECT_NAME main.cpp)
# target_link_libraries($PROJECT_NAME headextra)

# add_library(headextra impl1.c impl2.c impl3.c main.cpp)
# add_executable($PROJECT_NAME $<TARGET_OBJECTS:headextra>)

此 cmake 文件成功生成构建并编译。但是在链接时我遇到了无法解决的错误:

make -f CMakeFiles/cmake_test.dir/build.make CMakeFiles/cmake_test.dir/build
/usr/bin/cc    -o CMakeFiles/cmake_test.dir/impl1.c.o   -c /home/ahmet/Desktop/cmake_test/impl1.c
/usr/bin/cc    -o CMakeFiles/cmake_test.dir/impl2.c.o   -c /home/ahmet/Desktop/cmake_test/impl2.c
/usr/bin/cc    -o CMakeFiles/cmake_test.dir/impl3.c.o   -c /home/ahmet/Desktop/cmake_test/impl3.c
/usr/bin/c++     -o CMakeFiles/cmake_test.dir/main.cpp.o -c /home/ahmet/Desktop/cmake_test/main.cpp
/usr/bin/cmake -E cmake_link_script CMakeFiles/cmake_test.dir/link.txt --verbose=1
/usr/bin/c++      CMakeFiles/cmake_test.dir/impl1.c.o CMakeFiles/cmake_test.dir/impl2.c.o CMakeFiles/cmake_test.dir/impl3.c.o CMakeFiles/cmake_test.dir/main.cpp.o  -o cmake_test -rdynamic 
CMakeFiles/cmake_test.dir/main.cpp.o: In function `main':
main.cpp:(.text+0x5): undefined reference to `setup()'
CMakeFiles/cmake_test.dir/main.cpp.o: In function `foo(float, float)':
main.cpp:(.text+0x31): undefined reference to `boo(float*, float*)'
collect2: error: ld returned 1 exit status
make[2]: *** [cmake_test] Error 1
make[1]: *** [CMakeFiles/cmake_test.dir/all] Error 2
make: *** [all] Error 

我肯定错过了关于 cmake 构建生成的一些关键点。希望有人能提供帮助。

【问题讨论】:

【参考方案1】:

即使在旧的 Makefile 中也不应该工作,因为您正在混合 C 和 C++。这个问题实际上与您的构建系统无关,只是与 C 和 C++ 的混合有关。

为了解决它,将头文件更改为:

#ifndef HEAD_H
#define HEAD_H

#ifdef __cplusplus
extern "C" 
#endif

int foo(float e, float b);
void boo(float *eo, float *eh);
void setup();
int load(int* n);

#ifdef __cplusplus

#endif

#endif

最大的变化是在头文件中我添加了对宏 __cplusplus 的检查,该宏将由 C++ 编译器而不是 C 编译器定义。在定义__cplusplus 时,我已经告诉编译器将函数声明视为“C”声明,这意味着不会为函数执行name mangling

问题在于 C++ 编译器修改它的函数名,以启用函数重载。这意味着例如符号编译为 C++ 时,函数 setup 有所不同。 extern "C" 告诉 C++ 编译器不要破坏名称。

参见例如this reference 了解更多关于 extern "C" 的信息。

还请注意,我更改了标头包含保护,因为所有具有双前导下划线的符号都为所有范围内的“实现”(即编译器和标准库)保留。参见例如this old question and answer.

【讨论】:

我认为它起作用了,因为他定义了CC=g++,以便使用 C++ 编译器来编译 C 代码。 感谢您的明确答复。有效。还有关于双前导下划线的建议,我忘记了。

以上是关于将具有循环链接依赖的makefile转换为CMake的主要内容,如果未能解决你的问题,请参考以下文章

CLion+CMake编写C++程序

Makefile 循环依赖被删除

将 Makefile 转换为 CMakeLists.txt

依赖树中的 Makefile 循环

Makefile循环依赖错误

将makefile转换为其相关的makefile [关闭]