0
votes

I have a lot of libraries in my project, and a LOT of individual applications. A few of the my libraries have dependency libraries, some of them external ones, and I'd like a way for the application CMakeList.txt files to be simpler. I'm hoping to use macros to simplify.

Below is a much reduced test case. For example, in my project, including one of my libraries requires also include_directories, link_directories, and target_link_libraries for ImageMagick, pugixml, jsoncpp, liboauthcpp, etc... And, some of these third party libraries require compiler flags. My project's version of the _LIB() macro below will be much longer...

Question: Is there a way I can have the _LIB() macro below automatically add something to the target_link_libraries that invoke the macro?

I'm not sure how to do that because target_link_libraries argument 1 is the target name, which changes per application.

~/codeTest/CMakeLists.txt

cmake_minimum_required(VERSION 2.6)
project(codeTest)

macro(_LIB)
  include_directories(~/codeTest/lib)
  link_directories(~/codeTest/lib)
endmacro()

add_subdirectory(lib)
add_subdirectory(app)

~/codeTest/lib/CMakeLists.txt

include_directories(~/codeTest/lib)
add_library(lib lib.cpp)

~/codeTest/lib/lib.h

#ifndef __LIB__
#define __LIB__

namespace LIB {

unsigned long libFunc(unsigned long inValue);

}

#endif

~/codeTest/lib/lib.cpp

#include <lib.h>

namespace LIB {

unsigned long libFunc(unsigned long inValue) {
   return inValue+1;
}

}

~/codeTest/app/CMakeLists.txt

_LIB()
add_executable(app app.cpp)
target_link_libraries(app lib)

~/codeTest/app/app.cpp

#include <lib.h>
using namespace LIB;

int main() {
   unsigned long x = 1;
   unsigned long y = libFunc(x);
}
3

3 Answers

1
votes

With the growth of your project's complexity, you will soon find out that multiple {link,include}_directories directives, gathered in a macro, is inflexible solution, and they should be specified explicitly.

I recommend you to take into consideration an idea of modules (/usr/share/cmake/Modules/): for each of your external dependencies use a Find*.cmake file that can be found in cmake modules directory (see above) or in the package's one (if its authors have written an according module). Or you can write it yourself.

That modules usually define a number of variables, (e.g., Boost_INCLUDE_DIRS, Boost_LIBRARY_DIRS), that you will use only in those subprojects, that really need ones.

So, for each subproject you specify include_.../link_... directives explicitly referencing variables defined in modules or in your other subprojects (for internal dependencies).

Moreover, macros and functions in cmake are really very unpredictable for users, that expect them to behave like traditional functions in well-known programming languages (C, Java, Ruby, ...) when you start adding arguments to them; read about variable caching, scoping, and conversions between strings and lists.

0
votes

I might recommend creating an INTERFACE library, if you can group some of your dependencies into 1 target this way.

For example,

add_library(ImageMagick ...)
#...etc gather all library targets however is conviennt

add_library(CoreDependencies INTERFACE)
target_link_libraries(CoreDependencies PUBLIC ImageMagick ...)

Then in your app(s) you can just

target_link_libraries(MyApp PRIVATE CoreDependencies)

and this will bring in everything you need. You could create several subsets of libraries if you really have so many different use cases.

Of note, this approach requires CMake 3. https://cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html#interface-libraries