Shared library in CMake

Hello everyone.

Summary

I am working in a C++20 project that makes use of a shared library. So far, I successfully managed to call the shared library when compiling and running the script, however, when using CMake to do the job, the things are not going that well.

Pre-CMake

I have a code main.cpp with a structure similar to the one below:

extern "C"
{
void c_custom(char *param);
}

int main() {

    char *param = ...

    c_custom(param);

    return 0;
}

The above script has no imports or includes whatsoever.

For compiling, I simply type:

g++ -O3 main.cpp -L/opt/custom/lib -std=c++20 -lcustom -lgfortran -lm -o main

Where the folder /opt/custom/lib contains a file named libcustom.a.

The compilation and execution work perfectly well.

Pos-CMake

For the CMake project, I created the following folder structure:

  • includes
  • src
  • build

With the following CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(Main)

##### Include Dirs

include_directories(SYSTEM includes "${PROJECT_BINARY_DIR}")

##### Compile options

add_compile_options(
    -std=c++20
    # CUSTOM Lib
    -L/opt/custom/lib
    -lcustom
    -lgfortran
    -lm
    )

file(GLOB_RECURSE SOURCES "src/*.cpp")

add_executable(main ${SOURCES})

target_link_libraries(main)

With the following codes:

  • includes/main.hpp:
#ifndef MAIN_HPP

    #define MAIN_HPP

    #ifdef __cplusplus
        extern "C" {
    #endif

            void c_custom(char *param);

    #ifdef __cplusplus
        }
    #endif

#endif
  • src/main.cpp:
#include "main.hpp"

int main() {
    char *param = ...
    c_custom(param);
    return 0;
}

When running the commands:

cd build
cmake ..

I obtain the following output:

-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: project/build

Which seems to be fine. However, when running make, we obtain these:

[ 50%] Building CXX object CMakeFiles/main.dir/src/main.cpp.o
[100%] Linking CXX executable main
/usr/bin/ld: CMakeFiles/main.dir/src/main.cpp.o: in the function `main':
main.cpp:(.text+0x812): reference to `c_custom' not defined
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/main.dir/build.make:97: main] Error 1
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/main.dir/all] Error 2
make: *** [Makefile:91: all] Error 2

I took some hours to play with the commands add_library, set_target_properties, target_include_directories, and even setting the LANGUAGE to C CXX Fortran in the project statement, but none worked.

I would like to know if someone ever did something similar before.

Thanks and regards.

You’re giving linker options as compile options. You should use add_link_options() for those. Better would be to use IMPORTED targets, but this should at least get you a working build.

1 Like

Thank you very much. I tried to follow the recommendation, ending-up with the following script:

cmake_minimum_required(VERSION 3.16)
project(Main)

##### Include Dirs

include_directories(SYSTEM includes "${PROJECT_BINARY_DIR}")

##### Compile options

# CUSTOM Lib
add_link_options(-L/opt/custom/lib)

add_compile_options(
    -std=c++20
    # CUSTOM Lib
    -lcustom
    -lgfortran
    -lm
    )

file(GLOB_RECURSE SOURCES "src/*.cpp")

add_executable(main ${SOURCES})

target_link_libraries(main)

But the same error persists. What do you mean by IMPORTED targets? I am sorry, but could you post some code snippet? I am newbie in CMake.

Regards.

The -l flags are also linker options.

For an IMPORTED target, it’s something like:

add_library(custom STATIC IMPORTED)
set_target_properties(custom PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "/abs/path/to/custom/includes/dir"
  IMPORTED_LOCATION "/opt/custom/lib/libcustom.a"
  INTERFACE_LINK_LIBRARIES "gfortran;m"
  )

You then use it by target_link_libraries(main PRIVATE custom).

1 Like

Thank you very much, it works prefectly well.