cmake


Missing dependencies for udacity/FCND-Controls C++ project (Flying Car Nanodegree Program)

While taking the Flying Car Nanodegree Program at the third chapter, named Controls, you will be asked to compile a C++ project and build a controller from https://github.com/udacity/FCND-Controls-CPP. On a standard Fedora installation some packages are missing and you will get an error, to resolve that issue you need to install the following two packages on your system:

sudo dnf install -y qt5-devel freeglut-devel;

 

Without these packages you would get the following errors:

CMake Error at CMakeLists.txt:29 (find_package):
By not providing "FindQt5Core.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "Qt5Core", but
CMake did not find one.

Could not find a package configuration file provided by "Qt5Core" with any
of the following names:

Qt5CoreConfig.cmake
qt5core-config.cmake

Add the installation prefix of "Qt5Core" to CMAKE_PREFIX_PATH or set
"Qt5Core_DIR" to a directory containing one of the above files. If
"Qt5Core" provides a separate development package or SDK, be sure it has
been installed.


-- Configuring incomplete, errors occurred!

CMake Error at /usr/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:137 (message):
Could NOT find GLUT (missing: GLUT_glut_LIBRARY GLUT_INCLUDE_DIR)
Call Stack (most recent call first):
/usr/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:378 (_FPHSA_FAILURE_MESSAGE)
/usr/share/cmake/Modules/FindGLUT.cmake:116 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
CMakeLists.txt:34 (find_package)


-- Configuring incomplete, errors occurred!
See also "/home/xeirwn/Downloads/Flying Car/03 - Controls/14 - Control of a 3D Quadrotor/FCND-Controls-CPP/build/CMakeFiles/CMakeOutput.log".

Manually set the CMake output folder

If you want to manually set the global output folder for you whole CMake project and depending on the output you expect add the following configuration lines in the root CMakeLists.txt file of your project:

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

In case you wan to specify those folders per target, you can update them as follows:

set_target_properties( target_or_targets
  PROPERTIES
  ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)

Please note that we are setting the same properties using different variables.

The CMAKE_ARCHIVE_OUTPUT_DIRECTORY variable is used to initialize the ARCHIVE_OUTPUT_DIRECTORY property on all the targets.
ARCHIVE_OUTPUT_DIRECTORY property specifies the directory into which archive target files should be built.
An archive output artifact of a buildsystem target may be:

  • The static library file (e.g. .lib or .a) of a static library target created by the add_library() command with the STATIC option.
  • On DLL platforms: the import library file (e.g. .lib) of a shared library target created by the add_library() command with the SHARED option.
  • On DLL platforms: the import library file (e.g. .lib) of an executable target created by the add_executable() command when its ENABLE_EXPORTS target property is set.

 

The CMAKE_LIBRARY_OUTPUT_DIRECTORY variable is used to initialize the LIBRARY_OUTPUT_DIRECTORY property on all the targets.
LIBRARY_OUTPUT_DIRECTORY property specifies the directory into which library target files should be built.
A library output artifact of a buildsystem target may be:
The loadable module file (e.g. .dll or .so) of a module library target created by the add_library() command with the MODULE option.
On non-DLL platforms: the shared library file (e.g. .so or .dylib) of a shared shared library target created by the add_library() command with the SHARED option.

 

The CMAKE_RUNTIME_OUTPUT_DIRECTORY variable is used to initialize the RUNTIME_OUTPUT_DIRECTORY property on all the targets.
RUNTIME_OUTPUT_DIRECTORY property specifies the directory into which runtime target files should be built.
A runtime output artifact of a buildsystem target may be:

  • The executable file (e.g. .exe) of an executable target created by the add_executable() command.
  • On DLL platforms: the executable file (e.g. .dll) of a shared library target created by the add_library() command with the SHARED option.

From: https://cmake.org/documentation/


Two ways to append a new argument to CMAKE_ARGS list for ExternalProject_Add

Recently, we wanted to pass a new cached value to an external project in CMake via the CMAKE_ARGS variable.

CMAKE_ARGS holds various types of arguments. From which, the arguments in the form -Dname:type=value are passed to the CMake command line and cannot be changed by the user.

We found two ways to add a new argument pair to the CMAKE_ARGS of the external project.

The first method uses the set function:

set(CMAKE_ARGS "${CMAKE_ARGS} -D${CACHE_VAR}${CACHE_VAR_TYPE}=\"${${CACHE_VAR}}\"")

where basically we create a new string that starts with the existing value of CMAKE_ARGS and then we append to its end the new pair.

The second method uses the list function:

list(APPEND CMAKE_ARGS "-D${CACHE_VAR}${CACHE_VAR_TYPE}=${${CACHE_VAR}}")

that treats CMAKE_ARGS as a list and it appends to its end the new pair.

To verify by hand that they were equivalent we did the following small test with success

#Copy the original value of ${CMAKE_ARGS}
set(CMAKE_ARGS_ORIGINAL ${CMAKE_ARGS})

#Define the Name, Type and Value for the new argument pairs
set(CACHE_VAR_P_NAME PRODUCTS_DIR)
set(CACHE_VAR_P_TYPE string)
set(CACHE_VAR_P_VALUE "someplace/with spaces")

set(CACHE_VAR_C_NAME CLIENTS_DIR)
set(CACHE_VAR_C_TYPE string)
set(CACHE_VAR_C_VALUE "another/place")

