According to:

This chapter explains step by step the tasks handled by the cmake template created by study-cmake.

This series of cmake-related notes, notes, and books are currently under the [cmake-hello]>(/tags/#cmake-hello) tag.

译 文 : At hedzr.github. IO

Please visit the source code:

Github.com/hedzr/study… Please note that the source code is still being updated with the iteration of my notes.

C + + 11 etc.

CMAKE_CXX_STANDARD is a built-in variable that is used to remove clutter like -stdc++11 -gnu ++11 -std=gnu++11, etc.

We now have a safer and more succinct way to announce:

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# set(CMAKE_CXX_EXTENSIONS OFF)
Copy the code

CMAKE_CXX_STANDARD_REQUIRED initializes the CXX_STANDARD_REQUIRED attribute for each Target. CXX_STANDARD_REQUIRED exists so that if the user does not declare his CXX_STANDARD, it will die — but set(CMAKE_CXX_STANDARD 17) can make these measures meaningless. This is meant to be a better constraint on the compiler’s C++ standard compatibility, but it doesn’t make sense to most people.

Target specific

The old method

For each Target, you should have declared CXX_STANDARD:

set_property(TARGET tgt PROPERTY CXX_STANDARD 11)
Copy the code

But if CMAKE_CXX_STANDARD is already preset, CXX_STANDARD will get the corresponding value.

The new method

set_target_properties(myTarget PROPERTIES
    CXX_STANDARD 11
    CXX_STANDARD_REQUIRED YES
    CXX_EXTENSIONS NO
)
Copy the code

Policies

Do not know whether the translation is a policy, a principle, a precondition or an agreement? I didn’t translate policy when I was working on C++ Template anyway.

CMake Policies are a set of compatibility conventions that determine how CMake interprets cmakelists.txt and whether it determines invalid, outdated, or unrecognized syntaxes and reports errors.

In real life, you often don’t need to care which policies to use and which policies to make forward compatible. Many error messages will tell you that you should enable the Policy compatibility switch at CMP####.

Some of the most commonly used:

# CMake Warning (dev) at CMakeLists.txt:5 (ADD_EXECUTABLE):
# Policy CMP0049 is not set: Do not expand variables in target source
# entries. Run "cmake --help-policy CMP0049" for policy details. Use the
# cmake_policy command to set the policy and suppress this warning.
if (POLICY CMP0049)
  cmake_policy(SET CMP0049 NEW)
endif(a)if (COMMAND cmake_policy)
  # we prefer the more strict behavior, to find out more:
  # cmake --help-policy CMP0003
  cmake_policy(SET CMP0003 NEW)
endif(a)cmake_policy(SET CMP0042 NEW) # ENABLE CMP0042: MACOSX_RPATH is enabled by default.
cmake_policy(SET CMP0048 NEW)
cmake_policy(SET CMP0054 NEW) # ENABLE CMP0054: Only interpret if() arguments as variables or keywords when unquoted.
cmake_policy(SET CMP0063 NEW) # ENABLE CMP0063: Honor visibility properties for all target types.
cmake_policy(SET CMP0069 NEW)
cmake_policy(SET CMP0077 NEW) # ENABLE CMP0077: option() honors normal variables

if (POLICY CMP0068)
  cmake_policy(SET CMP0068 NEW)
endif(a)if (${CMAKE_VERSION} VERSION_LESS 3.12)
  cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif(a)Copy the code

You can skip Policy, which doesn’t prevent you from mastering cmake.

Follow the instructions and read the online documentation in the future when you have a problem. For example, for CMP0054, the command cmake –help-policy CMP0054 gives a brief explanation:

CMP0054
-------

Only interpret ``if() ' 'arguments as variables or keywords when unquoted. CMake 3.1 and above no longer dereference variables or  interpret keywordsin an ``if() ` `command argument when
it is a :ref:`Quoted Argument` or a :ref:`Bracket Argument`.

The ``OLD`` behavior for this policy is to dereference variables and
interpret keywords even if they are quoted or bracketed.
The ``NEW`` behavior is to not dereference variables or interpret keywords
that have been quoted or bracketed.

Given the following partial example:

::

 set(A E)
 set(E "")

 if("${A}" STREQUAL "")
   message("Result is TRUE before CMake 3.1 or when CMP0054 is OLD")
 else()
   message("Result is FALSE in CMake 3.1 and above if CMP0054 is NEW")
 endif()

After explicit expansion of variables this gives:

::

 if("E" STREQUAL "")

With the policy set to ``OLD`` implicit expansion reduces this semantically to:

::

 if("" STREQUAL "")

With the policy set to ``NEW`` the quoted arguments will not be
further dereferenced:

::

 if("E" STREQUAL "")

This policy was introduced in CMake version 3.1.
CMake version 3.18.3 warns when the policy is not set and uses
``OLD`` behavior.  Use the ``cmake_policy()`` command to set
it to ``OLD`` or ``NEW`` explicitly.

.. note::
  The ``OLD`` behavior of a policy is
  ``deprecated by definition``
  and may be removed in a future version of CMake.
Copy the code

If you enable cmake_policy(SET CMP0054 NEW), if you enable cmake_policy(SET CMP0054 NEW), if you enable cmake_policy(SET CMP0054 NEW), The quoted variable name will not be evaluated. It is simply a string, and the quoted variable name must be expanded by ${var}.

As an extension, you can also refer to:

  • Cmake.org/cmake/help/…
  • www.mankier.com/7/cmake-pol…
  • Community.kde.org/Policies/CM…

And so on.

Modules

You can customize findxxx. cmake. When you use find_library(XXX), find_library will look for findxxx. cmake and use it to locate specific modules.

For example, you could write findflex.cmake so that later when you use find_library(FLEX), your script can be used to FindFLEX, a compiler of a well-known lexical compiler.

The same is true for the find_package directive.

However, in this section, just to make room for traditional (old) Modules, Earlier cmake writing conventions REQUIRED that we first write a series of FindXXX methods such as find_package(BOOST REQUIRED) to locate and arrange the third-party libraries we needed to use.

There are also questions about where third-party dependencies are located, whether they should be compiled beforehand, and so on.

In Modern CMake, we only make FindXXX calls selectively within each Target, sometimes even skipping it.

More useful modules

Cmake provides a large number of built-in Modules, of which CheckXXX and FindXXX are the most famous. FindXXX is usually called by the find_package or find_library directive. The purpose of FindXXX is to find the installation location, header file, library file, etc configuration value of a third-party library on the build host. Resolve the corresponding dependencies.

There are some Modules that can be quite useful, so here are a few simple introductions. You can browse all of cmake’s built-in Modules at Modules at GitHub. Cmake-modules (7) — Cmake 3.19.0 Documentation is available in the Documentation section.

CheckCXXCompilerFlag

CheckCXXCompilerFlag (doc, SRC) can be used to check if the compiler flag is supported by the current compiler.

include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-std=c++21" COMPILER_SUPPORTS_CXX21)
check_cxx_compiler_flag("-std=c++17" COMPILER_SUPPORTS_CXX17)
check_cxx_compiler_flag("-std=c++14" COMPILER_SUPPORTS_CXX14)
check_cxx_compiler_flag("-std=c++11" COMPILER_SUPPORTS_CXX11)
check_cxx_compiler_flag("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
    add_definitions(-DCOMPILEDWITHC11)
    message(STATUS "Using flag -std=c++11.") 
endif(a)Copy the code

Note that similar built-in Modules can have [CheckForPthreads], [CheckFunctionExists], [CheckLanguage], [CheckLibraryExists], [CheckLinkerFlag], and so on. You can find them on the cmake-Modules (7) page.

CMakePrintHelpers

CMakePrintHelpers (doc, SRC) contains debugging output functions such as cmake_print_variables and cmake_print_properties.

Example:

cmake_print_variables(var1 var2 ..  varN)
cmake_print_variables(CMAKE_C_COMPILER CMAKE_MAJOR_VERSION DOES_NOT_EXIST)
cmake_print_properties(TARGETS foo bar PROPERTIES
                       LOCATION INTERFACE_INCLUDE_DIRECTORIES)
Copy the code

Cmake_print_properties has the following syntax and format:

cmake_print_properties([TARGETS target1 .. targetN] [SOURCES source1 .. sourceN] [DIRECTORIES dir1 .. dirN] [TESTS test1  .. testN] [CACHE_ENTRIES entry1 .. entryN] PROPERTIES prop1 .. propN )Copy the code

FeatureSummary

Add commands at the beginning of cmakelists.txt:

include(FeatureSummary)
Copy the code

Any find_package statements are then used as usual. The difference now is that all the relevant information in the find process can be gathered somewhere. Eventually, you can output this information somewhere:

if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
    feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES PACKAGES_FOUND)
    feature_summary(FILENAME ${CMAKE_CURRENT_BINARY_DIR}/features.log WHAT ALL)
endif(a)Copy the code

You can also use add_feature_info to embed your own text as you use a lot of find_package. For example:

include(FeatureSummary)

...
find_package(OpenMP)
add_feature_info(WITH_OPENMP OpenMP_CXX_FOUND "OpenMP (Thread safe FCNs only)")... .Copy the code

You can also extend some metadata for a package:

set_package_properties(OpenMP PROPERTIES
    URL "http://www.openmp.org"
    DESCRIPTION "Parallel compiler directives"
    PURPOSE "This is what it does in my package")
Copy the code

GNUInstallDirs

CMake provides a built-in standardized directory set, which is a set of predefined variables for folders that conform to the GNU Coding Standard. They will be used in the install() directive.

We can include it first:

# Must use GNUInstallDirs to install libraries into correct locations on all platforms.
include(GNUInstallDirs)
Copy the code

Then we’ll get a bunch of variables, which are CMAKE_INSTALL_


and CMAKE_INSTALL_FULL_

:

CMAKE_INSTALL_<dir>      - destination for files of a given type
CMAKE_INSTALL_FULL_<dir> - corresponding absolute path
Copy the code

And


will be these names:

BINDIR - user executables (bin) SBINDIR - system admin executables (sbin) LIBEXECDIR - program executables (libexec) SYSCONFDIR - read-only single-machine data (etc) SHAREDSTATEDIR - modifiable architecture-independent data (com) LOCALSTATEDIR - modifiable single-machine data (var) LIBDIR - object code libraries (lib or lib64 or lib/<multiarch-tuple> on Debian) INCLUDEDIR - C header files (include) OLDINCLUDEDIR - C header files for non-gcc (/usr/include) DATAROOTDIR - read-only architecture-independent data root (share) DATADIR - read-only architecture-independent data (DATAROOTDIR) INFODIR - info documentation (DATAROOTDIR/info) LOCALEDIR - locale-dependent  data (DATAROOTDIR/locale) MANDIR - man documentation (DATAROOTDIR/man) DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME)Copy the code

