How exactly does CMake work? - cmake

I'm not asking this for just myself. I hope this question will be a reference for the many newbies who like me, found it utterly perplexing about what exactly what was going on behind the scenes when for such a small CMakeLists.txt file
cmake_minimum_required (VERSION 2.6)
project(Tutorial)
add_executable(Tutorial tutorial.cpp)
and such a small tutorial.cpp
int main() { return 0; }
there are so many files generated
CMakeCache.txt cmake_install.cmake Makefile
CMakeLists.txt tutorial.cpp
and a CMakeFiles folder with so many files and folders
CMakeCCompiler.cmake CMakeOutput.log Makefile.cmake
cmake.check_cache CMakeSystem.cmake progress.marks
CMakeCXXCompiler.cmake CMakeTmp TargetDirectories.txt
CMakeDetermineCompilerABI_C.bin CompilerIdC Tutorial.dir
CMakeDetermineCompilerABI_CXX.bin CompilerIdCXX
CMakeDirectoryInformation.cmake Makefile2
Not understanding what was going on behind the scenes (i.e: why so may files had to be generated and what their purpose was), was the biggest obstacle in being able to learn CMake.
If anyone knows, could you please explain it for the sake of posterity? What is the purpose of these files, and when I type cmake ., what exactly is cmake configuring and generating before it builds the project?

The secret is that you don't have to understand what the generated files do.
CMake introduces a lot of complexity into the build system, most of which only pays off if you use it for building complex software projects.
The good news is that CMake does a good job of keeping a lot of this messiness away from you: Use out-of-source builds and you don't even have to look at the generated files. If you didn't do this so far (which I guess is the case, since you wrote cmake .), please check them out before proceeding. Mixing the build and source directory is really painful with CMake and is not how the system is supposed to be used.
In a nutshell: Instead of
cd <source_dir>
cmake .
always use
cd <build_dir_different_from_source_dir>
cmake <source_dir>
I usually use an empty subfolder build inside my source directory as build directory.
To ease your pain, let me give a quick overview of the relevant files which CMake generates:
Project files/Makefiles - What you are actually interested in: The files required to build your project under the selected generator. This can be anything from a Unix Makefile to a Visual Studio solution.
CMakeCache.txt - This is a persistent key/value string storage which is used to cache value between runs. Values stored in here can be paths to library dependencies or whether an optional component is to be built at all. The list of variables is mostly identical to the one you see when running ccmake or cmake-gui. This can be useful to look at from time to time, but I would recommend to use the aforementioned tools for changing any of the values if possible.
Generated files - This can be anything from autogenerated source files to export macros that help you re-integrate your built project with other CMake projects. Most of these are only generated on demand and will not appear in a simple project such as the one from your question.
Anything else is pretty much noise to keep the build system happy. In particular, I never needed to care about anything that is going on inside the CMakeFiles subdirectory.
In general you should not mess with any of the files that CMake generates for you. All problems can be solved from within CMakeLists.txt in one way or the other. As long as the result builds your project as expected, you are probably fine. Do not worry too much about the gory details - as this is what CMake was trying to spare you of in the first place.

As stated on its website:
Cmake is cross-platform, open-source build system for managing the build process of software using a compiler-independent method
In most cases it is used to generate project/make files - in your example it has produced Makefile which are used to build your software (mostly on Linux/Unix platform).
Cmake allows to provide cross platform build files that would generate platform specific project/make files for particular compilation/platform.
For instance you may to try to compile your software on Windows with Visual Studio then with proper syntax in your CMakeLists.txt file you can launch
cmake .
inside your project's directory on Windows platform,Cmake will generate all the necessary project/solution files (.sln etc.).
If you would like to build your software on Linux/Unix platform you would simply go to source directory where you have your CMakeLists.txt file and trigger the same cmake . and it will generate all files necessary for you to build software via simple make or make all.
Here you have some very good presentation about key Cmake functionalities http://www.elpauer.org/stuff/learning_cmake.pdf
EDIT
If you'd like to make platform dependent library includes / variable definitions etc. you can use this syntax in CMakeLists.txt file
IF(WIN32)
...do something...
ELSE(WIN32)
...do something else...
ENDIF(WIN32)
There is also a lot of commands with use of which you are able to prevent the build from failing and in place Cmake will notify you that for instance you do not have boost libraries filesystem and regex installed on your system. To do that you can use the following syntax:
find_package(Boost 1.45.0 COMPONENTS filesystem regex)
Having checked that it will generate the makefiles for appropriate system/IDE/compiler.

