Can you have a common cmake minimum included from another file? - cmake

I am quite new to cmake with a makefile background.
I like to use things like include(cmake_utils/header.cmake) to include common snippets of cmake files so that I can include them in my projects but only change them in one once in one place. Where cmake_utils is a git repo.
This is working nicely, but every single CMakeLists.txt I write has to have a cmake_minimum_required.
That is fine, but I may want to change this one day - lets say when one of my common files uses a feature from a newer version of cmake. In that case I don't want to go around changing all the CMakeLists.txt - I just want to change it in one place (ideally).
Here is my current CMakeFile.txt:
cmake_minimum_required(VERSION 3.10.2)
# Include common elements
include(cmake_utils/header.cmake)
include(cmake_utils/cpp_flags.cmake)
# Include path
include_directories(
inc
inc/log4cpp
)
# Include source files by wild card
file(GLOB SOURCES "src/log4cpp/*.cpp")
# Setup output and libs
include(cmake_utils/output_lib_shared.cmake)
include(cmake_utils/common_libs.cmake)
I really want to move the line cmake_minimum_required(VERSION 3.10.2) into my cmake_utils/header.cmake file.
But when I do this I get the following error right at the end of calling cmake:
CMake Error in CMakeLists.txt:
No cmake_minimum_required command is present. A line of code such as
cmake_minimum_required(VERSION 3.10)
should be added at the top of the file. The version specified may be lower
if you wish to support older CMake versions for this project. For more
information run "cmake --help-policy CMP0000".
Is this just a limitation of cmake that I have to live with, or is there a way to archive this?
It's also possible that I am still thinking like a gnu make writer and I have this all horribly wrong :o

Per the documentation cmake_minimum_required: Call the cmake_minimum_required() command at the beginning of the top-level CMakeLists.txt file even before calling the project() command. It is important to establish version and policy settings before invoking other commands whose behavior they may affect.
There is no way of getting around this.

Related

CMake add_subdirectory() for SDL_image out of tree build

I cloned the SDL_image library and wish to build it using a simple add_subdirectory(...) command. My current directory structure is as follows.
├───lib
│ ├───sdl_image
| ├─── ...
├───build
I have a CMakeLists.txt which is as follows.
cmake_minimum_required(VERSION 3.13)
project(test)
add_subdirectory(lib/sdl_image)
Upon execution (For my environment, cmake . -G "MinGW Makefiles"), I get the following error.
CMake Error at lib/sdl_image/CMakeLists.txt:18 (message):
Prevented in-tree built. Please create a build directory outside of the
SDL_image source code and call cmake from there
In an attempt to fix this, I modified my CMakeLists.txt add_subdirectory(...) command as follows. If I understand correctly, this should specify the output directory to build/sdl_image, outside of the SDL_image source code.
...
add_subdirectory(lib/sdl_image build/sdl_image)
However, I still get the same error. The line that is giving me the error under lib/sdl_image/CMakeLists.txt is as follows.
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR ...)
endif()
I don't understand why this condition is getting triggered since I've specified the source_dir and binary_dir (parameters in add_subdirectory(...)) as very different paths. I also tried add_subdirectory(lib/sdl_image ../../build/sdl_image) in case it was treated as relative to the source_dir. This is still not working.
Any help is appreciated. Thanks.
This is about building in sources (calling cmake in your sources), not a path problem where to put sdl_image.
You probably call cmake from within your source directory which is considered a bad practice (same thing when using autotools, or any other build generator).
So you should have some kind of build tree like:
MyProjectWorkspace
|
\_ sources (tree in your case)
\_ build
and invoke cake with cmake ../build from the build directory.
The reason is that when building in sources, you somehow "pollute" your sources. Very likely you will need to add some .gitignore (if using git) and take special care not to commit thing that are built.
Moreover, when generating code, the generated code will appear in the source tree leading to some confusions at some point (you edit the generated file and see it deleted later).
It is also handy: to completely clear a build, you only need to remove the content of the build directory (would be much harder within the sources)
Last but not least, this also ease the packager's job as usually, the use off source builds.

Creating a library in CMake depending on source files not available when generating build files