These directories are related to PREFIX: /configure –prefix, Use./configure –prefix=/usr/local to make INSTALL_DIR /usr/local/{bin,include,lib}, etc instead of /usr/{bin,include,lib}. To avoid sudo/root privilege requests; In cmake, this refers to the CMAKE_INSTALL_PREFIX variable, which you can set to the same effect.

For example, CMAKE_INSTALL_FULL_BINDIR=/usr/local/bin is typical

Without CMAKE_INSTALL_PREFIX, it defaults to /usr/local, which is a common default for modern Cmake and modern Linux distributions (and macOS). This topic is quite complicated, however, because sometimes empty values, / and /opt are possible. You can also refer to the Filesystem Hierarchy Standard for these structures in a Filesystem.

CMAKE_INSTTALL_PREFIX is more intended for users of the library, who can select the build purpose they want by cmake -d CMAKE_INSTALL_PREFIX= XXX.

For a user who wants to support multi-arch, he might use /usr/local/lib/x86 as a build purpose, which avoids polluting active arch on the build host. Using /usr/local instead of /usr to avoid sudo requests is another possibility. Other possibilities include different lib variants (e.g. /usr/local/{lib,lib64,lib32,libx32}), cross-compilation environments, and so on.

GoogleTest

TODO

  • GoogleTest

