cmakelists.txt usage macro definition

Described in the following aspects:

1. How to describe each LIB to be compiled into a static library or a dynamic library, and how to describe each TOOL to be compiled into an executable file?

2. LIB and TOOL may depend on other LIBs, how should they be described?

3. Each LIB and TOOL will include a lot of header files. How to deal with the same header files and how to deal with private header files?

4. Some usages in CMake

5. Techniques used in LLVM

Here is a simple example:
//
└─tutorial.c
└─TutorialConfig.h.in
└─CMakeLists.txt
└─include
 └─mysqrt.h
└─lib
└─CMakeLists.txt
 └─MakeTable.c
 └─mysqrt .c

In this example, Top level and lib will define a CMakeLists.txt to specify compilation rules. This example mainly does the following things:
1) Use the macro USE_MYMATH to control whether to use self-defined sqrt()

  1. A MakeTable.c file is defined to generate a result table of sqrt(), and simple results can be obtained by looking up the table in the sqrt() function defined by yourself.

3) Or calculate the value of sqrt through exp and log, but only if the current library supports exp and log, these are all checked by check_function_exists to check whether they are supported, and if the corresponding macro is supported, it will be turned on.

4) Through CTest, define the corresponding test case and check whether the execution result is correct.


Tutorial.c in Top Level :

// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
#ifdef USE_MYMATH
#include "mysqrt.h"
#endif

int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"%s Version %d.%d\n", argv[0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,"Usage: %s number\n",argv[0]);
    return 1;
    }

  double inputValue = atof(argv[1]);

#ifdef USE_MYMATH
  double outputValue = mysqrt(inputValue);
  printf ("call local sqrt\n");
#else
  double outputValue = sqrt(inputValue);
#endif

  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}

CMakeList.txt is defined as follows:

cmake_minimum_required (VERSION 2.6)
project (Tutorial)

# The version number.
//"set"用来给对应的变量赋值
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
set(include_dir ${CMAKE_CURRENT_SOURCE_DIR}/include)

//"MESSAGE"用来打印信息,包括变量的值
MESSAGE(STATUS "include folder: " ${include_dir})

//"option"用来定义宏,"ON"表示打开,"OFF"表示关闭
option (USE_MYMATH "Use tutorial provided math implementation" ON)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  )

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
//"include_directories"用来指定build时需要的头文件路径
//"PROJECT_BINARY_DIR"是内置变量,表示工程编译的目录,也就是--prefix指定的目录
include_directories("${PROJECT_BINARY_DIR}" "${include_dir}")

if (USE_MYMATH)
    include_directories(${include_dir})
    //"add_subdirectory"用来添加外部项目目录,将指定的目录添加到build任务列表中。
    //在这里"lib"目录是否需要编译,是通过宏USE_MYMATH控制,
    //如果这个宏打开,就需要编译"lib",也就是需要通过"add_sudirectory"添加"lib"
    add_subdirectory(lib)
    set(EXTRAL_LIBS ${EXTRAL_LIBS} MathFunctions)
endif(USE_MYMATH)

# add the executable
// "add_executable"用来指定生成可执行文件,这里会生成Tutorial.out或者Tutorial.exe
add_executable(Tutorial tutorial.c)

// 由于tutorial.c中需要用到sqrt函数,这个函数要么来自于系统库,要么来自于自定义库,
//因此这里需要通过"target_link_libraries"指定链家的libraries
target_link_libraries(Tutorial m ${EXTRAL_LIBS}) # link to -lm

install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" DESTINATION include)

