未定义的引用,因为我找不到相应的源文件

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

我有一个程序中的 cpp 文件,我想将其与整个文件结构分开打开。我需要这样做才能在 ros 中使用这个 cpp 文件。我包含了 header 文件,但如果我是正确的,我还需要包含源文件。

我的 cpp 文件名为 open_camera.cpp 并包含一个 header 文件 /usr/include/ids_peak-1.3.0/peak/backend/peak_backend.h

peak_backend.h 文件包含这样的声明:

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

我的 Cmake 文件如下所示:

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)

如果我 运行 catkin_make 我得到如下错误:

/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'

据我了解,问题是我需要 link header 的源文件。我该怎么做,我在哪里可以找到我的 header 的源文件?我找了一段时间没找到

open_camera.cpp 有自己的 CMake 文件,如下所示:

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)

我不太了解原始 cmake 文件,因为我对这个话题还很陌生。

cpp文件路径为:/usr/local/src/ids/samples/peak/cpp/open_camera/open_camera.cpp

根据 CMake 文档,我们不应再对包含目录等使用全局设置,而应使用 target_ 版本。老实说,我不认为第二个复杂的 CMakeLists.txt 被使用(或需要),它似乎不包含在第一个中(但如果不知道目录结构。

但是,无论如何,如果你想使用一些库,你需要两样东西:带有所提供项目声明的头文件和通常包含 definition/implementation 的已编译库项目(静态或动态库)。原则上,如果您可以访问库源文件,您也可以自己编译库。在这种情况下,我的建议是:

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)

这将定义两个要编译的目标:

  • 一个库目标,编译列表 ${ID_PEAK_SOURCE_FILES} 中的任何源文件并附加相应的包含目录
  • 一个可执行目标,正在编译您的 open_camera.cpp 源文件。附有这个 catkin 包含目录(也许我们应该在这里选择其他库目标?是否有编译源或是否只有 lib+headers?)。最后但并非最不重要的是,向该目标添加了一个依赖项。

由于库目标的包含目录被声明为 public,它们被转发到依赖于它的所有目标(target_compile_definitionstarget_link_librariestarget_link_options, 等等).

您可能会对这些链接感兴趣:

而且,如果您愿意挂断 FILE(GLOB...) 电话。有一次,一些 CMake 贡献者告诉我,这个功能没有发布,但是被逃脱了,根本不应该使用,因为它很容易出错。我知道它会派上用场,但您无法真正控制您的构建真正在做什么。最好明确命名文件。或者,在安装 (https://cmake.org/cmake/help/latest/command/install.html#directory) 或复制的情况下,您可以应用到整个目录。