开发者

How do you export a system library using cmake?

开发者 https://www.devze.com 2023-03-28 08:25 出处:网络
How can I export the libraries that a cmake library depends on, such that an executable depending on that library does not have to manually depend on the dependencies of that library?

How can I export the libraries that a cmake library depends on, such that an executable depending on that library does not have to manually depend on the dependencies of that library?

That's a bit of a mouthful, so here's an example:

dummy (application) ----> depends on liba

liba ----> depends on libpng

Compiling dummy generates errors:

-- Found LIBPNG
-- Found LIBA
-- Configuring done
-- Generating done
-- Build files have been written to: /home/doug/projects/dummy/build
Linking C executable dummy
../deps/liba/build/liba.a(a.c.o): In function `a_dummy':
/home/doug/projects/dummy/deps/liba/src/a.c:6: undefined reference to `png_sig_cmp'
collect2: ld returned 1 exit status
make[2]: *** [dummy] Error 1
make[1]: *** [CMakeFiles/dummy.dir/all] Error 2
make: *** [all] Error 2

I can fix that by adding this into CMakeLists.txt for dummy:

TARGET_LINK_LIBRARIES(dummy png)

However, dummy has no knowledge of how liba implements its api. At some point that may change to being libjpg, or something else, which will break the dummy application.

After getting some help from the cmak开发者_如何学Pythone mailing list I've been directed to this example for exporting things: http://www.cmake.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file

However, following that approach leaves me stuck at this line:

export(TARGETS ${LIBPNG_LIBRARY} FILE "${PROJECT_BINARY_DIR}/ALibraryDepends.cmake")

Clearly I'm missing something here; this 'export' command looks like its designed to export sub-projects to a high level; ie. nested projects inside liba.

However, that is not the problem here.

When configuring liba (or any cmake library) I will always generate a list of dependencies which are not part of that project.

How can I export those so they appear as part of LIBA_LIBRARY when I use find_package() to resolve liba?

Using static libraries is not an option (static library for something that links to opengl? no.)


Given your comment to arrowdodger's answer about the fear of installing something would mess up your system I chose to give a conceptional comment in form of an answer because of its length.

Chaining cmake project works via find_package, which looks for *Config.cmake and *-config.cmake files.

Project A's CMakeLists.txt:

#CMakeLists.txt
project(A)
install(FILES 
  ${CMAKE_CURRENT_SOURCE_DIR}/AConfig.cmake share/A/cmake
)

#AConfig.cmake
message("Yepp, you've found me.")

$ mkdir build
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/tmp/test-install ..
$ make install

Project B's CMakeLists.txt:

project(B)
find_package(A)

Then

$ mkdir build
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/tmp/test-install ..
$ make install

results in

...
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
Yepp, you've found me.

B found A because it installed AConfig.cmake into a location where cmake will find it 'share/A/cmake' AND was given the same value for CMAKE_INSTALL_PREFIX.

Now this is that. Lets think about what you can do in AConfig.cmake: AFAIK everything you want to. But the most common task is to pull information about the targets of A via include(), do some additional find_package invoctions for 3rd party packages (HINT HINT) and create the variables

A_LIBRARIES
A_INCLUDE_DIRS

What you want to include is a file that was created by

install(EXPORT A-targets
   DESTINATION share/A/cmake
)

in A's CMakeLists.txt , where A-targets refers to a global cmake variable that accumulated all target informations when used in

install(TARGETS ...
  EXPORT A-targets
  ...
)

statments. What is created at make install is

/tmp/test-install/share/A/cmake/A-targets.cmake

which then resides alongside AConfig.cmake in the same directory. Please take another look at the wiki page on how to use this file within AConfig.cmake.

Regarding the export() command: This comes handy if your projects have gotten HUGE and it takes a considerable amount of time to install them. To speed things up, you want to use what's in A's build/ directory directly. It's an optimization and also explained in the wiki. It still works via find_package(), see

  • http://cmake.org/cmake/help/cmake-2-8-docs.html#command:export

But I strongly suggest that you go for the usual make install route for now.


I found my own solution to this problem using the accepted solution above, which I leave here for others:

In liba/CMakeLists.txt:

# Self
set(A_INCLUDE_DIRS ${A_INCLUDE_DIRS} "${PROJECT_SOURCE_DIR}/include")
set(A_LIBRARIES ${A_LIBRARIES} "${PROJECT_BINARY_DIR}/liba.a")

# Libpng
FIND_PACKAGE(libpng REQUIRED)
set(A_INCLUDE_DIRS ${A_INCLUDE_DIRS} ${LIBPNG_INCLUDE_DIRS})
set(A_LIBRARIES ${A_LIBRARIES} ${LIBPNG_LIBRARIES})

ADD_LIBRARY(a ${SOURCES})

# Includes
INCLUDE_DIRECTORIES(${A_INCLUDE_DIRS})

# Allow other projects to use this
configure_file(AConfig.cmake.in "${PROJECT_BINARY_DIR}/AConfig.cmake")

In liba/AConfig.cmake:

set(A_LIBRARIES @A_LIBRARIES@)
set(A_INCLUDE_DIRS @A_INCLUDE_DIRS@)

In dummy/CMakeLists.txt:

FIND_PACKAGE(A REQUIRED)
INCLUDE_DIRECTORIES(${A_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(dummy ${A_LIBRARIES})

This yields an AConfig.cmake that reads:

set(A_LIBRARIES /home/doug/projects/dummy/deps/liba/build/liba.a;/usr/lib/libpng.so)
set(A_INCLUDE_DIRS /home/doug/projects/dummy/deps/liba/include;/usr/include)

And a verbose compile that reads:

/usr/bin/gcc  -std=c99 -g   CMakeFiles/dummy.dir/src/main.c.o  -o dummy -rdynamic ../deps/liba/build/liba.a -lpng 

Which is exactly what I was looking for.


If liba doesn't provide any means to determine it's dependencies, you can't do anything. If liba is library developed by you and you are using CMake to build it, then you should install libaConfig.cmake file with liba itself, which would contain necessary definitions. Then you include libaConfig in dummy's CMakeLists.txt to obtain information about how liba have been built.

You can look how it's done in LLVM project, relevant files have cmake.in extension http://llvm.org/viewvc/llvm-project/llvm/trunk/cmake/modules/

In the end, in dummy project you should use

target_link_libraries( ${LIBA_LIBRARIES} )

include_directories( ${LIBA_INCLUDE_DIR} )

link_directories( ${LIBA_LIBRARY_DIR} )

If that liba is used only by dummy, you can build it from single CMake project. This is more convenient, since you don't need to install liba each time you recompile it and it will be rebuilt and relinked with dummy automatically every time you run make. If you liked this approach, the only thing you should do - define in liba' CMakeLists.txt variables you need with PARENT_SCOPE option (see set() command manual).

Finally, you can use shared libs, .so's don't have such problem.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号