//引入CTest, 添加相应的测试用例,正确的测试结果通过set_tests_properties指定。
//在这里也就是说sqrt(25) = 5,否则就会failed
#include(CTest)
MESSAGE(STATUS "Meditator testttttttttttt")
enable_testing()
add_test (TutorialRuns Tutorial 25)
add_test (TutorialComp25 Tutorial 25)
set_tests_properties(TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")

//check_function_exists检查当前库是否支持log和exp,如果支持相应的宏就会打开。
include(CheckFunctionExists)
check_function_exists(log HAVE_LOG)
check_function_exists(exp HAVE_EXP)

The mysqrt.c file is as follows:

#include "mysqrt.h"
#include "Table.h"
double mysqrt(double input)
{
#if defined (HAVE_LOG) && defined (HAVE_EXP)
    return = exp(log(x) * 0.5);
#else
    if (input < 10)
        return sqrtTable[(int)input];
    else
        return 0;
#endif
}

The MakeTable.c file mainly defines an array to store a set of sqrt(i) values, the contents of which are as follows:

// A simple program that builds a sqrt table 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main (int argc, char *argv[])
{
  int i;
  double result;

  // make sure we have enough arguments
  if (argc < 2)
    {
    return 1;
    }

  // open the output file
  FILE *fout = fopen(argv[1],"w");
  if (!fout)
    {
    return 1;
    }

  // create a source file with a table of square roots
  fprintf(fout,"double sqrtTable[] = {\n");
  for (i = 0; i < 10; ++i)
    {
    result = sqrt((double)i);
    fprintf(fout,"%g,\n",result);
    }

  // close the table with a zero
  fprintf(fout,"0};\n");
  fclose(fout);
  return 0;
}

The content of CMakeLists.txt under lib is as follows:

nclude_directories(${include_dir})
MESSAGE(STATUS "Meditator test cmake_current_souce_dir" ${CMAKE_CURRENT_SOURCE_DIR})

set(MAKETABLE_SRC MakeTable.c)
add_executable(MakeTable ${MAKETABLE_SRC})
target_link_libraries(MakeTable m) # link to -lm

//通过执行COMMAND,产生Table.h文件
add_custom_command (
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
    COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
    DEPENDS MakeTable
    )

include_directories(${CMAKE_CURRENT_BINARY_DIR})

set(MYSQRT_SRC mysqrt.c)
add_library(MathFunctions ${MYSQRT_SRC} ${CMAKE_CURRENT_BINARY_DIR}/Table.h)

install(TARGETS MathFunctions DESTINATION bin)
install(FILES mysqrt.h DESTINATION include)

The following explains several CMake built-in variables
1. CMAKE_CURRENT_SOURCE_DIR
refers to the path where the currently processed CMakeLists.txt is located. When processing CMaleLists in different directories, the value of this macro is different. In Top level, this value is the directory where Top Level is located, and in lib, this value is the directory where lib is located.
2. CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
These three variables refer to the same content. If it is in source compilation, it refers to the top-level directory of the project. If it is out-of-source compilation, it refers to the directory where the project compilation occurs. . PROJECT_BINARY_DIR is slightly different from other commands. Now, you can understand that they are consistent.
3. PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR is
used to indicate the directory where the current project is located.
4. INCLUDE_DIRECTORIES is
used to specify the header file path required during build
5. LINK_DIRECTORIES is
used to specify the path where the third-party library is located, which can be understood as -L
6 in gcc . TARGET_LINK_LIBRARIES is
used to specify the libraries that the project depends on
7. OPTION (USE_MYMATH "Use tutorial provided math implementation" ON)
Use option in CMakeLists to control whether the current USE_MYMATH is turned on, and the corresponding option will appear in cmake-gui, the default is ON
8. Usage of Function

function(<name>[arg1 [arg2 [arg3 ...]]])
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...

endfunction(<name>)

Define a function named <name>, and the parameter names are arg1 arg2 arg3(...). The commands in the function body will not be executed until the function is called. The ARGC variable represents the number of parameters passed to the function. ARGV0, ARGV1, ARGV2 represent the actual parameters passed to the function. ARGN represents the parameter list beyond the last expected parameter. For example, when the function prototype is declared, only one parameter is accepted, then the parameter list passed to the function when the function is called will be saved starting from the second parameter (if any) To ARGN.
Give a simple example

cmake_minimum_required(VERSION 2.8)  
 project(ArgumentExpansion)  
   
function (argument_tester arg)  
    message(STATUS "ARGN: ${ARGN}")  
    message(STATUS "ARGC: ${ARGC}")  
    message(STATUS "ARGV: ${ARGV}")  
    message(STATUS "ARGV0: ${ARGV0}")  
  
    list(LENGTH ARGV  argv_len)  
    message(STATUS "length of ARGV: ${argv_len}")  
    set(i 0)  
    while( i LESS ${argv_len})  
         list(GET ARGV ${i} argv_value)  
         message(STATUS "argv${i}: ${argv_value}")  
         math(EXPR i "${i} + 1")  
    endwhile()   
endfunction ()  
argument_tester(arg0 arg1 arg2 arg3) 

====output====
-- ARGN: arg1;arg2;arg3
-- ARGC: 4
-- ARGV: arg0;arg1;arg2;arg3
-- ARGV0: arg0
-- ARGV1: arg1
-- length of ARGV: 4
-- argv0: arg0
-- argv1: arg1
-- argv2: arg2
-- argv3: arg3

9. LIST usage

set(SRC)  
list(APPEND SRC a.cpp b.cpp)  
list(APPEND SRC c.cpp d.cpp)  
  
function(tst_arguments src_list)  
    message("src_list = "${src_list})  
endfunction()  
  
message("SRC = "${SRC})  
tst_arguments(${SRC})  
  
==== output ====  
SRC = a.cppb.cppc.cppd.cpp  
src_list = a.cpp  

10. CMAKE_PARSE_ARGUMENTS

CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords> <multi_value_keywords> args...)  

prefix is ​​a prefix
<option> is a list, which can contain some KeyWords or macros that you are interested in, and then you can use it to see if the KeyWord you need is set. Only when option is used in the calling place, it means that this macro is open.
<one_value_keywords> is a KeyWord list of single value parameters.
<multi_value_keywords> is a KeyWord list of multi-value parameters (such as list)

