5

I am attempting to use LibUSB in a project. However whenever I attempt to use basic libUSB functions I get the following error:

...src/main/main.cpp.o: In function `main':
...src/main/main.cpp:10: undefined reference to `libusb_init'
...src/main/main.cpp:11: undefined reference to `libusb_set_debug'
collect2: error: ld returned 1 exit status

The package LibUSB-devel is installed (I'm on fedora 22) and my IDE KDevelop finds and recognises the headers, to the point it offers LibUSB code completions once you have added the import statement. I don't have any custom include lines in either my IDE or CMake (my build system) so I would like to know what I need to to to make CMake find the LibUSB headers.

This is the contents of main.cpp, just in case I messed something up:

#include <iostream>
#include <libusb-1.0/libusb.h>

int main(int argc, char **argv) {
      libusb_init(NULL);
      libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING);

      /*snip*/

      std::cout << "Hello, world! PTPID="  << std::endl;
      return 0;
}

The following are the CMakeLists.txt:
../

cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_BUILD_TYPE Debug)

project(aProjectThatHasHadIt'sNameObcured)
add_subdirectory(src)

.../src/cmakelists.txt just adds subdirectories

.../src/main/

add_executable(main main.cpp)
usr1234567
  • 21,601
  • 16
  • 108
  • 128
Segfault
  • 97
  • 1
  • 3
  • 11
  • Undefined references are linker errors, so they're about finding libraries, not headers. Perhaps you can show how your project uses CMake to link against LibUSB? – Angew is no longer proud of SO Jan 25 '16 at 15:18
  • Are you linking the binaries from libusb? – Otomo Jan 25 '16 at 15:18
  • looks like you are misisng some linkerflags for libusb – Kai Iskratsch Jan 25 '16 at 15:20
  • @Angew CMake doesn't link against LibUSB I took the assumption that the devel package would put the right stuff into the default include path. I'm guessing now that's wrong? – Segfault Jan 25 '16 at 15:26
  • 1
    There's a difference between include paths (where headers are stored), and libraries. You need to explicitly configure your buildsystem to link against libraries which you're using. I see not `target_link_libraries()` call in your CMake code, so you're obviously not linking against LibUSB. – Angew is no longer proud of SO Jan 25 '16 at 15:46
  • @Angew I'm clearly failing at using google today because I can't find the relevent information; How do I link against an external libraray like LibUSB? Lets assume I don't know where DNF installed it to. – Segfault Jan 25 '16 at 16:02
  • @LordNibbler If it's installed in the "standard" location as you expect, [`target_link_libraries(main USB)`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) should do the trick (where `USB` is the name of the library file without the `lib` part). If it's not, make a guess at where it is, ask the user (through a CMake cache variable), or use [`find_library()`](https://cmake.org/cmake/help/latest/command/find_library.html). – Angew is no longer proud of SO Jan 25 '16 at 19:49
  • @NathanOliver: The actual question is "How to correctly link with 3d-party library in CMake", not about "What is undefined reference in C/C++ in general", which is described in the referenced question. [But the title is definitely bad]. Given question could be good base for nice answer, which many of CMake beginners (and not only them) look for. Voted for reopen. – Tsyvarev Jan 25 '16 at 19:50
  • 1
    @NathanOliiver I have improved the heading – Segfault Jan 26 '16 at 08:38

3 Answers3

9

In general, to link a third party library, you need to add the include directory where the compiler will look for the headers, and the libraries which are used by the linker.
To add include directories use target_include_directories, to add a library to be linked to a target use target_link_libraries.
For libUSB and a testLibUSB.cpp source file this would result in

add_executable(targetTestLibUSB testLibUSB.cpp)
target_include_directories(targetTestLibUSB ${LIBUSB_INCLUDE_DIR})
target_link_libraries(targetTestLibUSB ${LIBUSB_LIBRARY})

If you have several targets, you might want to use include_directories and link_libraries before defining any target. These commands apply to all targets of a project after they are set and save a lot of repetition

You can specify the paths for LIBUSB_INCLUDE_DIR and LIBUSB_LIBRARY by hand. But more flexible and portable is to use CMake built-in mechanisms to find headers and libraries.
Header can be searched by find_path and libraries by find_library.
in your case this could be

find_path(LIBUSB_INCLUDE_DIR
  NAMES libusb.h
  PATH_SUFFIXES "include" "libusb" "libusb-1.0")
find_library(LIBUSB_LIBRARY
  NAMES usb
  PATH_SUFFIXES "lib" "lib32" "lib64")

The PATH_SUFFIXES are optional. If you have installed the library in a default location, CMake will find it automatically. Otherwise specify CMAKE_PREFIX_PATH and CMake will look for the headers and libraries there, too. You can specify the variable either by adding it in the CMake GUI or adding -DCMAKE_PREFIX_PATH=/path/to/add to your CMake call.

A common pitfall is to not delete the CMakeCache.txt file in the build directory. CMake caches the values for LIBUSB_INCLUDE_DIR and LIBUSB_LIBRARY and if you makes adjustment to the prefix path or your search logic, it still does not reevaluate the variable values but sticks to the cached values.

usr1234567
  • 21,601
  • 16
  • 108
  • 128
  • 1
    Nice explanations. Minor note: currently `include_directories` is much more common than `target_include_directories`, especially for CMake beginners. The last paragraph actually describes **debugging** process for *developers* - shouldn't it be delimited somehow from "How to write CMake script" content? – Tsyvarev Jan 26 '16 at 09:14
  • That's all great, and a really helpful answer but in my case it doesn't help when Neither I nor find_path seem to know where DNF put the library. I think thats for a seperate question though – Segfault Jan 26 '16 at 09:23
  • @LordNibbler `repoquery --list ` might help – usr1234567 Jan 26 '16 at 09:31
  • @Tsyvarev Thanks for the hint, I added `include_directories`, too. You are right that the caching is kind of unrelated, but it is a common pitfall. That's why I am mentioning it. – usr1234567 Jan 26 '16 at 09:33
  • @usr1234567 that fixed it – Segfault Jan 26 '16 at 09:44
5

From your projects CMakeLists.txt file it doesn't become apparent to me how you've tried to link libusb. The way I would do is the following:

target_link_libraries(project_name <other_dependencies> usb-1.0)

(Just to clarify I mean the CMakeLIsts.txt file in which you add your executable)

You're trying to import from <libusb-1.0/...> thus you need to link usb-1.0 (the lib is always omitted from linker commands!)

I'm on Fedora 23, also using KDevelop and I didn't have to specify a path. Especially because on my system all the environment variables used in the previous answer are NULL anyways.

And to confirm where and how a library is installed in the future you can just do: locate libusb | grep .so

Hope this was somewhat helpful.

AreusAstarte
  • 1,958
  • 2
  • 17
  • 29
  • 1
    For my project `rookery` I added `target_link_libraries(rookery usb-1.0)`, but that gave me `CMake Error at CMakeLists.txt:6 (target_link_libraries): Cannot specify link libraries for target "rookery" which is not built by this project.` – Thom Mar 30 '21 at 11:06
-1

Since recently (May 2023) there is totally different alternative: https://github.com/libusb/libusb-cmake

A few things to be aware:

  • as of this moment CMake exports/package is not available (i.e. CMake's find_package will not be of use);
  • this is not officially supported by the mainline libusb maintainers, but hosted under Libusb Org. on Github and supported by the community;
  • this is going to be the most useful for those who need to include libusb as part of their project as a subdirectory;
Youw
  • 799
  • 6
  • 11