UsewxWidgets

TODO

  • UsewxWidgets

WriteCompilerDetectionHeader

  • WriteCompilerDetectionHeader

More on this in the next section

Detect the system, detect the compiler environment

Detect the build-time system environment

For a system environment, cmake recognizes and places the built-in variable CMAKE_SYSTEM_NAME during the first build preparation, CMAKE_SYSTEM_VERSION and CMAKE_SYSTEM_PROCESSOR (see Possible Values for uname -m for Possible values). If interested in insider can also reference CMakeDetermineSystem cmake in the code.

Because we like MACOS, among other reasons, we made a detect-systems.cmake

# for MacOS X or iOS, watchOS, tvOS (since 3.10.3)
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin" OR APPLE)
  # MESSAGE("** Darwin detected.")
  SET(MACOSX TRUE)
  SET(MACOS TRUE)
  SET(macOS TRUE)
  SET(DARWIN TRUE)
  SET(MAC TRUE)
  SET(Mac TRUE)
endif(a)if (UNIX AND NOT MACOS)
  # for Linux, BSD, Solaris, Minix
  if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
    set(LINUX TRUE)
    set(Linux TRUE)
  elseif (${CMAKE_SYSTEM_NAME} MATCHES "^GNU/kFreeBSD|NetBSD|OpenBSD")
    set(BSD TRUE)
  elseif (${CMAKE_SYSTEM_NAME} MATCHES "Minix")
    set(Minix TRUE)
  endif(a)endif(a)if (WIN32)
  #do something