Exactly how CMake works is a question for the developers, so this question can't be answered here.
However we can give a touch of useful guidance as far as when you should use CMake and when you therefore need to worry about how it works. I'm not a fan of "oh it just works" answers either - because, especially in software, NOTHING ever "just works" and you ALWAYS have to get into the nitty-gritty details at some point.
CMake is an industrial-strength tool. It automates several VERY complex process and takes into account many variables of which you may not be aware, especially as a fairly new developer, probably working with limited knowledge of all the operating systems and build tools CMake can handle. The reason so many files are generated and why things seem so complex is because all of those other systems are complex and must be accounted for and automated. Additionally there are the issues of "caching" and other time-saving features of the tool To understand everything in CMake would mean understanding everything in these build tools and OS's and all the possible combinations of these variables, which as you can imagine is impossible.
It's important to note that if you're not in charge of managing a large cross-platform build system, and your code base is a few KLOC, maybe up to 100KLOG, using CMake seems a little bit like using a 100,000 dollar forestry tree removal machine to remove weeds from your 2 foot by 2 foot flower garden. (By the way, if you've never seen such a machine, you should look for one on youtube, they're amazing)
If your build system is small and simple it's likely to be better to just write your own makefiles by hand or script them yourself. When your makefiles become unwieldy or you need to build a version of your system on another platform, then you can switch over to CMake. At that point, you'll have lots of problems to solve and you can ask more focused questions about it. In the meantime, check out some of the great books that have been written about CMake, or even better, write one yourself! 8)

Related

How to make a CMake package?

I'm attempting to make a CMake package for Crypto++ inclusion in CMake projects, this will end up in the noloader/cryptopp-cmake repo if it gets done.
The ultimate goal is to come up with a working cross-platform FindCryptoPP.cmake file which can be dropped in the Crypto++ source directory to do things like:
find_package(CryptoPP REQUIRED)
target_link_libraries(libbiocoin cryptopp-static)
Or:
find_package(CryptoPP REQUIRED)
target_link_libraries(libbiocoin cryptopp-shared)
In a finished application and have it "just work."
My current best solution within a CMake application is to build Crypto++ for the platform, stick the resulting archive or library in a lib directory, reference that within the CMakeLists.txt and pull it in that way, but of course that requires packaging a binary distribution of the compiled Crypto++ for every platform targeted by the application, which would be nasty to maintain and generally bad even if it weren't crypto code.
It's better to provide a CMake configuration file. find_package will look for a configuration file if no FindFoo.cmake find script is provided. One advantage over a find script is that you won't end with different, maybe conflicting versions of the find script.
See https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html, especially the section Create Layout.

Installing qlfw3 without root access

