Cmake configuration for multiple sub-libraries in a "packages" directory - c++

Here is a sample project I am trying to build with a "Packages" directory which includes all the libraries to be used in the main code.
I am trying to keep my root cmake file as clean as possible and avoid relative path such as
include_directory(packages/lib1)
but I am struggling. Is there a way of including sub-directories of a directory for the purposes of header inclusion.

First a few minor remarks:
always name the CMake configuration files CMakeLists.txt (because of)
bookmark the documentation on CMake: https://cmake.org/documentation/
Sometimes it's not that easy to read, but very specific once you adopt your head to the "CMake world" ;-)
make yourself comfortable with the scope of CMake variables
include_directories(DIR1 [DIR2 [...]])
Tells CMake where the compiler should look for header files, i.e. -IDIR1 -IDIR2 ....
add_library(NAME [STATIC|SHARED] SOURCES)
This command creates the required compiler commands to create a static or shared library out of a given list of source files. No need to add in the header files. The make target will be called NAME and the library target is known to CMake as NAME.
add_subdirectory(DIR)
Tells CMake to look into DIR and parse the included CMakeLists.txt with all its content.
target_link_libraries(TARGET LIB1 [LIB2 [...]])
Tells CMake to instruct the linker to link LIB1, LIB2, etc. to the TARGET, i.e. -LLIB1 -LLIB2 .... TARGET is a CMake/make target previously defined/created with a call to add_{library,executable,custom_target}.
CMakeLists.txt:
include_directories(libraries)
# a header file in `libraries/lib1/foo.hpp` can be included
# in the whole CMake project by `#include "lib1/foo.hpp"`.
add_subdirectory(libraries)
add_subdirectory(tests)
libraries/CMakeLists.txt:
add_subdirectory(lib1)
add_subdirectory(lib2)
libraries/lib1/CMakeLists.txt:
add_library(lib1 STATIC ${LIB1_SOURCES})
libraries/lib2/CMakeLists.txt:
add_library(lib2 STATIC ${LIB2_SOURCES})
tests/CMakeLists.txt:
add_executable(tests ${TEST_SOURCES})
target_link_libraries(tests lib1 lib2)

Related

How to use find_package on a package added from top level add_subdirectory?

