Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMake : how to link a library WITHOUT automatic search function FIND_PACKAGE?

Tags:

cmake

I wonder how to find/link a library without any FIND_PACKAGE.

Assume that we have a "personal" library called testlib :

/perso/testlib/include/testlib1.h
/perso/testlib/include/testlib2.h
/perso/testlib/lib/testlib1.a
/perso/testlib/lib/testlib2.a

How to link it with CMake ?

1) What are the functions to link it directly in the code of the CMakeLists.txt ?

2) How to allow the user to select where are the files ?

3) I have difficulties to understand what is interpreted and what it's not by CMake. For example if you define a variable ${MYVARIABLE_INCLUDE_DIR} or ${MYVARIABLE_LIBRARIES} is "INCLUDE_DIR" or "LIBRARIES" an extension interpreted by CMake or there is no difference if I call this variable ${MYVARIABLE_INCDIR} ?

4) How to do the same procedures (including a "personal" library) if you have a library that contains ten library files or more in the lib directory ?

5) And finally, when you type TARGET_LINK_LIBRARIES(myexecutable gmp), how do you know that the name of the library is "gmp". Why not "Gmp" or "GMP" ? Is the name of the library to put in this function just equal to the .a file minus "lib" and ".a" ? For example libgmp.a -> gmp ? If I want to link a library called libtestlolexample.a, do I have to type TARGET_LINK_LIBRARIES(myexecutable testlolexample) ?

Thank you very much.

like image 554
Vincent Avatar asked Aug 08 '11 15:08

Vincent


People also ask

How do I link libraries in CMakeLists?

Let's start by adding the library's directory as a subdirectory to our myapp project. add_subdirectory makes the library test defined in libtestproject available to the build. In target_link_libraries we tell CMake to link it to our executable. CMake will make sure to first build test before linking it to myapp.

What does Find_package in CMake do?

FIND_PACKAGE(<name>) asks CMake to find and load settings from the external package <name>. This works as follows: CMake searches the directories listed in CMAKE_MODULE_PATH for a file called Find<name>. cmake.

Where does CMake search Find_package?

Search Modes. In this mode, CMake searches for a file called Find<PackageName>. cmake , looking first in the locations listed in the CMAKE_MODULE_PATH , then among the Find Modules provided by the CMake installation. If the file is found, it is read and processed by CMake.


2 Answers

You can use target_link_libraries(myexecutable mylib) to link to the library "mylib". The compiler will use its default way to find the specified library (e.g. it will look for libmylib.a on Linux). The compiler will only look in the link_directories(directory1 directory2 ...), so you could try that command to add the required directories to the search path.

When "mylib" is also compiled with CMake this will be recognized and everything should work automatically.

When you want the user to specify a directory you can use a cached CMake variable. set(MYPATH "NOT-DEFINED" CACHE PATH "docstring").

For more complex stuff it is very advisable to write a CMake find module that can be used with find_package. I suggest you take a look at the FindALSA.cmake which can be used as a good starting point.

The interesting part is at the end:

if(ALSA_FOUND)
  set( ALSA_LIBRARIES ${ALSA_LIBRARY} )
  set( ALSA_INCLUDE_DIRS ${ALSA_INCLUDE_DIR} )
endif()

mark_as_advanced(ALSA_INCLUDE_DIR ALSA_LIBRARY)

The ALSA_LIBRARY and ALSA_INCLUDE_DIR variables are user configurable and stored in the cache, while ALSA_LIBRARIES and ALSA_INCLUDE_DIRS as well as ALSA_FOUND get computed and are the ones that the user of the find module is supposed to use.

Typically one would use the find module like this:

find_package(ALSA REQUIRED)
include_directories(${ALSA_INCLUDE_DIRS})
target_link_libraries(myexe ${ALSA_LIBRARIES})

I'm sure you can adapt this for your personal library.

like image 167
trenki Avatar answered Oct 07 '22 17:10

trenki


Usually when you want to link against a library that doesn't have a find_package module (e.g. it's an uncommon library, or it's your own library), then you can use the basic commands (the find_X commands) to set variables with the paths you need. Then you use those variables as with find_package (include_directories, target_link_libraries).

If you're going to be using this library from multiple packages, you may want to create a find_package module; basically it's using the same commands with certain conventions.

Either of these allow you to specify paths (in the CMake module) to look in, and they allow the user to override the paths (the variables show up as options in ccmake/cmake-gui).

I'd be glad to add an example of one or both of these methods, just let me know what you're looking for.

If you just want a quick-and-dirty solution, you could do this, but I wouldn't recommend it:

include_directories(/perso/testlib/include)
add_executable(myexecutable myexecutable.cpp)
target_link_libraries(myexecutable
    /perso/testlib/lib/testlib1.a
    /perso/testlib/lib/testlib2.a)

Regarding your question about target_link_libraries (#5), you can do it several ways. If you want you can provide the full name (e.g. target_link_libraries(myexe libfoo.a)), but I think it's better (more portable I suppose) to use the short name (e.g. target_link_libraries(myexe foo). You can also include linker flags; I'm not sure where I read it, but I think it may translate the -L and -l flags for different linkers.

For example, if I have a bunch of libraries in the same directory, and I know the names, I might find the directory, store it in a variable, and then do this:

# First, find and set TESTLIB_LIBRARY_DIR, e.g. with find_path
# ...

# This assumes the libraries are e.g. 'libtestlib1.a' and 'libtestlib2.a'
set(TESTLIB_LIBRARIES
    -L${TESTLIB_LIBRARY_DIR)
    -l testlib1
    -l testlib2)

add_executable(myexecutable myexecutable.cpp)
target_link_libraries(myexecutable ${TESTLIB_LIBRARIES})

If you want to make your own find_package module (like trenki mentioned, FindALSA.cmake seems to be a good starting point), you can use it by adding the directory to the CMAKE_MODULE_PATH; for example, if you put your module(s) in a cmake/modules/ subdirectory:

# Look for extra CMake modules in a subdirectory of this project
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/" ${CMAKE_MODULE_PATH})

One possible issue with FindALSA.cmake: I'm not sure CMAKE_CURRENT_LIST_DIR will work. So I think you should make this change (the second work for me in a module I wrote):

# Change this line
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)

# To this (with no path/extension it will search the CMake modules path):
include(FindPackageHandleStandardArgs)

And to get the usage of FIND_PACKAGE_HANDLE_STANDARD_ARGS, look at FindPackageHandleStandardArgs.cmake in the CMake Modules directory.

like image 43
Sam Hartsfield Avatar answered Oct 07 '22 17:10

Sam Hartsfield