c++cmakeheader-filescatkin

undefined reference because I can't find the according source files


I have a cpp file from a program that I want to open separately from the whole file structure. I need to do that in order to use this cpp file in ros. I have the header files included but I need to include the source files as well if I am correct.

my cpp file is called open_camera.cpp and includes a header file /usr/include/ids_peak-1.3.0/peak/backend/peak_backend.h

the peak_backend.h file contains declarations like this:

PEAK_C_API PEAK_Library_GetLastError(
    PEAK_RETURN_CODE* lastErrorCode, char* lastErrorDescription, size_t* lastErrorDescriptionSize);

My Cmake File looks like this:

cmake_minimum_required(VERSION 3.0.2)
project(ros_package)

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
)

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES ros_package
#  CATKIN_DEPENDS roscpp rospy std_msgs
#  DEPENDS system_lib
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)


add_executable(open_camera_node src/open_camera.cpp)

#############
## Install ##
#############

include_directories(/usr/include/ids_peak-1.3.0)

if I run catkin_make I get errors like:

/usr/bin/ld: CMakeFiles/open_camera_node.dir/src/open_camera.cpp.o: in function `void peak::core::ExecuteAndMapReturnCodes<(anonymous namespace)::CallAndCheckCInterfaceFunction(std::function<int ()> const&)::{lambda()#1}>((anonymous namespace)::CallAndCheckCInterfaceFunction(std::function<int ()> const&)::{lambda()#1} const&)':
open_camera.cpp:(.text+0x516): undefined reference to `PEAK_Library_GetLastError'

From my understanding the problem is that I need to link the source files for the header. How can I do that and where do I find the source files for my headers? I searched for quiet some time but could not locate them.

The open_camera.cpp has its own CMake file looking like this:

cmake_minimum_required(VERSION 3.2 FATAL_ERROR)

project ("open_camera_cpp")

message (STATUS "[${PROJECT_NAME}] Processing ${CMAKE_CURRENT_LIST_FILE}")

