Creating CMakeLists file from existing Makefile - c++

I want to use cmake to generate my build files for a C++ project. I have an existing Makefile.
I am having problems generating this Makefile using the standard cmake syntax.
How do I include standard C++ libraries like -lstdc++ -lpthread -lboost_thread-mt in the TARGET_LINK_LIBRARIES section of cmake? Or should these files be included in the ADD_DEPENDENCIES section.
(OR)
Is there a simple tool which generates a CMakeList.txt file from a Makefile

Unfortunately, there is no straightforward 1:1 conversion from Makefiles to CMakeLists. Since CMake is supposed to run on all platforms, it can not rely on platform specific assumptions like GNU make does, which complicates things in certain places.
In particular, CMake offers a very powerful and rather complex mechanism for using libraries: You call find_package with the name of your library, which will invoke a library search script from your cmake module path. This script (which is also written in CMake) will attempt to detect the location of the library's header and lib files and store them in a couple of CMake variables that can then be passed to the according CMake commands like include_directories and target_link_libraries.
There are two problems with this approach: First, you need a search script. Fortunately, CMake ships with search scripts for Pthreads, Boost and a couple of others, but if you are using a more exotic library, you might have to write the search script yourself, which is kind of an arcane experience at first...
The second major problem is that there is no standard way for a search script to return its results. While there are naming conventions for the used variables, those often don't apply. In practice that means you will have to check out a search script's source to know how to use it. Fortunately, the scripts that come with CMake are mostly very well documented.
The builtin scripts are located somewhere like <cmake-install-prefix>/share/cmake-2.8/Modules. For your question, look at the FindBoost.cmake and FindThreads.cmake files (CMake should automatically link with the standard library). Anycorn already gave some sample code for using the Boost script, everything else you need to know is in the CMake documentation or directly in the search script files.

Like this:
target_link_libraries(your-target-name pthread boost_thread-mt etc)
You should not use add_dependencies when you want to link libraries. Linking implies a dependency, but the dependency alone will not be sufficient when you need to link.

With Boost you really need to use package finder
set(Boost_ADDITIONAL_VERSIONS "1.46" "1.46.0" "1.46.1")
set(Boost_USE_MULTITHREADED ON) # for -mt
find_package(Boost COMPONENTS thread)
if(Boost_FOUND)
MESSAGE(STATUS "Found Boost: ${Boost_LIBRARY_DIRS}")
MESSAGE(STATUS "Found Boost libraries: ${Boost_LIBRARIES}")
set(LIBRARIES "${LIBRARIES};${Boost_LIBRARIES}")
else()
MESSAGE(FATAL_ERROR "Boost Thread NOT FOUND")
endif()
target_link_libraries(executable ${LIBRARIES})

Related

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.

CMake FetchContent does not copy libraries