function(tst_arguments)  
  CMAKE_PARSE_ARGUMENTS(  
    TEST "" "NAME;COMMAND;BASELINE"  
       "ARGSLIST"  
       ${ARGN}  
  )  
  
  message("TEST_DEFAULT_ARGS is ${TEST_DEFAULT_ARGS} from ${ARGN}")  
  message("TEST_NAME is ${TEST_NAME}")  
  message("TEST_COMMAND is ${TEST_COMMAND}")  
  message("TEST_ARGSLIST is ${TEST_ARGSLIST}")  
  message("TEST_BASELINE is ${TEST_BASELINE}")  
  
endfunction(tst_arguments) 

The prefix here is TEST, <one_value_keywords> we set the KeyWord (NAME; COMMAND; BASELINE) of the single-value parameter, which will indicate the relationship between KeyWord and Value in the subsequent function call, <multi_value_keywords> we set the KeyWord of the multi-value parameter ("ARGSLIST").
Call functions:

TEST_ARGUMENT(  
    NAME  
      testiso  
    COMMAND  
      "RunMe"  
    ARGSLIST  
      ${SRC}  
    BASELINE  
      "/home/sakaue/iWork"  
)  
  
==== output ====  
TEST_DEFAULT_ARGS is  from NAME;testiso;COMMAND;RunMe;ARGSLIST;a.cpp;b.cpp;c.cpp;d.cpp;BASELINE;/home/sakaue/iWork  
TEST_NAME is testiso  
TEST_COMMAND is RunMe  
TEST_ARGSLIST is a.cpp;b.cpp;c.cpp;d.cpp  
TEST_BASELINE is /home/sakaue/iWork  

Here is an example of a function that is more frequently used in LLVM:

macro(add_llvm_library name)
  if( BUILD_SHARED_LIBS )
    llvm_add_library(${name} SHARED ${ARGN})
  else()
    llvm_add_library(${name} ${ARGN})
  endif()
  set_property( GLOBAL APPEND PROPERTY LLVM_LIBS ${name} )
  ...
  ...
endmacro(add_llvm_library name)

function(llvm_add_library name)
  cmake_parse_arguments(ARG
    "MODULE;SHARED;STATIC"
    "OUTPUT_NAME"
    "ADDITIONAL_HEADERS;DEPENDS;LINK_COMPONENTS;LINK_LIBS;OBJLIBS"
    ${ARGN})
  list(APPEND LLVM_COMMON_DEPENDS ${ARG_DEPENDS})
  if(ARG_ADDITIONAL_HEADERS)
    # Pass through ADDITIONAL_HEADERS.
    set(ARG_ADDITIONAL_HEADERS ADDITIONAL_HEADERS ${ARG_ADDITIONAL_HEADERS})
  endif()
  if(ARG_OBJLIBS)
    set(ALL_FILES ${ARG_OBJLIBS})
  else()
    llvm_process_sources(ALL_FILES ${ARG_UNPARSED_ARGUMENTS} ${ARG_ADDITIONAL_HEADERS})
  endif()

This function is used in many CMakeLists of LLVM, such as the lib/IR directory

add_llvm_library(LLVMCore
  AsmWriter.cpp
  Attributes.cpp
  AutoUpgrade.cpp
  BasicBlock.cpp
  Comdat.cpp
  ConstantFold.cpp
  ConstantRange.cpp
  Constants.cpp
  Core.cpp
  DIBuilder.cpp
  ...
  ...

  )
add_dependencies(LLVMCore intrinsics_gen)

From the above CMakeLists, we can see that
name = LLVMCore in llvm_add_library(${name} SHARED ${ARGN}) ; ARGN is the dependent .cpp files. So is passed to cmake_parse_arguments
report this content share .cpp. So the
prefix is ​​ARG,
ARG_SHARED = TRUE,
ARG_MODULE=FALSE,
ARG_STATIC=FALSE.
Since
.cpp does not match any other parameters,
ARG_UNPARSED_ARGUMENTS = *.cpp
Note that <prefix>_UNPARSED_ARGUMENTS is used to indicate those parameters that do not match.

As we can see in LLVM, CMakeLists in lib generally do not specify the header file path , such as in LLVM:

#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"

The main reasons are based on the following two points:
1. CMake uses the include_directories command to add the include path of the header file, and the include_directories command is
inherited. The subordinate directory inherits the directorories of include in CMakeLists.txt in the superior directory. But the include_directories in CMakeList.txt between level directories cannot be shared.

  1. By default, all header files in the current directory will be searched, excluding header files in subdirectories

In LLVM's CMakeLists.txt also see a lot of such code:

include(AddLLVM)
include(TableGen)

What are AddLLVM and TableGen here? There are many functions defined in it. For example, llvm_add_library, which was used a lot before, is defined in llvm/cmake/modules. But CMAKE_MODULE_PATH must be specified before including these modules , as described in llvm:

set(CMAKE_MODULE_PATH
  ${CMAKE_MODULE_PATH}
  "${CMAKE_CURRENT_SOURCE_DIR}/cmake"
  "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules"
)



Author: WalkHeart
link: https: //www.jianshu.com/p/5a63414f0691
Source: Jane book
Jane book copyright reserved by the authors, are reproduced in any form, please contact the author to obtain authorization and indicate the source.

Guess you like

Origin blog.csdn.net/ynshi57/article/details/90021572