Linking a Shared Library Autotools - linker

I'm an autotools beginner, and I can't seem to figure out how to use an external library correctly with autotools.
Here is my directory hierarchy.
.
├── configure.ac
├── Makefile.am
├── README
└── src
(source files)
└── Makefile.am
The library's name is acml_mp and is, by default, installed in /opt/acml5.3.1/gfortran64/lib. There is also a directory called /opt/acml5.3.1/gfortran64/include to include. When I compile without autotools, including the usual compiler flags works fine:
g++ ... -L/opt/acml5.3.1/gfortran64_mp/lib -I/opt/acml5.3.1/gfortran64_mp/include -lacml_mp ...
In configure.ac, I put the command AC_LIB_LINKFLAGS([acml_mp]) which I think only deals with the -lacml_mp flag.
Basically, the end goal is to have autoconf search for this library, and have the makefile generated by automake include the correct link/include paths when compiling. Finally, when compiling by hand, I always need to modify the environment variable LD_LIBRARY_PATH using the command
Export LD_LIBRARY_PATH=/opt/acml5.3.1/gfortran64_mp/lib:$LD_LIBRARY_PATH
which, ideally, I would like to avoid having the user do. Apologies if this information exists already, I looked through SO and Google for a few hours to no avail.

The problem with searching is that /opt/acml5.3.1/gfortran is never going to be a standard (search) location for libraries (and headers) like /usr/lib, /usr/local/lib etc. Probably the best bet is to supply this location explicitly via --with-acml to configure.
The AC_ARG_WITH macro is described here. Assuming test "x$with_acml" != xno, you can try linking a program with AC_LINK_IFELSE.
AC_LANG_PUSH([C]) # or [Fortran]
ac_save_acml_CPPFLAGS="$CPPFLAGS" # or FCFLAGS instead of CPPFLAGS.
ac_save_acml_LIBS="$LIBS"
ac_acml_CPPFLAGS="-I${with_acml}/include"
ac_acml_LIBS="-L${with_acml}/libs -lacml_mp"
CPPFLAGS+="$ac_acml_CPPFLAGS"
LIBS+="$ac_acml_LIBS"
AC_LINK_IFELSE([AC_LANG_PROGRAM( ... some C or Fortran program ... )],,
AC_MSG_FAILURE([couldn't link with acml]))
AC_LANG_POP
# we *could* stop here... but we might need the original values later.
CPPFLAGS="$ac_save_acml_CPPFLAGS"
LIBS="$ac_save_acml_LIBS"
AC_SUBST(ACML_CPPFLAGS, $ac_acml_CPPFLAGS)
AC_SUBST(ACML_LIBFLAGS, $ac_acml_LIBS)
Assuming you've initialized libtool support with LT_INIT, you can add the acml library with $(ACML_LIBFLAGS) to your own libraries in src/Makefile.am via the LIBADD variable, or to executables with the LDADD variable. or <lib>_la_LIBADD, <prog>_LDADD respectively.
To compile sources with the $(ACML_CPPFLAGS) include path, add it to the AM_CPPFLAGS variable. Or the <prog>_CPPFLAGS variable.
It's difficult to be specific without knowing how your Makefile.am is already set up. I know it looks complicated - but it's better to get the infrastructure right the first time. I'll add to the answer if you have further questions.

Related

How do I add .a files to Cmake [duplicate]

How to get CMake to link an executable to an external shared library that is not build within the same CMake project?
Just doing target_link_libraries(GLBall ${CMAKE_BINARY_DIR}/res/mylib.so) gives the error
make[2]: *** No rule to make target `res/mylib.so', needed by `GLBall'. Stop.
make[1]: *** [CMakeFiles/GLBall.dir/all] Error 2
make: *** [all] Error 2
(GLBall is the executable)
after I copied the library into the binary dir bin/res.
I tried using find_library(RESULT mylib.so PATHS ${CMAKE_BINARY_DIR}/res)
Which fails with RESULT-NOTFOUND.
arrowdodger's answer is correct and preferred on many occasions. I would simply like to add an alternative to his answer:
You could add an "imported" library target, instead of a link-directory. Something like:
# Your-external "mylib", add GLOBAL if the imported library is located in directories above the current.
add_library( mylib SHARED IMPORTED )
# You can define two import-locations: one for debug and one for release.
set_target_properties( mylib PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/res/mylib.so )
And then link as if this library was built by your project:
TARGET_LINK_LIBRARIES(GLBall mylib)
Such an approach would give you a little more flexibility: Take a look at the add_library( IMPORTED) command and the many target-properties related to imported libraries.
I do not know if this will solve your problem with "updated versions of libs".
Set libraries search path first:
link_directories(${CMAKE_BINARY_DIR}/res)
And then just do
target_link_libraries(GLBall mylib)
I assume you want to link to a library called foo, its filename is usually something link foo.dll or libfoo.so.
1. Find the library
You have to find the library. This is a good idea, even if you know the path to your library. CMake will error out if the library vanished or got a new name. This helps to spot error early and to make it clear to the user (may yourself) what causes a problem.
To find a library foo and store the path in FOO_LIB use
find_library(FOO_LIB foo)
CMake will figure out itself how the actual file name is. It checks the usual places like /usr/lib, /usr/lib64 and the paths in PATH.
You already know the location of your library. Add it to the CMAKE_PREFIX_PATH when you call CMake, then CMake will look for your library in the passed paths, too.
Sometimes you need to add hints or path suffixes, see the documentation for details:
https://cmake.org/cmake/help/latest/command/find_library.html
2. Link the library
From 1. you have the full library name in FOO_LIB. You use this to link the library to your target GLBall as in
target_link_libraries(GLBall PRIVATE "${FOO_LIB}")
You should add PRIVATE, PUBLIC, or INTERFACE after the target, cf. the documentation:
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
If you don't add one of these visibility specifiers, it will either behave like PRIVATE or PUBLIC, depending on the CMake version and the policies set.
3. Add includes (This step might be not mandatory.)
If you also want to include header files, use find_path similar to find_library and search for a header file. Then add the include directory with target_include_directories similar to target_link_libraries.
Documentation:
https://cmake.org/cmake/help/latest/command/find_path.html
and
https://cmake.org/cmake/help/latest/command/target_include_directories.html
If available for the external software, you can replace find_library and find_path by find_package.
Let's say you have an executable like:
add_executable(GLBall GLBall.cpp)
If the external library has headers, give the path to its include folder:
target_include_directories(GLBall PUBLIC "/path/to/include")
Add the library directory path:
target_link_directories(GLBall PUBLIC "/path/to/lib/directory")
Finally, link the library name
target_link_libraries(GLBall mylib)
Note that the prefix and extension of the library file are removed:
libmylib.a ➜ mylib
mylib.so ➜ mylib
One more alternative, in the case you are working with the Appstore, need "Entitlements" and as such need to link with an Apple-Framework.
For Entitlements to work (e.g. GameCenter) you need to have a "Link Binary with Libraries"-buildstep and then link with "GameKit.framework". CMake "injects" the libraries on a "low level" into the commandline, hence Xcode doesn't really know about it, and as such you will not get GameKit enabled in the Capabilities screen.
One way to use CMake and have a "Link with Binaries"-buildstep is to generate the xcodeproj with CMake, and then use 'sed' to 'search & replace' and add the GameKit in the way XCode likes it...
The script looks like this (for Xcode 6.3.1).
s#\/\* Begin PBXBuildFile section \*\/#\/\* Begin PBXBuildFile section \*\/\
26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks \*\/ = {isa = PBXBuildFile; fileRef = 26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/; };#g
s#\/\* Begin PBXFileReference section \*\/#\/\* Begin PBXFileReference section \*\/\
26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System\/Library\/Frameworks\/GameKit.framework; sourceTree = SDKROOT; };#g
s#\/\* End PBXFileReference section \*\/#\/\* End PBXFileReference section \*\/\
\
\/\* Begin PBXFrameworksBuildPhase section \*\/\
26B12A9F1C10543B00A9A2BA \/\* Frameworks \*\/ = {\
isa = PBXFrameworksBuildPhase;\
buildActionMask = 2147483647;\
files = (\
26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks xxx\*\/,\
);\
runOnlyForDeploymentPostprocessing = 0;\
};\
\/\* End PBXFrameworksBuildPhase section \*\/\
#g
s#\/\* CMake PostBuild Rules \*\/,#\/\* CMake PostBuild Rules \*\/,\
26B12A9F1C10543B00A9A2BA \/\* Frameworks xxx\*\/,#g
s#\/\* Products \*\/,#\/\* Products \*\/,\
26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/,#g
save this to "gamecenter.sed" and then "apply" it like this ( it changes your xcodeproj! )
sed -i.pbxprojbak -f gamecenter.sed myproject.xcodeproj/project.pbxproj
You might have to change the script-commands to fit your need.
Warning: it's likely to break with different Xcode-version as the project-format could change, the (hardcoded) unique number might not really by unique - and generally the solutions by other people are better - so unless you need to Support the Appstore + Entitlements (and automated builds), don't do this.
This is a CMake bug, see http://cmake.org/Bug/view.php?id=14185 and http://gitlab.kitware.com/cmake/cmake/issues/14185
It has been a long time since the question was posted but I am leaving this one just for reference.
I have a blog post describing step-by-step almost what you (or anyone else) were trying to do.
Please check here: https://michae9.wordpress.com/2022/09/01/shared-lib-to-be-used-by-client-programs-with-cmake/

M1 mac using clion with cmake and homebrew to use sdl2_gfx error [duplicate]

Looking around on the net I have seen a lot of code like this:
include(FindPkgConfig)
pkg_search_module(SDL2 REQUIRED sdl2)
target_include_directories(app SYSTEM PUBLIC ${SDL2_INCLUDE_DIRS})
target_link_libraries(app ${SDL2_LIBRARIES})
However that seems to be the wrong way about doing it, as it only uses the include directories and libraries, but ignored defines, library paths and other flags that might be returned by pkg-config.
What would be the correct way to do this and ensure that all compile and link flags returned by pkg-config are used by the compiled app? And is there a single command to accomplish this, i.e. something like target_use(app SDL2)?
ref:
include()
FindPkgConfig
First of, the call:
include(FindPkgConfig)
should be replaced with:
find_package(PkgConfig)
The find_package() call is more flexible and allows options such as REQUIRED, that do things automatically that one would have to do manually with include().
Secondly, manually calling pkg-config should be avoid when possible. CMake comes with a rich set of package definitions, found in Linux under /usr/share/cmake-3.0/Modules/Find*cmake. These provide more options and choice for the user than a raw call to pkg_search_module().
As for the mentioned hypothetical target_use() command, CMake already has that built-in in a way with PUBLIC|PRIVATE|INTERFACE. A call like target_include_directories(mytarget PUBLIC ...) will cause the include directories to be automatically used in every target that uses mytarget, e.g. target_link_libraries(myapp mytarget). However this mechanism seems to be only for libraries created within the CMakeLists.txt file and does not work for libraries acquired with pkg_search_module(). The call add_library(bar SHARED IMPORTED) might be used for that, but I haven't yet looked into that.
As for the main question, this here works in most cases:
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
...
target_link_libraries(testapp ${SDL2_LIBRARIES})
target_include_directories(testapp PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(testapp PUBLIC ${SDL2_CFLAGS_OTHER})
The SDL2_CFLAGS_OTHER contains defines and other flags necessary for a successful compile. The flags SDL2_LIBRARY_DIRS and SDL2_LDFLAGS_OTHER are however still ignored, no idea how often that would become a problem.
More documentation here http://www.cmake.org/cmake/help/latest/module/FindPkgConfig.html
If you're using cmake and pkg-config in a pretty normal way, this solution works.
If, however, you have a library that exists in some development directory (such as /home/me/hack/lib), then using other methods seen here fail to configure the linker paths. Libraries that are not found under the typical install locations would result in linker errors, like /usr/bin/ld: cannot find -lmy-hacking-library-1.0. This solution fixes the linker error for that case.
Another issue could be that the pkg-config files are not installed in the normal place, and the pkg-config paths for the project need to be added using the PKG_CONFIG_PATH environment variable while cmake is running (see other Stack Overflow questions regarding this). This solution also works well when you use the correct pkg-config path.
Using IMPORTED_TARGET is key to solving the issues above. This solution is an improvement on this earlier answer and boils down to this final version of a working CMakeLists.txt:
cmake_minimum_required(VERSION 3.14)
project(ya-project C)
# the `pkg_check_modules` function is created with this call
find_package(PkgConfig REQUIRED)
# these calls create special `PkgConfig::<MODULE>` variables
pkg_check_modules(MY_PKG REQUIRED IMPORTED_TARGET any-package)
pkg_check_modules(YOUR_PKG REQUIRED IMPORTED_TARGET ya-package)
add_executable(program-name file.c ya.c)
target_link_libraries(program-name PUBLIC
PkgConfig::MY_PKG
PkgConfig::YOUR_PKG)
Note that target_link_libraries does more than change the linker commands. It also propagates other PUBLIC properties of specified targets like compiler flags, compiler defines, include paths, etc., so, use the PUBLIC keyword with caution.
It's rare that one would only need to link with SDL2. The currently popular answer uses pkg_search_module() which checks for given modules and uses the first working one.
It is more likely that you want to link with SDL2 and SDL2_Mixer and SDL2_TTF, etc... pkg_check_modules() checks for all the given modules.
# sdl2 linking variables
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2 SDL2_ttf SDL2_mixer SDL2_image)
# your app
file(GLOB SRC "my_app/*.c")
add_executable(my_app ${SRC})
target_link_libraries(my_app ${SDL2_LIBRARIES})
target_include_directories(my_app PUBLIC ${SDL2_INCLUDE_DIRS})
target_compile_options(my_app PUBLIC ${SDL2_CFLAGS_OTHER})
Disclaimer: I would have simply commented on Grumbel's self answer if I had enough street creds with stackoverflow.
Most of the available answers fail to configure the headers for the pkg-config library. After meditating on the Documentation for FindPkgConfig I came up with a solution that provides those also:
include(FindPkgConfig)
if(NOT PKG_CONFIG_FOUND)
message(FATAL_ERROR "pkg-config not found!" )
endif()
pkg_check_modules(<some-lib> REQUIRED IMPORTED_TARGET <some-lib>)
target_link_libraries(<my-target> PkgConfig::<some-lib>)
(Substitute your target in place of <my-target> and whatever library in place of <some-lib>, accordingly.)
The IMPORTED_TARGET option seems to be key and makes everything then available under the PkgConfig:: namespace. This was all that was required and also all that should be required.
There is no such command as target_use. But I know several projects that have written such a command for their internal use. But every project want to pass additional flags or defines, thus it does not make sense to have it in general CMake. Another reason not to have it are C++ templated libraries like Eigen, there is no library but you only have a bunch of include files.
The described way is often correct. It might differ for some libraries, then you'll have to add _LDFLAGS or _CFLAGS. One more reason for not having target_use. If it does not work for you, ask a new question specific about SDL2 or whatever library you want use.
If you are looking to add definitions from the library as well, the add_definitions instruction is there for that. Documentation can be found here, along with more ways to add compiler flags.
The following code snippet uses this instruction to add GTKGL to the project:
pkg_check_modules(GTKGL REQUIRED gtkglext-1.0)
include_directories(${GTKGL_INCLUDE_DIRS})
link_directories(${GTKGL_LIBRARY_DIRS})
add_definitions(${GTKGL_CFLAGS_OTHER})
set(LIBS ${LIBS} ${GTKGL_LIBRARIES})
target_link_libraries([insert name of program] ${LIBS})

Flags in sub-Makefile.am

I have a C project with the following structure with 1 target (binary final product)
main.c
configure.in
configure
Makefile.am
Makefile.in
folder-1
..Makefile.am
..Makefile.in
..<static library files .c files>
..<static library files .h files>
folder-2
<some .c files>
<some .h files>
...
...
I am aware how to configure and compile my project with Autotools. In regard to my library of folder-1: i am often changing files in that library with different debug levels by defining a flag called DMYDEBUG.
Compilation time for the whole project takes a while and by now, i am able to change the flag by
(1) modifiying the top-level configure.in file:
CCONFIGFLAGS="${CCONFIGFLAGS} -DSF_BIGENDIAN -DMYDEBUG=3"
(2) running make clean
(3) regenerating configure from the edited configure.in where i modify DMYDEBUG
(3) running ./configure on top level
(4) running make
only this way the wished effect is taking places. Is there a better way to modify DMYDEBUG (which is only relevant to the static library in folder-1) without having to recompile the whole project each time?
In the first place, it's terrible that you modify your configure.in to change the flag value. It would be much better to make configure recognize a custom argument that conveys the information, such as --with-debug-level=x. The AC_ARG_WITH() macro serves this purpose.
However, if you have to reconfigure the project (re-run ./configure, with or without rebuilding it first) to change the flag, then changing the flag will always require a full rebuild. For more narrowly-scoped rebuilding, you need to rely on make detecting the flag modification and re-building the affected targets.
make recognizes only file-level dependencies, so that strategy relies on you putting the macro definition in a header file, which the files that use it #include. Since you're using Automake, you can rely on your build system to recognize header dependencies automatically, but you may need to perform one clean build to bootstrap that.

How to use autotools for deep projects?

I have a C project that has the following structure
Main/
Makefile.am
bin/
src/
Makefile.am
main.c
SomeLibrarySource/
SomeFuncs.c
SomeFuncs.h
The main.c contains the main function that uses functions defined in the SomeFuncs.{h/c} files.
I want to use autotools for this project. I read a couple of resources on autotools. But, I was only able to manage using autotools for a single level project where all source, object and other files reside in the same directory.
Then I got some links that talked about using autotools for deep projects like this one and then I got confused.
Right now I have two Makefile.am as follows
Makefile.am
SUBDIRS=src
src/Makefile.am
mainprgdir=../
mainprg_PROGRAMS=main
main_SOURCES=main.c
I am pretty sure that these files should not be as I have them now :P
How do I use autotools for the above project structure? (At least what should be there in those Makefile.am(s) and where should I place them.
EDIT:
One more thing! At the end I would like to have the object files created in the bin directory.
Thanks
mainprogdir=../ does not make a whole lot of sense (you don't know what it is relative to on installation). Probably intended:
# Main/Makefile.am
# .━━ target for `make install`
# |
# ↓ ↓━━ target for compilation
bin_PROGRAMS = bin/main
# ↓━━ based upon compilation target name
bin_main_SOURCES = src/main.c
There are two main approaches. If the functions in SomeLibrarySource are used only by main, then there's no need to build a separate library and you can simply specify the source files in src/Makefile.am
main_SOURCES = main.c SomeLibrarySource/SomeFuncs.c
However, if you actually want to use the functions in other code in your tree, you do not want to compile SomeFuncs.c multiple times but should use a convenience library.
# Assigning main_SOURCES is redundant
main_SOURCES = main.c
main_LDADD = SomeLibrarySource/libSomeFuncs.a
noinst_LIBRARIES = SomeLibrarySource/libSomeFuncs.a
AM_CPPFLAGS = -I$(srcdir)/SomeLibrarySource
(You'll need AC_PROG_RANLIB in configure.ac to use convenience libraries.)
If the source file is named SomeFuncs.c, automake will not need Makefile.am to specify SomeLibrarySource_libSomeFuncs_a_SOURCES, but if the name of the source code file does not match the name specified in noinst_LIBRARIES, SomeLibrarySource_libSomeFuncs_a_SOURCES should be set to the list of files used to build the library. Note that you do not need to specify main_SOURCES, since main.c is the default value if left unspecified (but it's not a bad idea to be explicit.) (In all of this, I am not comfortable use CamlCase names, but the system I'm using uses a case insensitive file system (biggest mistake apple ever made) and the examples I give here are working for me. YMMV)
You could of course do a recursive make, or build the library as a separate project and install it. (I like the final option. Libraries with useful features should exist on their own.)

How do you export a system library using cmake?

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 cmake 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.

Resources