I am using CMake FetchContent to download and build a third party library(realsense2 in this case). After trying out the googletest example from the official documentation (https://cmake.org/cmake/help/v3.11/module/FetchContent.html) I was impressed how easy it works. Including headers was done magically. Now with realsense2 SDK I have a problem.
I need to do add an additional include_directories command like this:
FetchContent_Declare(
realsense2
GIT_REPOSITORY https://github.com/IntelRealSense/librealsense.git
GIT_TAG v2.23.0
)
FetchContent_MakeAvailable(realsense2)
FetchContent_GetProperties(realsense2)
if(NOT realsense2_POPULATED)
FetchContent_Populate(realsense2)
add_subdirectory(${realsense2_SOURCE_DIR} ${realsense2_BINARY_DIR})
endif()
//I should not be required to do this according to documentation
include_directories(${realsense2_SOURCE_DIR}/include)
If I do not do this, some headers are not found. Any suggestions regarding this problem?
EDIT: To clarify, this is how I added the libraries:
target_link_libraries(TestExe gtest gtest_main)
and the other exactly the same but this time it is not an exe its a dll
add_library(TestLib SHARED ${TestLib_HEADERS} ${TestLib_SOURCES} )
target_link_libraries(TestLib realsense2)
At this point I am more concerned about why I do not have to add any includes for googletest framework
The main purpose of FetchContent is a garantee that at the time of call
add_subdirectory(${Foo_SOURCE_DIR} ${Foo_BINARY_DIR})
the "fetched" project will be (as sources) in the ${Foo_SOURCE_DIR} directory.
How to use the project inluded via add_subdirectory is completely up to that project:
Some projects (including gtest) create the library target Foo in a "modern" CMake way, by associating properties with it using target_include_directories and other commands. So, to use a library like this, it is sufficient to call target_link_libraries.
Some other projects require both include_directories and target_link_libraries to work with them.
Finally, there are many projects, which simply don't work when included via add_subdirectory. So FetchContent makes little sense for them.
Only a small subset of projects describe how to work with them via add_subdirectory approach. And gtest is among them.
But most projects simply don't describe this; if you want to use add_subdirectory with such a project, then you need to investigate the internals of that project to understand its usage (or use trial and error).

How to know variable such as 'OpenCV' in CMake

I am using OpenCV with gcc and cmake. And I found a tutorial https://docs.opencv.org/3.4.0/db/df5/tutorial_linux_gcc_cmake.html .In the file CMakeLists.txt, there are some variables such as OpenCV and OpenCV_INCLUDE_DIRS.
cmake_minimum_required(VERSION 3.9)
project(VideoRecord)
set(CMAKE_CXX_STANDARD 11)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(VideoRecord main.cpp)
target_link_libraries(VideoRecord ${OpenCV_LIBS})
I want to know where to find these variables definition.
EDIT
Thanks #qbranchmaster's answer. I tried to search FindOpenCV.cmake but failed.
First try.
➜ ~ cmake --help-module-list | grep "FindOpen"
FindOpenACC
FindOpenAL
FindOpenCL
FindOpenGL
FindOpenMP
FindOpenSSL
FindOpenSceneGraph
FindOpenThreads
Another try.
➜ / find . "FindOpenCV.cmake"
In addition, my os is osx and I install cmake with brew. I comiple and install OpenCV manually.
These variables are part of the package config script shipping with OpenCV.
Note that find_package is a two-headed beast. The classic mode of operation is finding libraries through find-scripts. This is still the approach being used today for third-party libraries that are not aware of CMake. However, if your dependency is itself being built with CMake, it can provide a package config file instead, which allows for a more powerful mode of operation.
The idea here is that instead of you telling CMake how to find a dependency, the dependency itself tells CMake how clients can find it. This is the approach that is taken by libraries like OpenCV and Qt.
To answer your question, those variables are being set by the package config file in your local OpenCV installation, the template of which can be found in the OpenCV source code under cmake/templates/OpenCVConfig.cmake.in.
They are defined in CMake OpenCV module. CMake has numerous modules that aid in finding various libraries like OpenCV (FindOpenCV.cmake module).
Using this command you can get a list of modules that your CMake supports:
cmake --help-module-list
Some libraries come with their own *.cmake modules which should be installed in some system path. If you are using Ubuntu, your cmake modules should be localised in:
/usr/share/cmake/Modules/
If not, just search system for file FindOpenCV.cmake. In that file you will find these variables.
In general, you get variable names from the documentation or source code of the package you want to find.
Often you can derive the name to put into find_package from the provided FindFoo.cmake module file name, because "Foo" would be the name. The find module is either part of CMake or comes with the third-party library.
If there is no find module, some modules provide FooConfig.cmake files, where "Foo" is again the string to put into find_package.
If you have neither a find nor a config file, you need to find the library by other means, e.g., FindPkgConfig or find_library / find_file.

Convert from Xcode to CMake

I have a C++ project that I initially developed using Xcode on my Mac and I would like to set up the very same project using CMake. The project makes use of some external libraries, including eigen and boost. Xcode has a rather long list of Build Settings, including paths to external libraries, specification of the compiler version, additional linker flags, compiler optimization level, etc... and I am wondering where all of this information goes in the CMakeLists.txt file. I've searched extensively for help on this but have found precious little. I am new to CMake and have never written make files before. If there were a utility that could convert my Xcode project into a CMake project, that would be ideal. But I would be very glad to know of a tutorial on this, or to have some specific guidance. Any help on this would be greatly appreciated.
Conversion Strategy
I am pretty sure that the easiest and fastest way to move to CMake is a mixture of looking at comparable projects that link the same dependencies, and maybe copying a few relative file paths to your source files from XCode.
A well-written CMakeLists.txt that uses Eigen and Boost can be very small (and platform-independent).
One reason for this is CMake has a very different way to use dependencies.
For well-known libraries such as Boost, CMake includes scripts to include headers and link libraries.
Minimal CMakeLists.txt
A minimal example may look like this:
find_package(Eigen3 REQUIRED)
find_package(Boost REQUIRED COMPONENTS system)
add_executable(
app
src/main.cpp
)
target_include_directories(
app
${Eigen3_INCLUDE_DIRS}
${Boost_INCLUDE_DIR}
)
target_link_libraries(
app
${Boost_LIBRARIES}
)
Example Projects
For Eigen, there is no such pre-installed script that can be called by find_package.
Here are two real-world example projects that link Eigen and Boost: https://github.com/ompl/ompl/blob/master/CMakeLists.txt
https://github.com/roboticslibrary/rl
Here is an example for a find script for Eigen.
As a piece of advice, some CMake projects are out there are convoluted and use old syntax, newer project files tend to use small letters in commands.
If it happens that directly target linking libraries doesn't work, you'll have to write your own Find*.cmake files by yourself.
Here's a spec of what you should cover to make it (this is just one approach):
Imagine you have a library XYZ:
Your file
FindXYZ.cmake should have as a result a set of variables to use:
XYZ_LIBRARIES
XYZ_INCLUDE_DIR
For example:
set(XYZ_LIBRARIES ${SOME_PATH_TO_LIBRARY1} ${SOME_PATH_TO_LIBRARY2})
find_path(XYZ_INCLUDE_DIR NAMES xyz/xyz.h HINTS ${PLACE_WHERE_INCLUDE_SHOULD_BE}
PATH_SUFFIXES include
)
From your main cmake maybe you 'use' it:
find_package(XYZ REQUIRED)
# Now we use both _INCLUDE_DIR and _LIBRARIES
include_directories(SYSTEM "${XYZ_INCLUDE_DIR}")
# TARGET_NAME is the current target, the 'client' of XYZ
# append XYZ to our list of libraries to link
target_link_libraries(${TARGET_NAME} ${XYZ_LIBRARIES})
I'm surely missing details but that's the general idea. Here are some cmake scripts that I know work in a big project:
Example with OpenSSL
Find:
https://github.com/highfidelity/hifi/blob/Android/cmake/modules/FindOpenSSL.cmake
Client (in this case is a library that indeed needs OpenSSL):
https://github.com/highfidelity/hifi/blob/Android/domain-server/CMakeLists.txt

How do I write system-independent code when there are paths involved?

Say I am creating a project that uses a certain library and I have to provide the path for that library while linking. In the command line or makefile I might have:
g++ ... -L/path/to/mylibrary
I'm also going to send this project to someone else who wants to use it. The path on their system might not necessarily be the same as mine. They could be using a different file path all together.
How do I make sure that the path to the library works for both my computer and the recipient of my project?
This is the role of a build system or build configuration tool. There are many of those around. The main one is probably CMake as it has a very extensive feature set, cross-platform, and widely adopted. There are others like Boost.Jam, autoconf, and others.
The way that these tools will be used is that they have automated scripts for looking into the file-system and finding the headers or libraries that you need, i.e., the dependencies required to compile your code. They can also be used to do all sorts of other fancy things, like checking what features the OS supports and reconfiguring the build as a consequence of that. But the point is, you don't hard-code any file-paths into the build configuration, everything is either relative to your source folder or it is found automatically by the build script.
Here is an example CMake file for a project that uses Boost:
cmake_minimum_required (VERSION 2.8)
project (ExampleWithBoost)
find_package(Boost 1.46 COMPONENTS thread program_options filesystem REQUIRED)
# Add the boost directory to the include paths:
include_directories(SYSTEM ${Boost_INCLUDE_DIR})
# Add the boost library directory to the link paths:
link_directories(${Boost_LIBRARY_DIRS})
# Add an executable target (for compilation):
add_executable(example_with_boost example_with_boost.cpp)
# Add boost libraries to the linking on the target:
target_link_libraries(example_with_boost ${Boost_LIBRARIES})
The find_package cmake function is simply a special script (specialized for Boost, and installed with CMake) that finds the latest version of boost (with some minimal version) installed on the system, and it does so based on the file-name patterns that the library uses. You can also write your own equivalents of find_package, or even your own package finders, using the functions that CMake provides for searching the file system for certain file-name patterns (e.g., regular expressions).
As you see, the build configuration file above only refer directly to your source files, like "example_with_boost.cpp", and it's only relative to the source folder. If you do things right, the configuration scripts will work on virtually any system and any OS that CMake supports (and that the libraries you depend on support). This is how most major cross-platform projects work, and when you understand how to work with these systems, it's very powerful and very easy to use (in general, far easier to use and trouble-free than build configurations that you do by point-and-click within IDE menus like in Visual Studio).
You can use premake that generates cross platform makefiles: Visual Studio, Gcc and others
http://industriousone.com/what-premake
CMake is another alternative
http://www.cmake.org/
I'm not sure if there's a single universal way of doing this, but people often provide different config files and let the main Makefile detect which one to include: linux.make, darwin.make, cygwin.make etc.
there are also various tools like CMake that allow to automate this, but all in all it's just scripting that hides the system-dependency from the developer.