set (SAMPLE_TARGET_NAME ${PROJECT_NAME})
set (CMAKE_SCRIPTS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../_cmake_scripts" CACHE STRING "The path of the cmake scripts directory.")
set (SAMPLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/output/bin")

include (${CMAKE_SCRIPTS_PATH}/cmake_detect_architecture.cmake)
detect_target_architecture (ARCH)

add_executable (${SAMPLE_TARGET_NAME}
    open_camera.cpp
)

set (LIBRARY_NAME_VISION_API "ids_peak")
string (TOUPPER ${LIBRARY_NAME_VISION_API} LIBRARY_NAME_UPPER_VISION_API)

if (NOT TARGET ids_peak)
    file (TO_CMAKE_PATH "$ENV{IDS_PEAK_SDK_PATH}/api" ${LIBRARY_NAME_UPPER_VISION_API}_PACKAGE_DIR)

    set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${${LIBRARY_NAME_UPPER_VISION_API}_PACKAGE_DIR}/cmake_finder")
    message (STATUS "[${PROJECT_NAME}] Will find IDS peak API library.. CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}")
    find_package (ids_peak REQUIRED)
endif ()

target_include_directories (${SAMPLE_TARGET_NAME}
    PRIVATE ${${LIBRARY_NAME_UPPER_VISION_API}_INCLUDE_DIR}
)

find_package (Threads REQUIRED)

target_link_libraries (${SAMPLE_TARGET_NAME}
    ids_peak
    ${CMAKE_THREAD_LIBS_INIT}
)

if ((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
    target_link_libraries (${SAMPLE_TARGET_NAME}
        atomic
    )
endif ()

# Set output directories for all configuration types (Debug, Release, etc.)
if (NOT CMAKE_BUILD_TYPE)
    set (CMAKE_BUILD_TYPE "Debug")
endif()
if (NOT CMAKE_CONFIGURATION_TYPES)
    set (CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE})
endif ()
if (CMAKE_CONFIGURATION_TYPES)
    foreach (CONFIGURATION_TYPE ${CMAKE_CONFIGURATION_TYPES})
        string (TOUPPER ${CONFIGURATION_TYPE} CONFIGURATION_TYPE_UPPER)
        if (CONFIGURATION_TYPE_UPPER STREQUAL "RELEASE")
            set (SAMPLE_RUNTIME_OUTPUT_NAME ${SAMPLE_TARGET_NAME})
            set (SAMPLE_RUNTIME_OUTPUT_DIRECTORY ${SAMPLE_OUTPUT_PATH}/${ARCH})
        else ()
            string (TOLOWER ${CONFIGURATION_TYPE} CONFIGURATION_TYPE_LOWER)
            set (SAMPLE_RUNTIME_OUTPUT_NAME "${SAMPLE_TARGET_NAME}_${CONFIGURATION_TYPE_LOWER}")
            set (SAMPLE_RUNTIME_OUTPUT_DIRECTORY ${SAMPLE_OUTPUT_PATH}/${ARCH}/${CONFIGURATION_TYPE})
        endif ()
        set_target_properties (${SAMPLE_TARGET_NAME} PROPERTIES
            RUNTIME_OUTPUT_NAME_${CONFIGURATION_TYPE_UPPER} ${SAMPLE_RUNTIME_OUTPUT_NAME}
            RUNTIME_OUTPUT_DIRECTORY_${CONFIGURATION_TYPE_UPPER} ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}
        )
        message (STATUS "[${PROJECT_NAME}] Cfg ${CONFIGURATION_TYPE} -> Output directory: ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}, Output name: ${SAMPLE_RUNTIME_OUTPUT_NAME}")
    endforeach ()
endif ()

set_target_properties(${SAMPLE_TARGET_NAME} PROPERTIES
    CXX_STANDARD 14
    CXX_STANDARD_REQUIRED ON
    CXX_EXTENSIONS NO
)

if (MSVC)
    target_compile_options (${SAMPLE_TARGET_NAME}
        PRIVATE "/bigobj"
        PRIVATE "/MP"
    )
endif ()

GET_PROPERTY(${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED_LOCAL GLOBAL PROPERTY ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED)
if(NOT ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED_LOCAL)
    file (GLOB ids_peak_LIBS
        "${${LIBRARY_NAME_UPPER_VISION_API}_LIBRARY_DIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}"
    )
    foreach (ids_peak_LIBRARY ${ids_peak_LIBS})
        message (STATUS "[${PROJECT_NAME}] Add PostBuildStep for copy of ${ids_peak_LIBRARY}.")
        add_custom_command (TARGET ${SAMPLE_TARGET_NAME} POST_BUILD
            COMMAND ${CMAKE_COMMAND} -E copy_if_different
            ${ids_peak_LIBRARY}
            $<TARGET_FILE_DIR:${SAMPLE_TARGET_NAME}>
            COMMENT "Post build copy of ${ids_peak_LIBRARY} to output dir." VERBATIM
        )
    endforeach ()
    SET_PROPERTY(GLOBAL PROPERTY ${LIBRARY_NAME_UPPER_VISION_API}_LIBRARIES_COPIED ON)
endif()

# For Unix Build we need the environment variable GENICAM_GENTL32_PATH respectivily GENICAM_GENTL64_PATH to find the GenTL producer libraries.
# To set these environment variables a shell script is used which can be found in the samples root folder in _cmake_scripts.
# To run the samples run this script not the binary.
if (UNIX)
    string (TOLOWER ${CMAKE_BUILD_TYPE} CONFIGURATION_TYPE_LOWER)
    if(${CONFIGURATION_TYPE_LOWER} STREQUAL "release")
        set(VSSL_SAMPLE_BINARY_NAME ${PROJECT_NAME})
    else()
        set(VSSL_SAMPLE_BINARY_NAME ${PROJECT_NAME}_${CONFIGURATION_TYPE_LOWER})
    endif()
    configure_file(${CMAKE_SCRIPTS_PATH}/sample_starter.in ${CMAKE_CURRENT_BINARY_DIR}/${VSSL_SAMPLE_BINARY_NAME}.sh)
    file(COPY ${CMAKE_CURRENT_BINARY_DIR}/${VSSL_SAMPLE_BINARY_NAME}.sh
        DESTINATION ${SAMPLE_RUNTIME_OUTPUT_DIRECTORY}
        FILE_PERMISSIONS
            OWNER_READ OWNER_WRITE OWNER_EXECUTE
            GROUP_READ GROUP_EXECUTE
            WORLD_READ WORLD_EXECUTE
    )
endif (UNIX)

I don't understand much of the original cmake file since I am quiet new to the topic.

The path of the cpp file is: /usr/local/src/ids/samples/peak/cpp/open_camera/open_camera.cpp


Solution

  • According to CMake documentation we shouldn't use the global settings for include directories or such anymore, but use the target_ versions. And to be honest, I don't think, that the second, complicated CMakeLists.txt is used (or needed), it doesn't seem to be included with the first one (but it is hard to say without knowing the directory structure.

    But, never the less, if you want to use some library, you need two things: the header file(s) with the declaration of the provided items and usually the compiled library containing the definition/implementation of the items (static or dynamic library). In principle, you can also compile the library on your own, if you have access to the libraries source files. In this case my suggestion would be:

    add_library(ids_peak
       ${IDS_PEAK_SOURCE_FILES}
    )
    target_include_directories(ids_peak PUBLIC /usr/include/ids_peak-1.3.0)
    ...
    add_executable(open_camera 
        src/open_camera.cpp
    )
    target_include_directories(open_camera PRIVATE ${catkin_INCLUDE_DIRS})
    target_link_libraries(open_camera PRIVATE ids_peak)
    

    This will define two targets to compile:

    Since the include directories of the library target are declared public, they are forwarded to all targets, that depend on it (same happens with target_compile_definitions, target_link_libraries, target_link_options, etc.).

    These links could be of interest to you:

    And, if you'd be so kind as to drop the FILE(GLOB...) call. I was told by some CMake contributor once, that this feature wasn't released, but escaped and shouldn't be used at all being pretty error prone. I know it comes in handy, but you can't really control, what your build is really doing. It is better to name the files explicitly. Or, in case of install (https://cmake.org/cmake/help/latest/command/install.html#directory) or copy, you can apply to whole directories.