I'm working on a project that requires the glfw3 package. I have tried installing glfw3 which requires access to "/usr/local/include". But since I don't have root access, permission to make directory GLFW is denied. How can I get around this?
You don't. That's a cmake default. If you want a nice interactive configuration, go to the top level of the source tree and type ccmake . There you'll easily find the option to override the INSTALL_PREFIX. If you hit 't', you get more advanced options. When done, 'g' will generate the Makefiles and exit. I install a lot of my local libraries for my projects in $HOME/local. glfw is one of them. As well as prepending $HOME/local/bin to PATH and $HOME/local/lib to [DY]LD_LIBRARY_PATH. It's served me well, having something of a local sandbox to develop in.
Even better, it installs lib/pkgconfig/glfw3.pc, which makes my life easier, building apps with GLFW, querying dependencies, include paths, library paths, libraries / frameworks by using pkg-config (with PKG_CONFIG_PATH=/Users/brett/local/lib/pkgconfig set in my shell script for all my other .pc metadata files.
It also allows me to integrate GLFW3 easily into autotools. pkg-config provides initialization macros like:
PKG_PROG_PKG_CONFIG([0.29]) # defines PKG_CONFIG variable.
And allows me to write some user-friendly scripts to integrate GLFW3 into my package, e.g.,
AC_MSG_RESULT([])
AC_MSG_RESULT([GLFW3 for OpenGL window and event management...])
PKG_CHECK_MODULES_STATIC([GLFW3], [glfw3 >= 3.2.1],
[ac_glfw3_version=`$PKG_CONFIG glfw3 --modversion`],
[ac_glfw3_version=;])
if test "x$ac_glfw3_version" != x ; then
GLFW_VIS_CXXFLAGS=`echo $GLFW3_CFLAGS`
GLFW_VIS_LIBFLAGS=`echo $GLFW3_LIBS`
AC_MSG_RESULT([GLFW3 version: $ac_glfw3_version])
AC_MSG_RESULT([CXXFLAGS: $GLFW_VIS_CXXFLAGS])
AC_MSG_RESULT([LIBFLAGS: $GLFW_VIS_LIBFLAGS])
else
dnl ... something else? ...
AC_MSG_WARN([the vis library will be omitted from the build:])
AC_MSG_RESULT([GLFW3 # glfw.org])
dnl ... or just AC_MSG_FAILURE? ...
fi
I've resisted cmake for now. As it grows in complexity due to the realities of real software development, I feel it reinvents the autotools wheel. Without the 20+ years of platform quirks and experience to draw on. Except that with cmake you need a whole host of extra support software just to build that wheel...

CMake for Code::Blocks -- how to NOT get a Makefile

Here is my setup:
Windows 7 x64, MingW, Msys, CMake, Freescale Kinetis SDK, Code::Blocks
I'm trying to get the project settings established by CMake into a proper Code::Blocks project. When I modify the provided build_debug.bat file with -G "CodeBlocks - Unix Makefiles", it indeed produces a .cbp file, as well as the normal Makefile (and it builds the project). However when I open this .cbp file in Code::Blocks, it basically just points to the Makefile, and building the project just runs make on the Makefile.
If I deselect "This is a custom Makefile" from Project Options, and add a source file to the project tree like a normal IDE, it doesn't get built correctly, ie the include files, libraries, linker stuff, compile options, etc., are not imported into the project itself. It seems the project is basically just a holder for the Makefile, so there is not much benefit to this as an IDE.
Of course if I add the source file to the original CMakeLists.txt which is part of the distribution, and rerun cmake (via the build_debug.bat file), then it works fine.
So is there any way to get a "real" IDE configuration out of CMake? I'm guessing the answer is No, since a "real" IDE configuration is a static thing, and a Makefile is a general (Turing complete) program, so there is no way in general to create this automatically, although I suspect for 99% of cases you're just specifying include directories, lib files, and compiler options, so no general programmability is truly needed.
I can probably try to figure out where the deeply obscured gcc calls are getting their include files from, what libs are being linked in, and what compile options are being used, and add all that stuff manually into a native Code::Blocks project, but this seems to defeat the purpose of having this already done for me by the package providers, and gets very tedious when building for a different CPU or development board.
Thanks
"Real configuration" is a CMakeLists.txt, and you need to modify CMakeLists when you editing project configuration. Both makefiles and IDE settings generated by CMake are temporary and you should not edit them.
Some IDEs are able to manage project configuration directly in the CMakeLists.txt

cmake: How to debug bad flags

I am currently having a bear of a time trying to compile a moderate sized library with a brand new toolchain, Assimp on Xcode6 with the new iOS 8.0 SDK.
Bundled with the project are various scripts and Xcode projects that have configurations for building on iOS, but unfortunately none of them work out of the box.
So far the farthest I have gotten is by using a build script which uses the cmake "Unix Makefiles" method to assemble static libs. Other methods would include using cmake to generate Xcode projects to use to build. I tried that also to no avail, and neither did the Xcodeproject that comes with the project in the repository (which I later learned was marked deprecated in one of the readme files).
Okay, so with this "Unix Makefiles" cmake script I have been able to generate some of the static libs (after manually forcing static lib generation inside the main CMakeLists.txt), but when it went on to build for i386 and x86_64 architectures for iPhoneSimulator it kept pulling in the headers for iOS which caused a torrent of compiler errors.
Luckily I followed a hunch and found assimp/code/CMakeFiles/assimp.dir/flags.make which is one of the cmake-generated files, and lo and behold, the entire cflags was in here, and once I removed the rogue header include path, the make call finally succeeds and I have my iPhoneSimulator static lib!
Okay so the question that I have is basically where do I get started when debugging these frustrating cmake problems. My relationship with cmake has always been a strained one because none of cmake's complexity and design principles ever made sense to me, and very infrequent are the times when cmake builds work for me out of the box... it is always something that almost works but then I have to spend hours debugging with make VERBOSE=1 and then haphazardly poking at generated files, which are of course all marked with warnings to not edit them as they are generated files.
I realize that some of the variables here are perhaps relevant to my troubles. But it isn't clear to me how I can debug these variables. Where do I go to print out these variables so that I can find which variable contains erroneous values? For example, in this most recent situation I had a -I flag that was cropping up in the wrong place. Luckily I was able to find a file that contained it using various large-hammer methods that involve grep but I am not close to actually fixing the build configuration to make the process any less painful in the future.
For complex CMakeLists.txt files I have found the variable_watch command can sometimes be useful (documentation here). It doesn't make it easy, but gives you another level of information.

Is there any interactive shell for module development in cmake?

CMake is awesome, especially with lots of modules (FindOOXX). However, when it comes to write a FindXXX module, a library XXX which your project depends, it's not that easy to handle for non-cmake-expert. I sometimes encounter a library without support to CMake, and I like to make one for it. I'm wondering if there is any interactive shell while writing/testing cmake modules?
Are you writing FindXXX for project "XXX" or is "XXX" a dependency of your project that you're trying to find? If the former, you should instead write a file called XXX-config.cmake (or XXXConfig.cmake) and install it into one of the directories mentioned in the docs for find_package. In general, XXX-config.cmake files are for projects which are expected to be found by CMake (and installed by the project itself) and FindXXX.cmake files are for projects which don't support CMake (and usually have to support finding any version of XXX).
That said, for FindXXX.cmake, usually you just need a few find_file (e.g., for headers), some find_library calls, or even just a single pkg_check_module from FindPkgConfig.cmake followed by a find_package_handle_standard_args call (use include(FindPackageHandleStandardArgs) to get it). FPHSA makes writing proper Find modules a breeze.
For XXX-config.cmake files, I have typically used configure_file to generate two versions: one for the install (which includes your install(EXPORT) file) and one for the build tree (generated by export() calls). Using this, other useful variables can be accurately set such as things like "which exact version of Boost was used" or "was Python support compiled in" so that dependent projects can get a better picture of what the dependency looks like.
I have also recently discovered that CMake ships with the CMakePackageConfigHelpers module which is supposed to help with making these files. There looks to be quite a bit of documentation for it.