Some Lessons from cmake

Posted by acey99 on Sun, 19 May 2019 09:11:41 +0200

Initial users of CMake or those who don't know much about it may often be stumbled by such issues as path inclusion, library search paths, link paths, RPath, which are easy tasks when performing gcc manually or writing makefile s.

In fact, I had a lot of doubts, but after a long time of practice and reading manual, I finally got a relatively clear understanding.

  • How to use manual

cmake's help organization is still very regular, understand its rules, find what you want will be very simple, so personal feel this may be the most important. Its help system is probably of several types:

  • command

This is the longest used in the practical process, equivalent to the basic grammar in the general foot language, including defining variables, foreach, string, if, builtin command are all here.

You can use these commands to get help:

 
cmake --help-commands 
 

This command will give detailed help to all the built-in commands of cmake, which can be used when you don't know what you're looking for or want to flip randomly.

My usual and more common approach is to redirect it to less and search for keywords in the editor.

 

In addition, the search scope can be reduced layer by layer by layer by using the following methods:

cmake --help-command-list
 

cmake --help-command-list | grep find

skyscribe@skyscribe:~/program/ltesim/bld$ cmake --help-command-list | grep find
find_file
find_library
find_package
find_path
find_program

 
 

cmake --help-command find_library

cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem

  find_library
       Find a library.

          find_library(<VAR> name1 [path1 path2 ...])

       This is the short-hand signature for the command that is sufficient in
       many cases.  It is the same as find_library(<VAR> name1 [PATHS path1
       path2 ...])

          find_library(
                    <VAR>
                    name | NAMES name1 [name2 ...]
                    [HINTS path1 [path2 ... ENV var]]
                    [PATHS path1 [path2 ... ENV var]]
                    [PATH_SUFFIXES suffix1 [suffix2 ...]]
                    [DOC "cache documentation string"]
                    [NO_DEFAULT_PATH]
                    [NO_CMAKE_ENVIRONMENT_PATH]
                    [NO_CMAKE_PATH]
                    [NO_SYSTEM_ENVIRONMENT_PATH]
                    [NO_CMAKE_SYSTEM_PATH]
                    [CMAKE_FIND_ROOT_PATH_BOTH |
                     ONLY_CMAKE_FIND_ROOT_PATH |
                     NO_CMAKE_FIND_ROOT_PATH]
                   )

  • variable

and command The help is similar, but you can find it here. cmake You define the variables that you can use directly, for example OSName,Is it? Windows,Unix And so on.

One of my most common examples is:

 
cmake --help-variable-list | grep CMAKE | grep HOST 
CMAKE_HOST_APPLE
CMAKE_HOST_SYSTEM
CMAKE_HOST_SYSTEM_NAME
CMAKE_HOST_SYSTEM_PROCESSOR
CMAKE_HOST_SYSTEM_VERSION
CMAKE_HOST_UNIX
CMAKE_HOST_WIN32
 

Find all of them here. CMake Self-defined builtin Variables; generally related to the system platform.

If you want to place all generated executable files and libraries in the same directory, you can do this:

The target_dir here is an absolute path set for implementation. (There are fewer problems with absolute paths in CMake than with relative paths, if possible with absolute paths)

 
# Targets directory 
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${target_dir}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${target_dir}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${target_dir}/bin)
 
  • property

Property Very few direct changes are required unless you want to modify some default behavior, such as modifying the generated dynamic library files. soname And so on.

For example, if you need to generate both dynamic and static libraries in the same directory, by default, cmake According to what you provided target Names automatically generate similar libtarget.so, libtarget.a,But the same project There can only be one at the same time, because target Must be unique.

At this point, you can modify it. taget The corresponding file name can generate both dynamic and static libraries.

For example:

 
cmake --help-property-list | grep NAME 
GENERATOR_FILE_NAME
IMPORTED_SONAME
IMPORTED_SONAME_<CONFIG>
INSTALL_NAME_DIR
OUTPUT_NAME
VS_SCC_PROJECTNAME
skyscribe@skyscribe:~$ cmake --help-property OUTPUT_NAME
cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem
 
  OUTPUT_NAME
       Sets the real name of a target when it is built.
 
       Sets the real name of a target when it is built and can be used to
       help create two targets of the same name even though CMake requires
       unique logical target names.  There is also a <CONFIG>_OUTPUT_NAME
       that can set the output name on a per-configuration basis.
 
  • module

Used to find commonly used modules, such as boost,bzip2, python And so on. By simple include Commands contain predefined modules, so you can get some variables defined after the module is executed, which is very convenient.

For example, commonly used boost The library can be used in the following ways:

 
# Find boost 1.40 
INCLUDE(FindBoost)
find_package(Boost 1.40.0 COMPONENTS thread unit_test_framework)
if(NOT Boost_FOUND)
    message(STATUS "BOOST not found, test will not succeed!")
endif()
 
