Converting makefile with circular link dependency to CMake

Vemulo

I have multiple implementations(.c or .cpp files) that implements different function of the same header file. I have the makefile approach for build and I want to reimplement it with cmake.(Note that I cannot change the implementation files or headers. If I could, I wouldn't design and implement the solution as it is.) I have prepared smaller case to point out my issue.

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:(this is building and linking)

# 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

With the makefile, these commands are executed:

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

What I have tried with cmake up to now:(you can observe my failed attempts as commented)

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>)

This cmake file successfully generates the build and also compiles. But when linking I got errors that I couldn't resolve:

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 

I am definitely missing some key point about cmake build generation. Hopefully, some can help.

Some programmer dude

That should not work even in the old Makefile, since you are mixing C and C++. The problem is actually unrelated to your build-system and have to do just with the mixing of C and C++.

To solve it change the header files as:

#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

The big change is that in the header file I have added a check for the macro __cplusplus which will be defined by a C++ compiler and not a C compiler. When __cplusplus is defined, I have told the compiler to treat the function declarations as "C" declarations, which means there will be no name mangling done for the functions.

The problem is that the C++ compiler mangles its function names, to enable function overloading. And that means the symbol for e.g. the function setup is something different when compiling as C++. The extern "C" tells the C++ compiler to not mangle the names.

See e.g. this reference for more information about extern "C".

Also note that I changed the header include guards, as all symbols with double leading underscores are reserved for the "implementation" (i.e. the compiler and standard library) in all scopes. See e.g. this old question and answer.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Java

Microservice Clients Circular Dependency

From Dev

Cocoapods circular dependency issue

From Dev

GWT GIN circular dependency

From Dev

AngularJS circular dependency

From Dev

ClojureScript circular dependency

From Dev

Converting 3NF to BCNF when there is a circular dependency

From Dev

makefile - dependency of dependency

From Dev

Is circular dependency good or bad

From Dev

Circular dependency in the class constructor

From Dev

Solve a Circular Dependency

From Dev

Circular dependency on nested concern

From Dev

Simple Makefile reporting circular dependency -- possibly from suffix rules?

From Dev

Design: Class circular dependency?

From Dev

Django circular dependency

From Dev

cmake link error - order dependency or something else?

From Dev

Handle circular dependency in CDI

From Dev

makefile library dependencies - resolve circular dependency

From Dev

Circular dependency in pandas dataframe

From Dev

SQL Circular Dependency

From Dev

SQLAlchemy circular dependency on delete

From Dev

Makefile Pattern rule: Circular makefile.o <- makefile dependency dropped

From Dev

How to link against PIC version of a library's dependency in CMake?

From Dev

Converting 3NF to BCNF when there is a circular dependency

From Dev

makefile - dependency of dependency

From Dev

Why does cmake not add a library link command in the makefile?

From Dev

Makefile Pattern rule: Circular makefile.o <- makefile dependency dropped

From Dev

Circular dependency when wiring a dependency

From Dev

Converting Makefile to CMake using Flex and Bison

From Dev

Link error in converting qmake to cmake