0

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

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
kolja
  • 3
  • 1
  • 2
    In the first `CMakeLists.txt` you set include directories but I don't see you linking to any libraries. – drescherjm Jan 17 '22 at 15:58
  • @drescherjm thanks for the answer! I added the line `link_directories(/usr/include/ids_peak-1.3.0)` but that didnt fix the problem. I saw a lot about the `target_link_libraries` command in other threads. Do i need that aswell? If so, what would be the correct syntax for it? I know that i need a target which would be my `${PROJECT_NAME}` but i need the libary locations aswell no? – kolja Jan 17 '22 at 18:42
  • link_directories is not the solution. `target_link_libraries()` probably is what you need however I don't know catkin so it's difficult to help. A quick search turns up links like this: [https://github.com/ATLFlight/ros-examples/blob/master/CMakeLists.txt](https://github.com/ATLFlight/ros-examples/blob/master/CMakeLists.txt) – drescherjm Jan 17 '22 at 18:45
  • thanks for the effort. I think to include the libraries i need the source path of the files and i cant find them sadly. In the original Code they add them with via: target_link_libraries (${SAMPLE_TARGET_NAME} ids_peak ${CMAKE_THREAD_LIBS_INIT}) – kolja Jan 17 '22 at 19:39

1 Answers1

0

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:

  • a library target, compiling any source files which are in the list ${ID_PEAK_SOURCE_FILES} and with the corresponding include directories attached
  • an executable target, compiling your open_camera.cpp source file. There is this catkin include directory attached (perhaps we should opt for an other library target here? Are there sources to compile or is there only a lib+headers?). Last but not least a dependency is added to this target.

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.