endif (WIN32)

if (MSVC OR MSYS OR MINGW)
  # for detecting Windows compilers
endif(a)if (LINUX)
  message(STATUS ">>> Linux" " FOUND")
elseif (MAC)
  message(STATUS ">>> macOS" " FOUND")
elseif (UNIX)
  message(STATUS ">>> Unix (BSD+,Unix+)" " FOUND")
elseif (MSVC)
  message(STATUS ">>> MSVC" " FOUND")
elseif (WIN32)
  message(STATUS ">>> Win32" " FOUND")
else(a)message(STATUS ">>> Unknown" " FOUND")
endif(a)Copy the code

Detect the compiler environment

Cmake also does the compiler’s checking automatically. The results of the checks are scattered around the world, so we generally do not make compiler-specific adjustments, and rely on CXX_STANDARD compatibility and expect compilers to meet our CXX_STANDARD declarations.

If there are special syntactic compatibility issues, they are usually solved by using the #if macro in C++ code files.

The compiler and its feature detection in classical C++ code is beyond the scope of this article, so we won’t expand it. But you can actually use cmake instead of the traditional #if macro routine.

So now we are more using the CXX standard and CXX feature support to code. In the modern development of c + +, if pass the test the compiler support way of writing good template code is basically a c + + metaprogramming must learn the steps, but this aspect is no system of teaching material, the article also can calculate, can only through the study of the wind source, such as follow up CppCon others to try to learn.

CMake, however, offers an alternative that is lighter and simpler, with a slight risk of binding. Continue reading the following section:

WriteCompilerDetectionHeader

CMake now supports a called WriteCompilerDetectionHeader (SRC) of the new features, its compiler can be defined as a c + + macro variables and write a c + + header files. In addition, it provides determination of CXX features by detecting predefined macros from various CXX compilers.

Based on these criteria, we can determine the current compiler’s support for CXX standard FEATURES in a relatively simple way. For example, we can check compiler support for templates with variable number of parameters by specifying FEATURES Cxx_variadic_templates.

More CXX feature names can be direct to Symbol Macros, Compatibility Implementation Macros and a series of online documents.

Check the VariADIC Template feature

We made a fragment called cXX-detect-compilers. Cmake to reference this feature:

# https://cmake.org/cmake/help/latest/module/WriteCompilerDetectionHeader.html
# include(WriteCompilerDetectionHeader)
# https://cmake.org/cmake/help/v3.14/manual/cmake-compile-features.7.html#manual:cmake-compile-features (7)
set(WriterCompilerDetectionHeaderFound NOTFOUND)
# This module is only available with CMake >=3.1, so check whether it could be found
# BUT in CMake 3.1 this module doesn't recognize AppleClang as compiler, so just use it as of CMake 3.2
if (${CMAKE_VERSION} VERSION_GREATER "3.2")
  include(WriteCompilerDetectionHeader OPTIONAL RESULT_VARIABLE WriterCompilerDetectionHeaderFound)
endif(a)if (WriterCompilerDetectionHeaderFound)
  write_compiler_detection_header(
          FILE the-compiler.h
          PREFIX The
          COMPILERS GNU Clang MSVC Intel  # AppleClang SunPro
          FEATURES cxx_variadic_templates
  )
endif(a)Copy the code

When the build of cmake is ready, you can refer to the output file the-Compiler.h of this fragment,

#include "the-compiler.h"

#if The_COMPILER_IS_AppleClang
#define COMPILER 1
#endif

#if The_COMPILER_IS_GNU
#define COMPILER 2
#endif

// Since we requested the detection of the VariADIC Template feature, the following is precompiled
// Condition can guarantee compilation correctness.
#if The_COMPILER_CXX_VARIADIC_TEMPLATES
template<int I, int. Is>struct Interface;

template<int I>
struct Interface<I>
{
  static int accumulate(a)
  {
    returnI; }};template<int I, int. Is>struct Interface
{
  static int accumulate(a)
  {
    returnI + Interface<Is... > : :accumulate();
  }
};
#else
template<int I1, int I2 = 0.int I3 = 0.int I4 = 0>
struct Interface
{
  static int accumulate() { returnI1 + I2 + I3 + I4; }};#endif
Copy the code

Don’t forget that the real treasure is in the-Compiler. h, and you’ll learn a lot about how to handle compiler differences correctly by reading it.

Output folder

Output position and installation

These paths are absolute:

  • CMAKE_SOURCE_DIR

    The content is the source tree root directory.

  • CMAKE_BINARY_DIR

    The contents of the binary tree root directory are the same as CMAKE_SOURCE_DIR in the in-source build.

  • PROJECT_SOURCE_DIR

    For each project, the folder in which the cmakelists. TXT file is located for the corresponding project directive.

  • PROJECT_BINARY_DIR

    CMAKE_BINARY_DIR = ${CMAKE_BIANRY_DIR}/ Same as PROJECT_SOURCE_DIR at in-source build.

  • CMAKE_CURRENT_SOURCE_DIR

    The folder in which cmakelists.txt is currently being processed.

  • CMAKE_CURRENT_BINARY_DIR

    The location of the build folder corresponding to cmakelists.txt that is currently being processed.

In addition, _SOURCE_DIR and _BIANRY_DIR are also valid for each PROJECT. You might need it in some situations.

In addition, the installation location has a set of variables, but we only need to know

print

With our debug_print_value() macro you can easily debug and view the actual values of these variables to gain a better understanding of their relationship.

include(utils)
debug_value_print(PROJECT_SOURCE_DIR)
Copy the code

Other output position

EXECUTABLE_OUTPUT_PATH is the output location of the executable file to which the Executable target will be written.

LIBRARY_OUTPUT_PATH is the output location of the library file to which the Library target will be written.

By default, both variables are in CMAKE_BINARY_DIR.

But we can move it out of the build folder to make it easier to call, for example:

