How do I set up CMake to generate header-only projects? - c++

I want to set up header-only C++ (or C) library projects, but can't find a clean way.
After some searches I've found that you can't set up a normal library using add_library to do this because it requires a compilable source file.
A way to do this would be to use add_custom_target instead, this way:
# Get all headers (using search instead of explicit filenames for the example)
file( GLOB_RECURSE XSD_HEADERS
*.hxx
)
add_custom_target( libsxsd SOURCES ${XSD_HEADERS} )
But that doesn't seem to work completely here as I can't see the sources in the project generated in VS2010. I don't know if it's a bug or if I'm doing it wrong or if there is a preferred way to do this.

Update: CMake will soon include a library target called INTERFACE that is ideal for header-only projects. This feature is currently in the master branch. Reference.
Using the command add_custom_target as you propose works for me (VS2010). The files are neatly listed within my project but it has the drawback that you can't define any "Additional Include Directories" with a custom target. Instead, I now use the following:
add_library(HEADER_ONLY_TARGET STATIC test1.hpp test2.hpp)
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
This sets up your header-only project as a dummy archive target. Don't worry, no actual binaries will be generated if you should try and build it (at least not in VS2010 and Xcode 4). The command set_target_properties is there because CMake will otherwise complain that it cannot infer the target language from .hpp files only.

You can do this using the recent Interface Library feature:
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE my_include_dir1 my_include_dir2)
This creates a library target without any source files, and adds the include directories to the INTERFACE_INCLUDE_DIRECTORIES property of the target. This means that any target that links to this library will get these directories as include paths (-I) when built.
For instance, to use the library with an executable target, just do:
add_executable(myexec ${MY_SOURCES})
target_link_libraries(myexec mylib)

I think what you are looking for is just adding an include directory using the
"include_directories" command for cmake.
When doing this, if it is a third party tool that you don't have control over, I would also add the "SYSTEM" flag.
So you command would look like something like this:
include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})

Related

Where does target_link_libraries look for the required files?

I am building a project from source and am trying to understand what is happening in the CMakeLists.txt files.
Where exactly is target_link_libraries looking for the required library files?
The specific CMakeLists.txt file I have has:
target_link_libraries(MyApplication PRIVATE
Magnum::Application
Magnum::GL
Magnum::Magnum
Magnum::Shaders)
I found folders that have the names GL and Shaders in a directory called Magnum in the project, and they have a collection of header files in them. I believed that target_link_libraries is telling CMake to include the libraries in the GL and Shaders folder.
However, I cannot seem to find a corresponding folder for Application, hence my line of reasoning must be flawed.
I do know target_link_libraries is doing something related to allowing the finally-put-together program to be able to use a set of libraries.
What exactly does target_link_libraries do? Where does it look for the required files in order to be able to use the libraries it needs to?
target_link_libraries doesn't link anything automatically. You should have a target previously created via add_library (or add_executable), where all files are listed.
The way these targets added into your CMake project may differ. E.g. you may have a library source files with CMakeLists.txt configuration (where the said add_library command is) under some folder libs/mylib. Then in your CMakeLists.txt you may have the library added with add_subdirectory(libs/mylib). Another option is to add the library with find_package.
In your specific case, you pass targets to target_link_libraries(). Targets with a shape like Magnum::Application etc are either imported targets or ALIAS targets.
From what you say, it seems than Magnum is vendored into your project, so I guess your are linking ALIAS targets, like the one defined here https://github.com/mosra/magnum/blob/cfc02599e54e02337dd56bb61f70b2e61eb9ce8d/src/Magnum/CMakeLists.txt#L295
Targets defined by add_library() or add_executable() in CMake are an abstraction carrying several informations, including location of files.

What's the shape of CMake library when using find_library()

