我如何使用 cmake link 将两个库添加到我的程序中?

How do I link two libraries to my program using cmake?

我想用 c++ 编写一个程序,它使用两个库 libgphoto2 和 opencv3.0。因此,我想包含一个自己编写的头文件, 包含一些使用 libgphoto2 库中定义的函数的函数,到我导入 opencv 的主要源代码中。第二个头文件只定义了一些常量,没有使用外部库

这两个库都已在我的 macbook 上正常安装。我可以使用 cmake 编写和编译使用它们中的任何一个的工作程序,但我真的不知道 cmake 的语法以解决上述问题...... 顺便说一句:find_package 适用于两个库!

文件夹结构如下:

./camera_cv
./camera_cv/CMakeLists.txt
./camera_cv/src
./camera_cv/src/takeShowAdjust.cpp
./camera_cv/src/mh_camera_control.hpp
./camera_cv/src/cv_supplement.hpp

我的 CMakeLists.txt 文件:

cmake_minimum_required(VERSION 2.8)
project( opencvTutorial )

SET(CMAKE_CXX_FLAGS_DEBUG "-g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "-Ofast -DNDEBUG -march=native")

SET( CMAKE_BUILD_TYPE DEBUG)

find_package( Gphoto2 REQUIRED )
find_package( OpenCV 3.0.0 REQUIRED )

include_directories( ${GPHOTO2_INCLUDE_DIR} ${OpenCV_INCLUDE_DIRS} )
add_executable( TakeShowAdjust src/takeShowAdjust.cpp src/cv_supplement.hpp src/mh_camera_control.hpp)
target_link_libraries( TakeShowAdjust ${LIBGPHOTO2_LIBRARIES} ${OpenCV_LIBS} -lm )

# --- Install ---

SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
INSTALL(TARGETS 
    TakeShowAdjust
    DESTINATION ${BINDIR})

输入 $ cmake 。写入构建文件没有任何错误。 但之后,如果我输入 $ make ,我会收到以下错误:

$ make
Scanning dependencies of target TakeShowAdjust
[ 50%] Building CXX object CMakeFiles/TakeShowAdjust.dir/src/takeShowAdjust.cpp.o
[100%] Linking CXX executable TakeShowAdjust
Undefined symbols for architecture x86_64:
  "_gp_camera_capture", referenced from:
      capture(char const*) in takeShowAdjust.cpp.o
  "_gp_camera_file_delete", referenced from:
      capture(char const*) in takeShowAdjust.cpp.o
  "_gp_camera_file_get", referenced from:
      capture(char const*) in takeShowAdjust.cpp.o
  "_gp_camera_free", referenced from:
      initialize_camera() in takeShowAdjust.cpp.o
  "_gp_camera_get_config", referenced from:
      get_config_value_string(char const*, char**, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
      set_config_value_string(char const*, char const*, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
  "_gp_camera_init", referenced from:
      initialize_camera() in takeShowAdjust.cpp.o
  "_gp_camera_new", referenced from:
      initialize_camera() in takeShowAdjust.cpp.o
  "_gp_camera_set_config", referenced from:
      set_config_value_string(char const*, char const*, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
  "_gp_camera_unref", referenced from:
      close_camera() in takeShowAdjust.cpp.o
      _main in takeShowAdjust.cpp.o
  "_gp_camera_wait_for_event", referenced from:
      capture(char const*) in takeShowAdjust.cpp.o
  "_gp_context_new", referenced from:
      initialize_camera() in takeShowAdjust.cpp.o
  "_gp_context_set_error_func", referenced from:
      initialize_camera() in takeShowAdjust.cpp.o
  "_gp_context_set_message_func", referenced from:
      initialize_camera() in takeShowAdjust.cpp.o
  "_gp_context_unref", referenced from:
      close_camera() in takeShowAdjust.cpp.o
      _main in takeShowAdjust.cpp.o
  "_gp_file_free", referenced from:
      capture(char const*) in takeShowAdjust.cpp.o
  "_gp_file_new_from_fd", referenced from:
      capture(char const*) in takeShowAdjust.cpp.o
  "_gp_widget_free", referenced from:
      get_config_value_string(char const*, char**, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
      set_config_value_string(char const*, char const*, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
  "_gp_widget_get_child_by_label", referenced from:
      get_config_value_string(char const*, char**, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
      set_config_value_string(char const*, char const*, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
  "_gp_widget_get_child_by_name", referenced from:
      get_config_value_string(char const*, char**, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
      set_config_value_string(char const*, char const*, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
  "_gp_widget_get_type", referenced from:
      get_config_value_string(char const*, char**, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
      set_config_value_string(char const*, char const*, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
  "_gp_widget_get_value", referenced from:
      get_config_value_string(char const*, char**, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
  "_gp_widget_set_value", referenced from:
      set_config_value_string(char const*, char const*, _GPContext*, _Camera*) in takeShowAdjust.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [TakeShowAdjust] Error 1
make[1]: *** [CMakeFiles/TakeShowAdjust.dir/all] Error 2
make: *** [all] Error 2

源代码:

takeShowAdjust.cpp:

#include <vector>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cstdarg>
#include <fcntl.h>
#include <opencv2/opencv.hpp>

#include "../src/cv_supplement.hpp"
#include "../src/mh_camera_control.hpp"

#define killall_PTPCamera system("killall PTPCamera");

using namespace cv;
using namespace std;

int main(int argc, char *argv[]) {
    // Load Camera
    initialize_camera();
    // take picture
    char filename[256];
    int i = 1;
    snprintf(filename, 256, "shot-%04d.jpg", i++);
    printf("Capturing to file %s\n", filename);
    capture(filename);


    // load image   
    Mat img = imread(filename, CV_LOAD_IMAGE_COLOR);

    // show image
    namedWindow("original", WINDOW_AUTOSIZE);
    moveWindow("original", POS_00);
    imshow("original", img);
    waitKey(0);

    close_camera();
    return 0;
}

mh_camera_control.hpp:

#ifndef MH_CAMERA_CONTROL
#define MH_CAMERA_CONTROL

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cstdarg>
#include <fcntl.h>
#include <gphoto2/gphoto2.h>

#define killall_PTPCamera system("killall PTPCamera");

Camera *camera;
GPContext *context;

int initialize_camera(void);
void close_camera(void);
int capture (const char *filename);
static int _lookup_widget(CameraWidget*widget, const char *key, CameraWidget **child);
int get_config_value_string (const char *key, char **str, GPContext *contxt = context, Camera *cam = camera);
int set_config_value_string (const char *key, const char *val, GPContext *contxt = context, Camera *cam = camera);
void error_func(GPContext *context, const char *format, va_list args, void *data);
void message_func(GPContext *context, const char *format, va_list args, void *data);


int initialize_camera(void){
    killall_PTPCamera;
    gp_camera_new(&camera);
    context = gp_context_new();

    gp_context_set_error_func(context, (GPContextErrorFunc)error_func, NULL);
    gp_context_set_message_func(context, (GPContextStatusFunc)message_func, NULL);

    int ret = gp_camera_init(camera, context);
    if(ret < GP_OK){
        printf("No camera auto detected.\n");
        gp_camera_free(camera);
        return 1;
    }   
    return 0;
}

void close_camera(void){
    gp_camera_unref(camera);
    gp_context_unref(context);
}

int capture (const char *filename){
    int fd, retval;
    CameraFile *file;
    CameraFilePath camera_file_path;

    //take a shot
    retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
    if (retval){
        // do some error handling (return from function??)
    }
    printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);
    fd = open(filename, O_CREAT | O_WRONLY, 0644);
    // create new CameraFile object from a file descriptor
    retval = gp_file_new_from_fd(&file, fd);
    if (retval){
        // error handling
    }

    // copy picture from camera
    retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name, GP_FILE_TYPE_NORMAL, file, context);
    if (retval){
        // error handling
    }

    // remove picture from camera memory
    printf("Deleting\n");
    retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name, context);
    if (retval){
        // error handling
    }

    // free CameraFile object
    gp_file_free(file);

    // wait till camera is no longer busy, could be done more efficiently (outside this fuction for ex.)
    int waittime = 2000;
    CameraEventType type;
    void *data;
    printf("Wait for events from camera\n");
    while(1){
        retval = gp_camera_wait_for_event(camera, waittime, &type, &data, context);
        if(type == GP_EVENT_TIMEOUT){
            break;
        }
        else if(type != GP_EVENT_CAPTURE_COMPLETE){
            if (type != GP_EVENT_UNKNOWN){
                printf("Unexpected event received from camera: %d\n", (int)type);
            }
        }
        else{
            break;
        }
    }
    return 0;
}


/*
 * This function looks up a label or key entry of
 * a configuration widget.
 * The functions descend recursively, so you can just
 * specify the last component.
 */
static int
_lookup_widget(CameraWidget*widget, const char *key, CameraWidget **child) {
    int ret;
    ret = gp_widget_get_child_by_name (widget, key, child);
    if (ret < GP_OK)
        ret = gp_widget_get_child_by_label (widget, key, child);
    return ret;
}

/* Gets a string configuration value.
 * This can be:
 *  - A Text widget
 *  - The current selection of a Radio Button choice
 *  - The current selection of a Menu choice
 *
 * Sample (for Canons eg):
 *   get_config_value_string (camera, "owner", &ownerstr, context);
 */
int
get_config_value_string (const char *key, char **str, GPContext *contxt, Camera *cam) {
    CameraWidget        *widget = NULL, *child = NULL;
    CameraWidgetType    type;
    int         ret;
    char            *val;

    ret = gp_camera_get_config (cam, &widget, contxt);
    if (ret < GP_OK) {
        fprintf (stderr, "camera_get_config failed: %d\n", ret);
        return ret;
    }
    ret = _lookup_widget (widget, key, &child);
    if (ret < GP_OK) {
        fprintf (stderr, "lookup widget failed: %d\n", ret);
        goto out;
    }

    /* This type check is optional, if you know what type the label
     * has already. If you are not sure, better check. */
    ret = gp_widget_get_type (child, &type);
    if (ret < GP_OK) {
        fprintf (stderr, "widget get type failed: %d\n", ret);
        goto out;
    }
    switch (type) {
        case GP_WIDGET_MENU:
        case GP_WIDGET_RADIO:
        case GP_WIDGET_TEXT:
        break;
    default:
        fprintf (stderr, "widget has bad type %d\n", type);
        ret = GP_ERROR_BAD_PARAMETERS;
        goto out;
    }

    /* This is the actual query call. Note that we just
     * a pointer reference to the string, not a copy... */
    ret = gp_widget_get_value (child, &val);
    if (ret < GP_OK) {
        fprintf (stderr, "could not query widget value: %d\n", ret);
        goto out;
    }
    /* Create a new copy for our caller. */
    *str = strdup (val);
out:
    gp_widget_free (widget);
    return ret;
}


/* Sets a string configuration value.
 * This can set for:
 *  - A Text widget
 *  - The current selection of a Radio Button choice
 *  - The current selection of a Menu choice
 *
 * Sample (for Canons eg):
 *   get_config_value_string (camera, "owner", &ownerstr, context);
 */
int
set_config_value_string (const char *key, const char *val, GPContext *contxt, Camera *cam) {
    CameraWidget        *widget = NULL, *child = NULL;
    CameraWidgetType    type;
    int         ret;

    ret = gp_camera_get_config (cam, &widget, contxt);
    if (ret < GP_OK) {
        fprintf (stderr, "camera_get_config failed: %d\n", ret);
        return ret;
    }
    ret = _lookup_widget (widget, key, &child);
    if (ret < GP_OK) {
        fprintf (stderr, "lookup widget failed: %d\n", ret);
        goto out;
    }

    /* This type check is optional, if you know what type the label
     * has already. If you are not sure, better check. */
    ret = gp_widget_get_type (child, &type);
    if (ret < GP_OK) {
        fprintf (stderr, "widget get type failed: %d\n", ret);
        goto out;
    }
    switch (type) {
        case GP_WIDGET_MENU:
        case GP_WIDGET_RADIO:
        case GP_WIDGET_TEXT:
        break;
    default:
        fprintf (stderr, "widget has bad type %d\n", type);
        ret = GP_ERROR_BAD_PARAMETERS;
        goto out;
    }

    /* This is the actual set call. Note that we keep
     * ownership of the string and have to free it if necessary.
     */
    ret = gp_widget_set_value (child, val);
    if (ret < GP_OK) {
        fprintf (stderr, "could not set widget value: %d\n", ret);
        goto out;
    }
    /* This stores it on the camera again */
    ret = gp_camera_set_config (cam, widget, contxt);
    if (ret < GP_OK) {
        fprintf (stderr, "camera_set_config failed: %d\n", ret);
        return ret;
    }
out:
    gp_widget_free (widget);
    return ret;
}

void error_func(GPContext *context, const char *format, va_list args, void *data){
    fprintf (stderr, "*** Contexterror ***\n");
    //fprintf(stderr, format);
    vfprintf(stderr, format, args);
    fprintf (stderr, "\n");
}

void message_func(GPContext *context, const char *format, va_list args, void *data){
    vprintf(format, args);
    //fprintf(stderr, format);
    printf("\n");
}

#endif

cv_supplement.hpp:

#ifndef CV_SUPPLEMENT
#define CV_SUPPLEMENT

#define HEADER_SIZE 23
#define POS_00 0*img.cols,0
#define POS_01 1*img.cols,0
#define POS_02 2*img.cols,0
#define POS_03 3*img.cols,0
#define POS_10 0*img.cols, 1*img.rows+2*HEADER_SIZE
#define POS_11 1*img.cols, 1*img.rows+2*HEADER_SIZE
#define POS_12 2*img.cols, 1*img.rows+2*HEADER_SIZE
#define POS_13 3*img.cols, 1*img.rows+2*HEADER_SIZE
#define POS_20 0*img.cols, 2*img.rows+3*HEADER_SIZE
#define POS_21 1*img.cols, 2*img.rows+3*HEADER_SIZE
#define POS_22 2*img.cols, 2*img.rows+3*HEADER_SIZE
#define POS_23 3*img.cols, 2*img.rows+3*HEADER_SIZE 

#endif

那么,我如何正确 link 这两个库?我做错了什么,或者我错过了什么?

非常感谢您!

我发现问题出在损坏的 FindGphoto2.cmake 文件上,我必须先下载并包含该文件,以便 cmake 找到 gphoto2 库的路径。 在此文件中,常量 GPHOTO2_INCLUDE_DIR 和 LIBGPHOTO2_LIBRARIES 未正确设置,因此无法链接库 gphoto2。