This might be an x y problem, so here's my situation.
Background
I have the following project structure:
-project
-examples
-example_that_uses_mylib_1
* CMakeLists.txt
* main.cpp
-example_that_uses_mylib_2
* CMakeLists.txt
* main.cpp
-external
-notmylib_a
* CMakeLists.txt
* ... (other stuff)
-notmylib_b
* CMakeLists.txt
* ... (other stuff)
-src
-mylib_stuff
* file1.cpp
* file1.h
*CMakeLists.txt
CMakeLists.txt
I'm attempting to make a cmake file that does the following:
allows mylib to depend on targets in 3rd party libraries found in external with out using add_subdirectory given that they aren't actually sub directories and that is bad practice.
allows example_that_uses_mylib executables that depend on mylib target with out adding it as a sub directory.
I was unsure of how to do this until I saw this project whos top level cmake does this:
add_subdirectory(lib/foo)
add_subdirectory(src/bar)
add_subdirectory(src/baz)
and bar and baz CMakeLists.txt do this:
#Bar
find_package(foo 0.1.2 CONFIG REQUIRED)
#Baz
find_package(bar CONFIG REQUIRED)
Which made me think I could do the same with my libraries. I couldn't.
Originally the single top level CMakeLists.txt built all targets, I wanted to move away from this, and split up building with add_subdirectories, starting with mylib.
Problem
Originally my top level CMakeLists.txt looked a lot like this (which previously worked):
add_subdirectory(external/notmylib_a)
add_library(mylib STATIC src/mylib_stuff/file1.cpp)
target_include_directories(mylib PUBLIC src/)
target_link_libraries(mylib PRIVATE notmylib_a::notmylib_a)
when I decided to split things up so originally I did this (which also works):
#CMakeLists.txt
add_subdirectory(external/notmylib_a)
#src/CMakeLists.txt
add_library(mylib STATIC src/mylib_stuff/file1.cpp)
target_include_directories(mylib PUBLIC src/)
target_link_libraries(mylib PRIVATE notmylib_a::notmylib_a)
Then to follow the other project I decided to do this:
#CMakeLists.txt
add_subdirectory(external/notmylib_a)
#src/CMakeLists.txt
find_package(notmylib_a CONFIG REQUIRED) #NEW LINE!!
add_library(mylib STATIC src/mylib_stuff/file1.cpp)
target_include_directories(mylib PUBLIC src/)
target_link_libraries(mylib PRIVATE notmylib_a::notmylib_a)
and I got an error in CMAKE
CMake Error at src/CMakeLists.txt:25 (find_package):
Could not find a package configuration file provided by "notmylib_a"
with any of the following names:
notmylib_aConfig.cmake
notmylib_a-config.cmake
Add the installation prefix of "notmylib_a" to CMAKE_PREFIX_PATH or set
"notmylib_a_DIR" to a directory containing one of the above files. If
"notmylib_a" provides a separate development package or SDK, be sure it
has been installed.
How was the other project able to utilize find_package in such a way?
How was the other project able to utilize find_package in such a way?
Config file for other foo project has these lines:
if(NOT TARGET foo::foo)
include("${foo_CMAKE_DIR}/foo-targets.cmake")
endif()
That is, when the foo is included with add_subdirectory approach and creates foo::foo target, find_package(foo) actually ignores its configuration file.
This is noted in the foo's CMakeLists.txt:
# We also add an alias definition so that we shadown
# the export namespace when using add_subdirectory() instead.
add_library(foo::foo ALIAS foo)
In other words, with given foo package included with add_subdirectory approach, using find_package(foo) is possible, but optional: One may directly use foo or foo::foo target without any find_package().
The example project that you refer to is able to do:
#Bar
find_package(foo 0.1.2 CONFIG REQUIRED)
#Baz
find_package(bar CONFIG REQUIRED)
because both of the sub-projects lib/foo and src/bar have CMakeLists.txt files
that contain CMake code that generates a CMake package configuration file
that will be searched for by find_package when CONFIG mode is specified,
discovery of which will constitute success for the find_package command.
In lib/foo/CMakeLists.txt, for example, such code includes:
include(CMakePackageConfigHelpers)
...
set(PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/foo-config.cmake")
...
configure_package_config_file(cmake/foo-config.cmake.in
${PROJECT_CONFIG_FILE}
INSTALL_DESTINATION ${INSTALL_CONFIG_DIR})
As a result, when cmake is run to generate the project build files, the
package configuration file is among the files generated, like so:
$ git clone https://github.com/sunsided/cmake.git
...
$ cd cmake/
$ mkdir build
$ cd build
$ cmake ..
...
$ find -name '*-config.cmake'
./lib/foo/foo-config.cmake
./src/bar/bar-config.cmake
Those two *-config.cmake files are respectively the package configuration
files for lib/foo and src/bar. The documentation for find_package CONFIG mode
describes the (complex) search algorithm by which find_package will discover them. For sub-project
src/bar,
find_package(foo 0.1.2 CONFIG REQUIRED)
is able to find lib/foo/foo-config.cmake because, when it runs, the build files
for its dependency lib/foo have already been generated. Likewise for sub-project
src/baz,
find_package(bar CONFIG REQUIRED)
succeeds because the build files for its dependency src/bar have already been
generated.
The CMake error that you receive from your own attempt to use find_package in the same way:
CMake Error at src/CMakeLists.txt:25 (find_package):
Could not find a package configuration file provided by "notmylib_a"
with any of the following names:
notmylib_aConfig.cmake
notmylib_a-config.cmake
...
now has an obvious meaning. To fix it, you need to fill in the missing
CMake code in the external/notmylib_a/CMakeLists.txt to generate notmylib_a-config.cmake.

Statically linking GLEW from source with cmake

I am somewhat new to cmake and completely new to glew, glfw, and the like. I'm following a youtube channel to learn more about game engines and programming.
My problem is linking the static glew library in my project using cmake.
First, I start with the glew source code from http://mcs.une.edu.au/doc/glew-devel/index.html.
I compile it by:
cd build; cmake ./cmake; make glew_s
This adds a lib directory into the build directory with libGLEW.a
A shortened version of my CMakeLists.txt looks like:
cmake_minimum_required (VERSION 3.5 FATAL_ERROR)
project (TestProject CXX)
set(CMAKE_CXX_FLAGS "-std=c++11")
###########################
# GLEW
###########################
add_subdirectory(${CMAKE_SOURCE_DIR}/Dependencies/GLEW)
target_link_libraries(${PROJECT_NAME} glew)
and in Dependencies/GLEW I have another CMakeLists.txt:
# Add glew source and header files
file(GLOB_RECURSE glew-lib ${CMAKE_CURRENT_SOURCE_DIR}/lib/*)
file(GLOB_RECURSE glew-headers ${CMAKE_CURRENT_SOURCE_DIR}/include/GL/*)
add_library(glew ${glew-lib} ${glew-headers})
target_include_directories(glew PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/GL")
I put a copy of the libGLEW.a file into the lib directory and the include directory is copied from the glew source code include directory. It holds the GL directory, which contains the header files glew.h, wglew.h, eglew.h, and glxew.h.
When I run cmake I get the error:
CMake Error: Cannot determine link language for target "glew".
CMake Error: CMake can not determine linker language for target: glew
The glew source code also has a src directory with glew.c in it, but if I put it in the libs directory and include it in the Dependencies/GLEW/CMakeLists.txt like:
# Add glew source and header files
file(GLOB_RECURSE glew-lib ${CMAKE_CURRENT_SOURCE_DIR}/lib/*.c)
file(GLOB_RECURSE glew-headers ${CMAKE_CURRENT_SOURCE_DIR}/include/GL/*)
add_library(glew ${glew-lib} ${glew-headers})
target_include_directories(glew PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
I get the error:
CMake Error: Error required internal CMake variable not set, cmake may
be not be built correctly.
Missing variable is: CMAKE_C_COMPILE_OBJECT
CMake Error: Error required internal CMake variable not set, cmake may
be not be built correctly.
Missing variable is: CMAKE_C_CREATE_STATIC_LIBRARY
Lastly, I have tried just including the glew.c and headers in the root CMakeLists.txt like:
#########################
# GLEW
#########################
include_directories("${CMAKE_SOURCE_DIR}/Dependencies/GLEW/lib/")
include_directories("${CMAKE_SOURCE_DIR}/Dependencies/GLEW/include/GL/")
Here cmake will finish, but it won't be able to compile, saying classes do not name a type and/or are not declared in this scope.
Any help would be appreciated. I was under the impression that the libGLEW.a was the static library, and all I would have to do is link and compile it along with the headers, but that did not work.
First of all, I forgot to use
make install
after using make the first time, so I was using the incorrect libGLEW.a file.
After including the proper libGLEW.a file, my directory structure had
./Dependencies/GLEW/include/GL/*.h //header files
./Dependencies/GLEW/lib/libGLEW.a //source file
Finally, the TopLevel CMakeLists.txt is changed to include:
add_library(glew STATIC IMPORTED GLOBAL)
set_target_properties(glew PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/Dependencies/GLEW/lib/libGLEW.a )
set_target_properties(glew PROPERTIES INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/Dependencies/GLEW/include )
If I want to include the library from a lower CMakeLists.txt such as /Dependencies/GLEW/CMakeLists.txt Then I would have to first export the library from there and then import it at the topLevel CMakeLists.txt file.
Now, in order to use glew headers in my project I can just use #include .

CMake + Qt Creator: Add header files to project files

If I have .h and .cpp files in the directory src, where the .cpp files include the .h files, using these commands in CMake:
aux_source_directory(src SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
And opening that CMake file in Qt Creator, gets all the files (sources + headers) in the list of project files (the file tree on the left by default).
Now, on the other hand, if I put all the .h files in a directory include, and use this:
include_directories(include)
aux_source_directory(src SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
The header files disappear from the project files!
How can I keep the header files in that directory, and still have them listed in Qt Creator's project files?
You shouldn't use aux_source_directory() for your task. That command is for something different. Just list the source files (or put them in a variable).
You shouldn't use include_directory() for defining include directories any more. This command will just populate the -I flag of the compiler. Define a variable with the header files and add that to the executable.
In case you don't want to list every file manually, use file(GLOB ...). But be aware of the caveats mentioned frequently all over the web with using that command.
Afterwards, tell CMake to populate the -I flag only for that executable with the include directory. That way, other targets don't get polluted by includes, they shouldn't use.
set(SOURCES
src/main.cpp
src/whatever.cpp)
set(HEADERS
include/whatever.h)
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC include)
I add my header files always explicit to avoid any surprise.
But on MacOS using QtCreator 4.2.0 and cmake 3.7.1 I can't reproduce your issue.
However I recommend to use following structure to know which files are within project and to trigger update of cmake's data during update of CMakeLists.txt.
In project/CMakeLists.txt:
add_subdirectory(src)
include_directory(include)
add_executable(foo ${SRC_LIST})
In project/src/CMakeLists.txt:
set(SRC_LIST
${SRC_LIST}
${CMAKE_CURRENT_SOURCE_DIR}/a.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b.cpp
PARENT_SCOPE
)

How to properly use file GLOB?

I'm trying to use GLOB_RECURSE to specify my sources and headers files. Currently, my CMakeLists.txt for a static library is:
project(LinearSystemLib)
file(GLOB_RECURSE ${PROJECT_NAME}_headers ${PROJECT_SOURCE_DIR}/*.h)
file(GLOB_RECURSE ${PROJECT_NAME}_sources ${PROJECT_SOURCE_DIR}/*.cpp)
add_library(
${PROJECT_NAME} STATIC ${${PROJECT_NAME}_headers}
${${PROJECT_NAME}_sources}
)
install(
TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION libs
ARCHIVE DESTINATION archives
)
The library directory looks like this:
LinearSystemLib
CMakeLists.txt
source
LinearSystemLib.cpp
include
LinearSystemLib.h
When I run command cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug (in the build directory) everything goes ok. Yet, command make it displays the following:
/home/felipe/Documents/Dados/SINMEC/Eclipse/LinearSystemLib/source/LinearSystemLib.cpp:1:29: fatal error: LinearSystemLib.h: No such file or directory
Is my CMakeLists wrong? I don't want to set specify the sources and headers files by name. I'm not finding information about glob_recurse easily.
I can make it work by listing the sources and headers files by name. However, it MUST be done with the glob_recurse or with glob.
I solved my problem, here's what LinearSystemLib directory looks like:
LinearSystemLib
CMakeLists.txt
source
LinearSystemLib.cpp
include
LinearSystemLib.h
The CMakeLists.txt contains:
project(LinearSystemLib)
#INCLUDE DIRECTORIES
include_directories(${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/include)
#SEARCH FOR .CPP AND .H FILES
file(GLOB ${PROJECT_NAME}_headers ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/include/*.h)
file(GLOB ${PROJECT_NAME}_sources ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/source/*.cpp)
#ADD LIBRARY
add_library(${PROJECT_NAME} STATIC ${${PROJECT_NAME}_sources})
#DEFINE OUTPUT LOCATION
install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION static_libs
)
You don't actually NEED to add the .h/.hpp files using GLOB. I did it because otherwise, Visual Studio (or CodeBlocks) wouldn't create a "Header Files" folder on the project menu.
I wasn't properly specifying the path where GLOB would find the files.
${CMAKE_SOURCE_DIR}/${PROJECT_NAME}/source/
You need to add
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR})
so the correct -I argument is added to your compilation step. Use make VERBOSE=1 to see exactly what commands make is executing.

CMake link shared library built from object libraries

I can't get my cmake project running. It should build a library (Core) and then add this library to a new shared library. The problem is that the resulting library does not seem to link correctly against the executable.
fatal error: JNF_NEAT/body.h: No such file or directory
I have a CMake project which is structured as follows:
root
-> CMakeLists.txt
-> Core/
-> CMakeLists.txt
-> Sources/
-> Examples/
-> CMakeLists.txt
-> Example1/
-> CMakeLists.txt
-> Sources/
root/CMakeLists.txt
cmake_minimum_required(VERSION 3.2.2)
project(JNF_NEAT)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/out/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/out/examples)
add_subdirectory(Core)
add_library(JNF_NEAT SHARED $<TARGET_OBJECTS:Core>)
target_link_libraries(JNF_NEAT -lstdc++fs)
add_subdirectory(Examples)
root/Core/CMakeLists.txt
file(GLOB SOURCES Sources/*.cpp Sources/*.h)
add_library(Core OBJECT ${SOURCES})
root/Examples/CMakeLists.txt
add_subdirectory(XOR)
root/Examples/XOR/CMakeLists.txt
include_directories(../../out/lib)
file(GLOB SOURCES Sources/*.cpp Sources/*.h)
add_executable(XOR_EXAMPLE ${SOURCES})
target_link_libraries(XOR_EXAMPLE JNF_NEAT)
The entire source code is available here.
Edit #1
I tried setting target_include_directories(XOR_EXAMPLE BEFORE PUBLIC ../../out/lib) before target_link_libraries
I also tried setting include_directories(out/lib) in the outer most CMakeLists.txt
Edit #2
On Linux this error occures.
The error
JNF_NEAT/body.h: No such file or directory
is not a link error, but rather a compilation error.
Check that JNF_NEAT/body.h actually exists. If it does, it might not be included in the list of directories where the compiler looks for #includes. You can set this in CMake with the include_directories command.
Add the given directories to those the compiler uses to search for include files. Relative paths are interpreted as relative to the current source directory.
The include directories are added to the INCLUDE_DIRECTORIES directory property for the current CMakeLists file. They are also added to the INCLUDE_DIRECTORIES target property for each target in the current CMakeLists file. The target property values are the ones used by the generators.
By default the directories specified are appended onto the current list of directories. This default behavior can be changed by setting CMAKE_INCLUDE_DIRECTORIES_BEFORE to ON. By using AFTER or BEFORE explicitly, you can select between appending and prepending, independent of the default.