What is the file structure of CMake library?
For example, here is my library located in library folder:
library
\mylib1.so
\mylib2.so
The headers are in other dir out of library.
Can I use find_library(mylib1 PATHS library) to find my library and use target_include_directories() or include_directories() to include my headers? For my testing, it's failed. So what's the right structure?
Do I need put the header files (.h or .hpp) into library/include folder and put .so in lib folder or put them all into the library folder?
I didn't find any explain in CMake documents. I also find other commands in CMake documents but they are lack of something in details that I don't understand. So I'd like to known how do I find the documents like this.
find_library is used to find a file, but it doesn't create identify provide any information about include directories or other information required by the linking target.
To be able to use your lib after installing it's best to make use of find_package.
You could place files like into a install directory MyLibrary like this:
MyLibrary/lib/MyLib-0.1.1/MyLibraryConfig.cmake
MyLibrary/lib/MyLib-0.1.1/MyLibraryConfigVersion.cmake
MyLibrary/lib/MyLib-0.1.1/libmylibrary.so
MyLibrary/include/MyLib-0.1.1/include/...
MyLibraryConfigVersion.cmake
This file is used to check, if the version of the library is compatible with the build configuration of the project using find_package. Additionaly it provides cmake with information about the version of the library:
set(PACKAGE_VERSION 0.1.1)
if(NOT UNIX # must be the same target platform
OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8 # assuming the installed lib is 64 bit
OR PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE) # inform find_package this is not a suitable version of the lib
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
MyLibraryConfig.cmake
This file is used to actually create the imported lib to use by the project calling find_package; It's only included, if the version file considers the configuration suitable.
if (NOT TARGET MyLibrary) # avoid issues with using find_package(MyLibrary) multiple times
add_library(MyLibrary IMPORTED)
# provide the necessary info about the target
set_target_properties(MyLibrary PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/libmylibrary.so" # specify absolute library file location using the directory containing this cmake file
)
target_include_directories(MyLibrary INTERFACE "${CMAKE_CURRENT_LIST_DIR}/../../include/MyLib-0.1.1/include")
endif()
This allows you to "tell" cmake about the location of the install dir and use find_package like this:
list(APPEND CMAKE_PREFIX_PATH /path/to/MyLibrary) # this could be passed via -D option during configuration instead
find_package(MyLibrary REQUIRED)
Note that there are alternative paths where to put the cmake configuration files, see the documentation of find_package. Furthermore there is cmake functionality that can take over generating some, if not all of the cmake configuration files for you automatically on installation, see
install()
The CMakePackageConfigHelpers cmake module

Android Studio Compiles With Gcc, how to change that to G++ ? (Using CMake)