set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin")
set(LIBRARY_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin/lib")

# Note that CMAKE_GENERATED_DIR is NOT a cmake builtin variable.
set(CMAKE_GENERATED_DIR "${CMAKE_BINARY_DIR}/generated")
Copy the code

CMAKE_GENERATED_DIR is not a built-in variable, but we define it to be used by Version-gen. For details on version-gen, see the section “Generating version Files” and the management of version.in and version iteration.

Debugging and output

Trace can provide fairly detailed build details that may help you find problems in your own Cmake scripts.

–verbose is useful in many cases, such as cmake –build…. It also increases the richness of the output.

CMAKE_VERBOSE_MAKEFILE

Modify cmakelists.txt to add the following line:

set(CMAKE_VERBOSE_MAKEFILE ON)
Copy the code

Cmake shows the build command line at build time, such as cc, what options link is executing, what intermediate files to link into, etc.

Command-line mode

Instead of modifying cmakelists.txt to add the above line, you can use the command line directly:

cmake -DCMAKE_VERBOSE_MAKEFILE=ON ... options...Copy the code

Or you can use a very strict statement:

cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON
Copy the code

People with OCD will love that.

utils

We provide an utils.cmake file that contains debug_print_value and debug_print_top_value predefined macros or functions that print out the values of the corresponding variables for debugging purposes.

They are complementary to CMakePrintHelpers.

A simple example:

set(A "hello world")
debug_print_value(A)
# will print:
# DEBUG - A = hello world
Copy the code

Debug generated expressions

Refer to the previous section on generating expressions/Debugging.

Generate version number files

Refer to the previous section version.in and version iteration management.

Specify compilation options

Previously we needed to set global variables like CXX_FLAGS. Such as:

SET(CMAKE_CXX_FLAGS "-std-c++11 ${CMAKE_CXX_FLAGS}")
Copy the code

But now we can use add_compile_options and add_definitions without having to manipulate global variables:

add_definitions(-DFOO -DBAR ...)
add_definitions(-std=c++11)     # CMake 2.8.11 or older
add_compile_options(-std=c++11) # CMake 2.8.12 or newer
Copy the code

Finally, we don’t have to manipulate compiler options directly for c++11:

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# set(CMAKE_CXX_EXTENSIONS OFF)
Copy the code

But other cases (non-std =c++11) can still use add_compiler_options.

In the Modern CMake era, global add_compiler_options was recommended with target-oriented target_compile_features, Target_compile_definitions and target_compile_options have been replaced.

target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11)
Copy the code

Multiple build configurations can also be leveraged with generator Expression support. For example, if you want to support both Debug and Release build configurations, you can enable the following statements:

set(MY_DEBUG_OPTIONS "-DDEBUG=1")
set(MY_RELEASE_OPTIONS "-DNDEBUG=1")

target_compile_options(foo PUBLIC "$<$<CONFIG:DEBUG>:${MY_DEBUG_OPTIONS}>")
target_compile_options(foo PUBLIC "$<$<CONFIG:RELEASE>:${MY_RELEASE_OPTIONS}>")
Copy the code

Macros and functions

utility

TODO

Custom macros and functions

In the Study-Cmake sample project, we provide a set of cmake utility functions (custom macros and functions) that are organized in a variety of ways in cmake subfolders.

We load them in order in cmakelists. TXT at the root of the Source Tree to provide a fairly complete base environment — so you can focus on how to write build processing scripts for your Target in cmakelists. TXT subdirectories. Don’t worry about the overall framework of the build — the overall framework of the build script now has a number of basic features.

Source Tree cmakelists. TXT

cmake_minimum_required(VERSION 3.9.3.19)
set(CMAKE_SCRIPTS "cmake")
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/${CMAKE_SCRIPTS}/modules; ${CMAKE_SOURCE_DIR}/${CMAKE_SCRIPTS}; ${CMAKE_SOURCE_DIR}; ${CMAKE_MODULE_PATH}")
include(prerequisites)
include(version-def)
include(add-policies)     # ${CMAKE_SOURCE_DIR}/${CMAKE_SCRIPTS}/
include(detect-systems)
include(target-dirs)
include(utils)


project(study-cmake
        VERSION ${VERSION}
        DESCRIPTION "study for cmake"
        LANGUAGES C CXX)

include(cxx-standard-def)
include(cxx-detect-compilers)
include(setup-build-env)
include(versions-gen)

debug_print_top_vars()


# add_subdirectory(third-party/catch)
# add_subdirectory(src)
# add_subdirectory(test)

# add_library(study_cmake library.cxx library.h)


debug_print_value(CMAKE_RUNTIME_OUTPUT_DIRECTORY)