Generally, the first part of the explanation is quite useful, which can satisfy 80.%Demand:
cmake --help-module FindBoost | head -40
cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem
 
  FindBoost
       Try to find Boost include dirs and libraries
 
       Usage of this module as follows:
 
       == Using Header-Only libraries from within Boost: ==
 
          find_package( Boost 1.36.0 )
          if(Boost_FOUND)
             include_directories(${Boost_INCLUDE_DIRS})
             add_executable(foo foo.cc)
          endif()
 
       
 
       
 
       == Using actual libraries from within Boost: ==
 
          set(Boost_USE_STATIC_LIBS   ON)
          set(Boost_USE_MULTITHREADED ON)
          find_package( Boost 1.36.0 COMPONENTS date_time filesystem system ... )
 
       
 
          if(Boost_FOUND)
             include_directories(${Boost_INCLUDE_DIRS})
             add_executable(foo foo.cc)
             target_link_libraries(foo ${Boost_LIBRARIES})
          endif()
 
       
 
       
 
       The components list needs to contain actual names of boost libraries
 
  • How to view some key information based on the intermediate files it generates

One advantage of CMake over Autotools is that the generated intermediate files are organized in a very orderly and clear way, unlike autotools, which can generate huge objects like Tianshu (10,000 + is not uncommon).

Generally, the Makefile corresponding to CMake has hierarchical structure, and the corresponding directory structure will be generated in the binary directory according to the relative structure between your CMake Lists. txt.

For example, for a target, a folder can be found under a binary tree: CMakeFiles/<targentName>.dir/, for example:

 
skyscribe@skyscribe:~/program/ltesim/bld/dev/simcluster/CMakeFiles/SIMCLUSTER.dir$ ls -l 
 
total 84 
 
-rw-r--r-- 1 skyscribe skyscribe 52533 2009-12-12 12:20 build.make 
 
-rw-r--r-- 1 skyscribe skyscribe 1190 2009-12-12 12:20 cmake_clean.cmake 
 
-rw-r--r-- 1 skyscribe skyscribe 4519 2009-12-12 12:20 DependInfo.cmake 
 
-rw-r--r-- 1 skyscribe skyscribe 94 2009-12-12 12:20 depend.make 
 
-rw-r--r-- 1 skyscribe skyscribe 573 2009-12-12 12:20 flags.make 
 
-rw-r--r-- 1 skyscribe skyscribe 1310 2009-12-12 12:20 link.txt 
 
-rw-r--r-- 1 skyscribe skyscribe 406 2009-12-12 12:20 progress.make 
 
drwxr-xr-x 2 skyscribe skyscribe 4096 2009-12-12 12:20 src 
Here, each file is a very short text file, the content is quite clear. build.make generally contains dependency rules for intermediate generated files, and DependInfo.cmake generally contains dependency rules for source files themselves.
More importantly, flags.make and link.txt generally contain information similar to GCC - I, such as search path, macro definition, etc. The latter includes linkage information and library search path when target is finally generated.
This information is a good aid for debugging when problems arise.
 
 
  • File lookup, path correlation
    • include

Commonly used are:

 
include_directories()Inclusive search paths for adding header files 
cmake --help-command include_directories
cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem
 
  include_directories
       Add include directories to the build.
 
         include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
 
       Add the given directories to those searched by the compiler for
       include files.  By default the directories are appended onto the
       current list of directories.  This default behavior can be changed by
       setting CMAKE_include_directories_BEFORE to ON.  By using BEFORE or
       AFTER you can select between appending and prepending, independent
       from the default.  If the SYSTEM option is given the compiler will be
       told that the directories are meant as system include directories on
       some platforms.
 
link_directories()Search Path for Adding Find Library Files
 
cmake --help-command link_directories
cmake version 2.6-patch 4
------------------------------------------------------------------------------
SingleItem
 
  link_directories
       Specify directories in which the linker will look for libraries.
 
         link_directories(directory1 directory2 ...)
 
       Specify the paths in which the linker should search for libraries.
       The command will apply only to targets created after it is called.
       For historical reasons, relative paths given to this command are
       passed to the linker unchanged (unlike many CMake commands which
       interpret them relative to the current source directory).
 
  • library search

Generally, there are two ways to link external libraries. One is to display the adding path, using link_directories(), and the other is to find the absolute path of the corresponding library through find_library().

The latter method is better because it can reduce many potential conflicts.

In general, find_library searches for files according to some default rules. If found, it will set the first variable parameter passed in. Otherwise, the corresponding parameter will not be defined, and an x xxx-NOTFOUND is defined. This way, you can debug whether the library search is successful.

For the names of Library files, libxxx.so (xxxx.dll) is automatically searched when searching for dynamic libraries, while libxxx.a (x xxx.lib) is used for static libraries. In the case of mixed use of dynamic libraries and static libraries, some confusion may arise, which requires extra care; in general, matching links should be made as far as possible.

  • rpath

The so-called rpath is related to the loading and running of dynamic libraries. I usually replace the default rpath by the following way:

 
# RPATH and library search setting 
SET(CMAKE_SKIP_BUILD_RPATH  FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/nesim/lib")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 

 

Topics: Programming cmake Linker Makefile less