#Print the original value of ${CMAKE_ARGS}
MESSAGE(STATUS "CMAKE_ARGS: '" ${CMAKE_ARGS} "'")

#Append the two pairs to ${CMAKE_ARGS} using set
set(CMAKE_ARGS "${CMAKE_ARGS} -D${CACHE_VAR_P_NAME}:${CACHE_VAR_P_TYPE}=\"${CACHE_VAR_P_VALUE}\"")
set(CMAKE_ARGS "${CMAKE_ARGS} -D${CACHE_VAR_C_NAME}:${CACHE_VAR_C_TYPE}=\"${CACHE_VAR_C_VALUE}\"")

#Print the modified value of ${CMAKE_ARGS}
MESSAGE(STATUS "CMAKE_ARGS: '" ${CMAKE_ARGS} "'")

#Reset ${CMAKE_ARGS} to its original value
set(CMAKE_ARGS ${CMAKE_ARGS_ORIGINAL})

#Print the original value of ${CMAKE_ARGS} for verification
MESSAGE( STATUS "CMAKE_ARGS: '" ${CMAKE_ARGS} "'")

#Append the two pairs to ${CMAKE_ARGS} using list
list(APPEND CMAKE_ARGS "-D${CACHE_VAR_P_NAME}:${CACHE_VAR_P_TYPE}=\"${CACHE_VAR_P_VALUE}\"")
list(APPEND CMAKE_ARGS "-D${CACHE_VAR_C_NAME}:${CACHE_VAR_C_TYPE}=\"${CACHE_VAR_C_VALUE}\"")

#Print the modified value of ${CMAKE_ARGS}
#Notice that here we surrounded ${CMAKE_ARGS} with quotes so that it is printed as list of delimiter separated values
#Between each element the character ';' will be added because ${CMAKE_ARGS} is surrounded with quotes
MESSAGE(STATUS "CMAKE_ARGS: '" "${CMAKE_ARGS}" "'")

Which resulted in the following output

-- CMAKE_ARGS: ''
-- CMAKE_ARGS: ' -DPRODUCTS_DIR:string="someplace/with spaces" -DCLIENTS_DIR:string="another/place"'
-- CMAKE_ARGS: ''
-- CMAKE_ARGS: '-DPRODUCTS_DIR:string="someplace/with spaces";-DCLIENTS_DIR:string="another/place"'

From the output we can see that the two options treat the variable in a different way as using set just created a huge string containing both pairs while list created a list of two elements.
Both methods seem to work properly but we chose the list method for our external project.

The CMakeLists.txt file that we used to include the external project using custom CMAKE_ARGS resulted as follows:

cmake_minimum_required (VERSION 3.2.2)
include (ExternalProject)

set (TARGET c-banana-eat-banana)
project (${TARGET} C)

set (BANANA_ROOT ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "")

#Add custom CMake arguments to be passed to the CMake command line of the external project
list(APPEND CMAKE_ARGS "-DARTIFACTS_DIR:string=\"${LIBRARIES_DIR}/${TARGET}\"")

ExternalProject_Add (${TARGET}
 URL ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET}.zip
 URL URL_HASH MD5=34734a678729967f426931d913326112
 CMAKE_ARGS "${CMAKE_ARGS}"
 BUILD_IN_SOURCE 1)

Make building with cmake verbose

Ever wanted to get more information out of a build process controlled by CMake?
We sure did and this is how we did it:

Option 1 – Change your CMakeLists.txt files

As our first option, we present updating your CMakeLists.txt file to include the following configuration line:

set(CMAKE_VERBOSE_MAKEFILE ON)

This option by itself is enough to enable verbosity.
A caveat with this option is that the configuration is not passed on to other CMakeLists.txt files that are included to the build using the command add_subdirectory () from the master CMakeLists.txt file.
Thus, you need to copy the configuration file to each CMakeLists.txt file you want to be verbose.

Option 2 – Add the variable VERBOSE=1 to your make command

Assuming you are using a terminal and you are in the folder where you want to build your project.
After you execute the command

cmake $path_to_project_source;

execute your make command using the VERBOSE=1 variable as follows

make VERBOSE=1;

The caveat of this solution is that EVERYTHING becomes verbose, so you could have too many output data.

Option 3 – Add the variable -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON to your cmake command

Adding the option -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON to the cmake command, it will enable verbosity on all generated Makefiles permanently.
So, assuming you are in the folder where you want to make the build, execute the following to generate the Makefiles:

cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON $path_to_project_source;

and then just issue make to perform the build with verbose output.
Please note, you cannot disable verbose output using make VERBOSE=0 after you enable it through cmake, you need to execute the cmake command again without the -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON option.

Bonus

To remove the ‘building’ and ‘linking’ lines from the output

e.g.

[ 10%] Building C object libs/segmentation/CMakeFiles/segments.dir/list_helpers.c.o
and
[ 30%] Linking C static library libsegments.a

add the option -DCMAKE_RULE_MESSAGES:BOOL=OFF to your cmake command to disable them.
e.g.

cmake -DCMAKE_RULE_MESSAGES:BOOL=OFF $path_to_project_source;

To remove the ‘Entering directory’ and ‘Leaving directory’ lines from the output

e.g.

make[2]: Leaving directory '/home/george/Projects/3rd Party/segments222bit/cmake-build-debug'
and
make[2]: Entering directory '/home/george/Projects/3rd Party/segments222bit/cmake-build-debug'

add the option --no-print-directory to your make command to disable them.
e.g.

make --no-print-directory;