I have a CMake configuration file building two libraries:
a third-party library (here called ThirdPartyLib) containing a real-time OS / board support package from a supplier. It is built outside CMake using the autotools toolchain.
an extended version of the former library (here called ExtendedThirdPartyLib)
Unfortunately, some source code that I need (various tools) are not built in the ordinary build script for (1). Since I don't want to mess with the suppliers build script I want to add another library (2), building the missing files and thus extending the library from the supplier.
I want to able to do something like this in CMakeFiles.txt:
cmake_minimum_required(VERSION 3.2)
project(bsp)
include(ExternalProject)
ExternalProject_Add(
ThirdPartyLib
URL <http://some.url/bsp.tar.bz2
BUILD_COMMAND make -C ../external/ThirdPartyLib/src
)
set_target_properties(ThirdPartyLib PROPERTIES EXCLUDE_FROM_ALL TRUE)
add_library(ExtendedThirdPartyLib
${CMAKE_CURRENT_BINARY_DIR}/some/path/missing_file1.c
${CMAKE_CURRENT_BINARY_DIR}/some/path/missing_file2.c
)
add_dependencies(ExtendedThirdPartyLib ThirdPartyLib)
target_include_directories(ExtendedThirdPartyLib PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/some/path/include
)
target_link_libraries(ExtendedThirdPartyLib ThirdPartyLib)
The problem here is that the path to missing_file1.c and missing_file2.c are not valid when CMake is generating the build files (they are extracted from the tarball from the supplier). CMake exits with an error output saying: "Cannot find source file".
Is there a neat way to make this work? I.e. is it possible to convince CMake that certain non-existant input files will exist when building of the library begins? Or is there any other recommended way to solve this issue?
(I have temporary made local copies of the files I need to build from the suppliers tarball, but that is of course not a good solution. If those files are changed in future versions of the suppliers package and I forget to overwrite my local copies it could be a horrible mess...
Another "solution" would be to create a small makefile outside CMake and use another ExternalProject_Add in the CMakeFiles.txt somehow. But that's not a good solution either, e.g. if compile and linker flags are modified I need to remember to change the makefile too.)
Personally, I dislike the ExternalProject_Add command, because it does way too many things for my taste, but I've digressed.
What if you do something like this, where bar is simulating your ExtendedThirdPartyLib target, since it depends on generated files
cmake_minimum_required(VERSION 3.11)
project(lol C)
set(SOURCES lol.c) # only this file exists
add_library(lol ${SOURCES})
set(FOO_FILES "foo1.c" "foo2.c")
add_custom_command(OUTPUT ${FOO_FILES}
COMMAND ${CMAKE_COMMAND} -E touch ${FOO_FILES}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Creating ${FOO_FILES}"
VERBATIM)
add_custom_target(foo DEPENDS ${FOO_FILES})
add_library(bar ${FOO_FILES})
add_dependencies(bar foo)
target_link_libraries(lol bar)
The whole approach hinges on the fact that the method, where produced/generated files are procured, is explicitly defined via the custom command and associated custom target.
You should modify the custom command to extract the required files (e.g. could even call some external script) from the tarball (which might require downloading with curl or something similar).

How to use cpplint code style checking with CMake?

The only online resources I have found are the CMake documentation on CMAKE_<LANG>_CPPLINT (link here) and this example (link here), but I cannot figure out how to actually use it inside a CMakeLists.txt file.
I tried the example provided, but I can't make it work. FYI, I installed cpplint as explained here.
As of now, I can run the cpplint python script inside CMakeLists.txt using this CMake command:
execute_process(COMMAND cpplint path/To/File/To/Analyse.cpp)
However, I am pretty sure that this is not the right way to do this.
Recommended way to use static analysis tools with CMake was presented in Daniel Pffeifer's "Effective Cmake" (https://www.youtube.com/watch?v=rLopVhns4Zs&amp=&t=77m13s).
You can either define it when calling cmake, eg.:
cmake "-DCMAKE_CXX_CPPLINT=cpplint" ..
or put it into CMakeLists.txt:
set(CMAKE_CXX_CPPLINT "cpplint")
Recommended option is the first one (we shouldn't define in a project what isn't a project requirement).
CMake will call cpplint for each file it compiles. You can pass extra arguments after semicolon (e.g. -DCMAKE_CXX_CPPLINT=cpplint;--linelength=100).
Downsides of this method:
Errors count will not get accumulated (because cpplint is invoked for each file separately).
It will not check header files (as opposed to what D. Pffeifer says in his presentation, include files are not being scanned by cpplint).
Note that you can use other static analysis tools the same way:
Clan Tidy "-DCMAKE_CXX_CLANG_TIDY=/usr/bin/clang-tidy-3.9;-checks=*"
CppCheck "-DCMAKE_CXX_CPPCHECK=/usr/bin/cppcheck;--std=c++11"
IWYU "-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=/usr/bin/iwyu;--transitive_includes_only"
LWYU cmake -DCMAKE_LINK_WHAT_YOU_USE=TRUE
clazy
Some of them will require "compilation database" (set(CMAKE_EXPORT_COMPILE_COMMANDS ON)).
I failed to use CMAKE_<LANG>_CPPLINT to check code style.
I make it by using add_custom_target.
download cpplint.py
then download cpplint.cmake or write yourselt.
Suppose that there is a source code directory named src in your project, code those statements into your CMakeLists.txt.
aux_source_directory(${CMAKE_SOURCE_DIR}/src src)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}) #I put cpplint.cmake in $CMAKE_SOURCE_DIR
include(cpplint)
add_style_check_target(phoenix-cpplint "${src}")
Note:
you should pass the whole list, so use "${src}" instead of ${src}.
By default nothing depends on the custom target, see add_custom_target.
If there's still some problem, debug your CMakeLists.txt.
I have been struggling with the same problem.
I tried it with CMake 3.10.2 and the comment by user2449761 is still true. Using set(CMAKE_CXX_CPPLINT "cpplint") still does not check any header files.
The answer by kgbook does not work anymore, since aux_source_directory does not list the header files. You can, however, use
get_target_property(src staticcodecheck SOURCES)
That will give you all the non-system headers. The rest can be kept the same. As for running cpplint at a specific time, you might try
add_custom_command(TARGET ${TARGET}
PRE_BUILD
...
That will replace add_custom_target(${TARGET_NAME}... in his cpplint.cmake.
Hope this helps.
The following is how I am running cpplint on all files in the src directory for a project.
file(GLOB_RECURSE SRC_FILES "${PROJECT_SOURCE_DIR}/src/**/*")
add_custom_command(TARGET target PRE_BUILD COMMAND cpplint ${SRC_FILES})
This runs every time, it fails the build when there are cpplint issues, and it runs on all files in the src directory. You may also want to consider adding cpplint specific arguments to the command, such as --quiet or --extensions for example.

Set CMAKE_INSTALL_PREFIX in toolchain.cmake

I have a toolchain.cmake defined for the platform I'm building for and in it I've specified the location where I want my files installed. However, when I run make install, the files go to the default location /usr/..... My toolchain.cmake is setup as follows:
# this one is important
SET(CMAKE_SYSTEM_NAME Linux)
#this one not so much
SET(CMAKE_SYSTEM_VERSION 1)
# specify the cross compiler
SET(CMAKE_C_COMPILER /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/bin/i686-aldebaran-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/bin/i686-aldebaran-linux-gnu-g++)
# where is the target environment
SET(CMAKE_FIND_ROOT_PATH /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot)
SET(CMAKE_SYSROOT /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot)
# search for programs in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
# for libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
SET(THREADS_PTHREAD_ARG 1)
SET(CMAKE_INSTALL_FULL_INCLUDEDIR /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot/usr/local/include)
SET(CMAKE_INSTALL_FULL_LIBDIR /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot/usr/local/lib)
SET(CMAKE_INSTALL_FULL_MANDIR /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot/share/man)
SET(CMAKE_INSTALL_PREFIX /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot)
SET(CMAKE_STAGING_PREFIX /usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot)
As can be seen, I've gone overkill on setting paths, but none of them work. In order to have make install place the file in the correct location, I have to pass -DCMAKE_INSTALL_PREFIX=<path> in the cmake command as shown below:
cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=~/myScripts/Toolchain-Naoqi.2.1.4.13.cmake -DCMAKE_INSTALL_PREFIX=/usr/local/naoqi-sdk-2.1.4.13-mac64/ctc-mac64-atom-2.1.4.13/cross/i686-aldebaran-linux-gnu/sysroot ..
It would be nice not to have the path set in my toolchain.cmake as it applies to all the projects I'm building.
The reason why your paths are overwritten is that the call to project() comes after the execution of this Toolchain file. The project() function, among other things, sets CMAKE_INSTALL_PREFIX to some default value.
It is good practice not to specify CMAKE_INSTALL_PREFIX in your CMakeLists.txt, to ensure cross-platform compatibility. For example, your approach would prevent someone with a non-Unix-like system of using your file.
If you really want to go on and specify it within the code, I see two options:
Hardcode CMAKE_INSTALL_PREFIX in one of the CMakeLists.txt of your project, i.e. after the call to project().
Provide a custom 'platform' file, located in some_folder/Platform/<My_Platform>.cmake, and add some_folder to your CMAKE_MODULE_PATH. In this platform file, you then specify CMAKE_INSTALL_PREFIX, such that you don't need to clobber your other CMakeLists.txt (my assumption here is that you only need this installation folder for this specific platform).
It looks like this issue is caused by a bug in CMake itself, based on an old bug report in their former bug tracker, which was closed without fixing the problem.
The issue is summarised as
Specifying CMAKE_INSTALL_PREFIX in a CMAKE_TOOLCHAIN_FILE file has no
effect
which exactly describes the problem you are encountering.
For what it's worth, I also encounter this same problem when trying to cross-compile with CMake v3.5.1 using a toolchain file, so I don't think that the issue has been fixed.
The migrated bug report in CMake's current bug tracker may be found here.
Besides the workaround you have identified involving setting CMAKE_INSTALL_PREFIX inline in the install command, a more permanent solution might be reached by petitioning the CMake team to fix this bug, by adding your comments to the conversation in the migrated bug report.

Is it possible to alter CMAKE_MODULE_PATH from CMake commandline?

Edit: The accepted answer actually shows that it is pretty normally possible to set CMAKE_MODULE_PATH as any other CMake variable e.g. via the -DCMAKE_MODULE_PATH path CLI parameter. It seems that in my case there is some included CMake script that calls set(CMAKE_MODULE_PATH /library_path), which erases all previous paths set to the variable. That's why I couldn't get the variable to do what I wanted it to do. I'll leave the question here in case anybody else faces this kind of situation.
I'm building a (3rd party) project that uses the Protobuf library (but this question is general). My system has a system-wide install of a newer version of Protobuf than the project is compatible with. So I've downloaded and compiled from source an older version of Protobuf.
The project uses CMake, and in its CMakeLists.txt, there is:
find_package(Protobuf REQUIRED)
Which, however, finds the (incompatible) system install. Of course, CMake doesn't know about my custom build of Protobuf. But how do I tell it?
I've created a FindProtobuf.cmake file in, say, ~/usr/share/cmake-3.0/Modules/ and want the build process to use this one for finding Protobuf. But I haven't succeeded forcing CMake to pick up this one and not the system one. I think the reason is quite obvious from the CMake docs of find_package:
The command has two modes by which it searches for packages: “Module” mode and “Config” mode. Module mode is available when the command is invoked with the above reduced signature. CMake searches for a file called Find<package>.cmake in the CMAKE_MODULE_PATH followed by the CMake installation. If the file is found, it is read and processed by CMake. ... If no module is found and the MODULE option is not given the command proceeds to Config mode.
So until I succeed to change CMAKE_MODULE_PATH, CMake will just pick up the FindProtobuf.cmake installed to the default system path and won't ever proceed to the "Config" mode where I could probably make use of CMAKE_PREFIX_PATH.
It's important for me to not edit the CMakeLists.txt since it belongs to a 3rd party project I don't maintain.
What I've tried (all without success):
calling CMAKE_MODULE_PATH=~/usr/share/cmake-3.0/Modules cmake ... (the env. variable is not "transferred" to the CMake variable with the same name)
calling cmake -DCMAKE_MODULE_PATH=~/usr/share/cmake-3.0/Modules ... (doesn't work, probably by design?)
calling Protobuf_DIR=path/to/my/protobuf cmake ... (the project doesn't support this kind of override for Protobuf)
It seems to me that, unfortunately, the only way to alter the CMAKE_MODULE_PATH used by find_package is to alter it from within CMakeLists.txt, which is exactly what I want to avoid.
Do you have any ideas/workarounds on how not to touch the CMakeLists.txt and still convince find_package to find my custom Protobuf?
For reference, the CMake part of this project is on github .
As a direct answer to your question, yes, you can set CMAKE_MODULE_PATH at the command line by running cmake -DCMAKE_MODULE_PATH=/some/path -S /path/to/src -B /path/to/build.
But that probably doesn't do what you want it to do; see below.
The Bitbucket link you supplied is dead, but here are a few suggestions that might help.
Avoid writing your own find modules, especially when the upstream supplies CMake config modules.
You can direct CMake to your custom Protobuf installation by setting one of CMAKE_PREFIX_PATH or Protobuf_ROOT (v3.12+) to the Protobuf install root.
You can tell find_package to try CONFIG mode first by setting CMAKE_FIND_PACKAGE_PREFER_CONFIG to true (v3.15+). Then set Protobuf_DIR to the directory containing ProtobufConfig.cmake.
Failing all else, you can manually set the variables documented in CMake's own FindProtobuf module, here: https://cmake.org/cmake/help/latest/module/FindProtobuf.html
All these variables can be set at the configure command line with the -D flag.
There are very few environment variables that populate CMake variables to start and I would avoid relying on them. There is an exhaustive list here: https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html. CMAKE_MODULE_PATH is not among them.