add_subdirectory(z01-hello-1)
add_subdirectory(z02-library-1)
add_subdirectory(z03-library-2)
add_subdirectory(z04-header-library)

add_subdirectory(z11-m1)
Copy the code

The download file

FetchContent

TODO

FetchContent can be used to download remote files, Git repo, and other remote objects.

Download remote project

Cmake now supports the ExternalProject (SRC) feature, which allows you to download projects directly from a remote server and embed them in the current project. This scheme solves the dependency problem of the third party dependency library well.

ExternalProject

Typical use is from Github (or another Git service) :

ExternalProject_Add(foobar
  GIT_REPOSITORY [email protected]:FooCo/FooBar.git
  GIT_TAG        origin/release/1.2.3
  STEP_TARGETS   build
)
Copy the code

Also available from Source Tarball:

find_program(MAKE_EXE NAMES gmake nmake make)
ExternalProject_Add(secretsauce
  URL               http://intranet.somecompany.com/artifacts/sauce-2.7.tgz
                    https://www.somecompany.com/downloads/sauce-2.7.zip
  URL_HASH          MD5=d41d8cd98f00b204e9800998ecf8427e
  CONFIGURE_COMMAND ""
  BUILD_COMMAND     ${MAKE_EXE} sauce
)
Copy the code

The syntax for ExternalProject looks pretty complicated, but that’s because the various sources have their own requirements for download and compile parameters, and it’s actually not that difficult to use.

For those projects that support modern Cmake style, we can build COMMAND line syntax using cmake when BUILD_COMMAND/COMMAND, or even:

ExternalProject_Add(example
  ... # Download options, etc.
  BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Starting $<CONFIG> build"
  COMMAND       ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG>
  COMMAND       ${CMAKE_COMMAND} -E echo "$<CONFIG> build complete"
)
Copy the code

Use the OPTION

The option directive provides a value defined on the command line if you define it explicitly, otherwise the default value specified by the option directive is applied. Such as:

option(ENABLE_BOOST "Enable Boost" ON)

if(ENABLE_BOOST)
  set(BOOST_STRING "foo")
endif(a)configure_file(config.h.in config.h)
Copy the code

You can tell ENABLE_BOOST to be false by cmake-denable_boost =OFF, in which case Line 3 will not pass.

In many large code bases, this approach provides flexible configuration for code base builds. Such as:

option(BUILD_SHARED_LIBS "Build DOLFIN with shared libraries." ON)
option(CMAKE_USE_RELATIVE_PATHS "Use relative paths in makefiles and projects." OFF)
option(DOLFIN_AUTO_DETECT_MPI "Detect MPI automatically (turn this off to use the MPI compiler wrappers directly via setting CXX, CXX, FC)." ON)
option(DOLFIN_ENABLE_CODE_COVERAGE "Enable code coverage." OFF)
option(DOLFIN_WITH_LIBRARY_VERSION "Build with library version information." ON)
option(DOLFIN_ENABLE_TESTING "Enable testing." OFF)
option(DOLFIN_ENABLE_GTEST "Enable C++ unit tests with Google Test if DOLFIN_ENABLE_TESTING is true (requires Internet connection to download Google Test when first configured)." ON)
option(DOLFIN_ENABLE_BENCHMARKS "Enable benchmark programs." OFF)
option(DOLFIN_ENABLE_DOCS "Enable generation of documentation." ON)
option(DOLFIN_SKIP_BUILD_TESTS "Skip build tests for testing usability of dependency packages." OFF)
option(DOLFIN_DEPRECATION_ERROR "Turn deprecation warnings into errors." OFF)
option(DOLFIN_IGNORE_PETSC4PY_VERSION "Ignore version of PETSc4py." OFF)...Copy the code

Other compilers

When using cmake, environment variables like CC are hard to use. In the ancient days we used CC=/usr/devel/gcc-9/bin/g++./configure to enable a non-default compiler. For cmake, however, the following method achieves a similar goal:

cmake -DCMAKE_C_COMPILER=/usr/local/opt/bin/gcc -DCMAKE_CXX_COMPILER=/usr/local/opt/bin/g++ ...
Copy the code

🔚