i have a Android studio project which uses Native C++ code with JNI, however when i try to Build my project, it throws error: undefined reference to 'std::basic_ostream<char...
errors.
I am using ubuntu and i believe the problem is caused by IDE using gcc compiler and tries to compile it as C code. When i try to run simple hello world program by running gcc -o foo foo.cpp in terminal, it gives same error. When i change gcc to g++, everything works fine.
But how do i change the compiler in IDE? I'm kinda new to using CMake and i think i need to use cmake's enable_language function but even i add enable_language(CPP) line to my CMakeLists.txt, it gives same errors.
Here is my CMakeLists.txt :
cmake_minimum_required(VERSION 3.4.1)
include_directories("/usr/include/x86_64-linux-gnu/c++/9")
include_directories("/usr/include/c++/9")
include_directories("/usr/include/x86_64-linux-gnu")
include_directories("/usr/include")
file(GLOB srcs *.cpp *.c)
file(GLOB hdrs *.hpp *.h)
include_directories(${OpenCV_DIR}/jni/include)
include_directories(${TESSERACT_INCLUDE_DIRS})
include_directories(${LEPTONICA_INCLUDE_DIRS})
add_library( lib_opencv SHARED IMPORTED )
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${OpenCV_DIR}/libs/${ANDROID_ABI}/libopencv_java4.so)
add_library(
native-lib
SHARED
#
native-lib.cpp )
find_library(
log-lib
log )
target_link_libraries(
native-lib
lib_opencv
${log-lib}
${TESSERACT_LIBRARIES}
${LEPTONICA_LIBRARIES})
Thanks!
ps: My G++ and Gcc versions are 9.3.0, build-essentials are already installed.
Your CMakeLists.txt is a whole lot of mess. Lets go through some potential problems, one by one, and see if it starts working.
The solution is probably in point 7, but I already wrote it all so I am leaving it here. Let me know if my answer helped you.
1. Does Android Studio really use this CMakeLists.txt file for building?
There are two usual build systems for NDK - older ndk-build and newer CMake. Check your Gradle build file to see if this CMakeLists.txt is really used. Here is documentation about how it should work: https://developer.android.com/studio/projects/gradle-external-native-builds.
And of course, you can make a deliberate syntax error in your CMakeLists.txt file to see if the build systems starts complaining about it.
And note that CMake should not by invoked separately when building for Android. It should be invoked from inside the Gradle script that is used by Android Studio for building the whole project.
2. Do all source files that use C++ libraries have .cpp extension?
CMake determines what compiler to use (C or C++) based on the extension of the file. If it is a .c file, then C compiler is used. If it is a .cpp file, then C++ compiler is used.
Also note that Android NDK now uses Clang (from LLVM project) instead of GCC, so GCC version or custom GCC invocations might not be that relevant.
You can also make CMake verbose - to print all the build commands, to see which commands are exactly executed. Add set(CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON") to your CMakeLists.txt file to make it verbose.
3. Is correct C++ library used?
Symbols you are missing (something from std::basic_ostream) are defined in C++ library (I think) that needs to be linked to your library. By default, libc++ should be linked and that is the right choice. But there is an option that can mess with that. So check your gradle build file for a ANDROID_STL variable and remove any assignments to that variable. The issue of C++ library in NDK is explained here: https://developer.android.com/ndk/guides/cpp-support#cmake
4. Missing source files.
When you call add_library, you are only adding one source file - native-lib.cpp. So only this one file will be compiled in the library (apart from lib_opencv that is linked to it). The line file(GLOB srcs *.cpp *.c) assigns all cpp and c files to the ${srcs} variable but that variable is then left unused. You could change add_library to use that variable: add_library( native-lib SHARED ${srcs} ).
If you want to also include files from subdirectories, use GLOB_RECURSE instead of GLOB in your file(GLOB srcs *.cpp *.c) command.
The line file(GLOB hdrs *.hpp *.h) is completely useless as far as I can tell. I don't see how a list of all header files could be useful in a CMake script.
5. Use new target-based CMake configuration.
Older CMake used file-based configuration, now target-based configuration is encouraged. File-based commands are for example link_libraries and include_directories, their target-based alternatives are target_link_libraries and target_include_directories. You are mixing those two styles. Basically, when a command has its target_ variant, you always want to use it.
You need to first create your target using add_library( native-lib SHARED ${srcs} ), the targets name is native-lib. Then add all include directories and link all libraries to it using target-based commands.
Also the way you add lib_opencv is missing a find_package command. Use find_package(OpenCV REQUIRED) and then target_link_libraries( native-lib PUBLIC ${OpenCV_LIBS} ) to link it.
The way you are adding logging library is OK, because the log-lib library is part of native API (https://developer.android.com/ndk/guides/stable_apis#logging) so it is more straightforward.
With CMake, you need to search internet or documentation to find out, which commands are needed to add each library to your target (unless you are compiling the library from sources). Tthough it is almost always some combination of find_package and target_link_libraries.
6. Missing project information.
I am not sure how important that is, but usually you start your main CMakeLists.txt with something like this:
cmake_minimum_required( VERSION 3.4.1 )
project( my_special_project )
set( CMAKE_CXX_STANDARD 14 )
set( CMAKE_CXX_STANDARD_REQUIRED True )
7. Don't manually include system directories.
Thinking about it, this is probably the problem. Toss out all of these lines:
include_directories("/usr/include/x86_64-linux-gnu/c++/9")
include_directories("/usr/include/c++/9")
include_directories("/usr/include/x86_64-linux-gnu")
include_directories("/usr/include")
Ndk uses its own toolchain, so never manualy add paths from your system toolchain to your NDK project.
I would discourage doing this also on any other project. It is not portable and things like that should be taken care of indirectly, by CMake itself.

Linking against built static libraries rather than using add_subdirectory?

Given a project with "app" and "lib" sibling directories, where "app" builds an executable depending on the (static) library built by "lib". What I'd like is a situation where the normal build process builds only the library, but if I build "app" it builds both "lib" and "app".
What I'm currently doing now is that in app, I include lib with add_subdirectory, but for whatever reason this is pulling in all of lib's indirect dependencies into the link line through a mechanism I'm not aware of. What I'd like is to have my app just build libmylib.a and libmylib.pc, then app could just calculate its own link line from libmylib.pc (or specify it manually), but I'm not sure how that's done.
Here's a minimum working example I've got set up now:
lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.8.0)
project(mylib CXX)
find_package(PkgConfig REQUIRED)
pkg_check_modules("mylib" "libssl")
find_package(Boost REQUIRED)
set(LIBDIR "${PROJECT_SOURCE_DIR}")
set(HEADERS "${LIBDIR}/map_printer.hpp")
set(SOURCES "${LIBDIR}/map_printer.cpp")
add_library("mylib" "${SOURCES}")
target_include_directories("mylib" PUBLIC "${LIBDIR}"
"${Boost_INCLUDE_DIR}"
"${mylib_INCLUDE_DIRS}")
target_link_libraries("mylib" "${Boost_LIBRARIES}" "${mylib_LIBRARIES}")
install(TARGETS "mylib" ARCHIVE DESTINATION "lib")
install(FILES ${HEADERS} DESTINATION "include")
app/CMakeLists.txt
cmake_minimum_required(VERSION 3.8.0)
project(mylib CXX)
set(APPDIR "${PROJECT_SOURCE_DIR}")
set(LIBDIR "${APPDIR}/../lib")
set(SOURCES "${APPDIR}/main.cpp")
add_subdirectory("${LIBDIR}" "build")
list(APPEND LIBS "mylib")
add_executable("myapp" "${SOURCES}")
target_include_directories("myapp" PUBLIC "${LIBDIR}")
target_link_libraries("myapp" "${LIBS}")
install(TARGETS "myapp" DESTINATION "bin")
To get a working example, here are some source files that pull in libssl in the lib (but this function is not used in the app) - I put them in gists because they are only included for completeness and I didn't want to clutter up the question text:
lib/map_printer.cpp
lib/map_printer.hpp
app/main.cpp
The problem is that when I cmake app and then do make VERBOSE=1, the linker command generated is:
/usr/lib/hardening-wrapper/bin/c++ CMakeFiles/myapp.dir/main.cpp.o -o myapp build/libmylib.a -lssl
But I have not specified -lssl anywhere in app. Normally this would be fine, but in my real app, -lssl and several other unnecessary symbols are being included as .so files because of indirect dependencies. When I remove them from the linker command manually, the task builds and runs just fine. Ideally I'd be pulling in the built .a with its .pc file (not generated in this example) and if excess dependencies are necessarily being pulled in, I could tweak the link line manually, but with this method, the linker flags (and possibly other things) are leaking out of the lib scope in some way I don't understand.
Linking is all about resolving symbols so that the final target (standalone executable or shared object) has everything it needs to be launched. Things are simple when you depend only on a single library or upon a collection of libraries that in turn depend on nothing else. This is less likely to be the case for any program of moderate or larger size. The underlying issue is how to deal with transitive dependencies, e.g. the dependencies of the things directly used by your program.
CMake understands all of this and is designed to make it simple for you to consume libraries without understanding the entire dependency graph. If you look at the documentation for target_link_libraries, you'll see the PRIVATE, PUBLIC, and INTERFACE keywords described. This allows you to describe the private requirements of the library (compile definitions, compile arguments, dependent libraries, etc.) that it needs when you build the library. The public part allows you to specify things that both the library and its dependents (consumers) need. The interface part lets you specify things that the dependents need, but not the library itself. The commands target_compile_definitions and target_include_directories operate similarly.
The upshot of all of this is that with a properly declared dependency in CMake, the client of that dependency just adds it to its list of dependencies in its own target_link_libraries command and it naturally picks up all compile definitions, include directories and transitory link dependencies necessary for successful compilation and linking.
A CppCon 2017 presentation Modern CMake for modular design goes over this in more detail.

how to add library source in cmake

i can't seem to find in google/stackoverflow how do i add a library to my project using the library sources so that in make time it will compile the library and then my project and then link between them.
the library i'm trying to add is curlpp
as of now I've tried the following:
add_library(curlpp STATIC IMPORTED ./curlpp)
target_link_libraries(myExec curlpp)
link_directories(./curlpp/src/)
include_directories(./curlpp/includes)
but it has no effect
You first need to add the subdirectory to actually build the library, then you can add the actual library for linking.
If the library is not a CMake project you might need to add a simple CmakeLists.txt file in the library, that executes the actual configuration and building.
You might also use custom commands from the top-level CMakeLists.txt file to configure/build the library.
Basing off on Some programmer dude's response the following commands allowed me to statically link with curlpp:
add_subdirectory(./vendor/curlpp)
set_property(TARGET curlpp PROPERTY IMPORTED_LOCATION ./vendor/curlpp/libcurlpp.a)
target_link_libraries(my_target curlpp)