CMake官方教程

這是一個(gè)來(lái)自官網(wǎng)的step-by-step的CMake教程

起點(diǎn)(Step 1)

參看下面的CMakeLists.txt文件,最簡(jiǎn)單的一個(gè)工程需要有一個(gè)這樣的cmake文件,一共就這么兩行

  cmake_minimum_required (VERSION 2.6)
  project (Tutorial) 
  add_executable (Tutorial tutorial.cxx)

注意到這里都是用的小寫的命令,在cmake文件里面大小寫不嚴(yán)格區(qū)分,都可以用。
add_executable添加一個(gè)可編譯的目標(biāo)到工程里面

add_executable(<name> [WIN32] [MACOSX_BUNDLE]
               [EXCLUDE_FROM_ALL]
               source1 [source2 ...])
  • name: 工程所要構(gòu)建的目標(biāo)名稱
  • WIN32/..: 目標(biāo)app運(yùn)行的平臺(tái)
  • source1:構(gòu)建目標(biāo)App的源文件

tutorial.cxx的源代碼計(jì)算一個(gè)數(shù)的平方根。第一版的代碼很簡(jiǎn)單,參看如下:

  // A simple program that computes the square root of a number
  #include <stdio.h>
  #include <stdlib.h>
  #include <math.h>

  int main (int argc, char *argv[])
  {
    if (argc < 2)
    {
      fprintf(stdout, "Uage: %s number\n", argv[0]);
      return 1;
    }
    double inputValue = atof(argv[1]);
    double outputValue = sqrt(inputValue);
    fprintf(stdout, "The square root of %g is %g\n",
              inputValue, outputValue);
    return 0;
  }

編寫完上面兩個(gè)文件以后,在根目錄下新建一個(gè)build目錄
.
├── build
├── CMakeLists.txt
└── tutorial.cxx
然后運(yùn)行如下命令

  $ cd buld
  $ CMake ..
  $ make
  • CMake會(huì)自動(dòng)加載上級(jí)目錄里面的CMakeLists.txt文件,編譯所需的文件都會(huì)生成在build目錄下
  • make之后會(huì)生成可執(zhí)行文件Tutorial

添加一個(gè)版本號(hào)和配置的頭文件

修改CMakeList.txt來(lái)添加version number:

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
# 版本號(hào).
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
 
# 配置一個(gè)頭文件來(lái)傳遞一些CMake設(shè)置到源代碼
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  )
 
# 添加TutorialConfig.h的路徑到頭文件的搜索路徑
include_directories("${PROJECT_BINARY_DIR}")
 
# 添加目標(biāo)可執(zhí)行文件
add_executable(Tutorial tutorial.cxx)

configure_file會(huì)拷貝一個(gè)文件到另一個(gè)目錄并修改文件內(nèi)容:

configure_file(<input> <output>
               [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
               [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])

cmake會(huì)自動(dòng)定義兩個(gè)變量

  • ${PROJECT_SOURCE_DIR}: 當(dāng)前工程最上層的目錄
  • ${PROJECT_BINARY_DIR}: 當(dāng)前工程的構(gòu)建目錄(本例中新建的build目錄)

在這個(gè)例子里,configure_file命令的源文件是TutorialConfig.h.in,手動(dòng)創(chuàng)建這個(gè)文件:

// Tutorial工程的配置選項(xiàng)和設(shè)置
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

調(diào)用CMake的時(shí)候會(huì)在build目錄下新的頭文件,并且使用CMakeList.txt中定義的值來(lái)替換@Tutorial_VERSION_MAJOR@和@Tutorial_VERSION_MINOR@這兩個(gè)變量。
下一步要在源文件tutorial.cxx中包含這個(gè)配置的頭文件,就能使用這些版本信息了。

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

  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, "Uage: %s number\n", argv[0]);
      return 1;
    }
    double inputValue = atof(argv[1]);
    double outputValue = sqrt(inputValue);
    fprintf(stdout, "The square root of %g is %g\n",
              inputValue, outputValue);
    return 0;
  }

運(yùn)行如下命令查看結(jié)果

  $ cmake ..
  $ make
  $ ./Tutorial

這個(gè)時(shí)候控制臺(tái)會(huì)打印出來(lái)版本號(hào)

./Tutorial Version 1.0
Uage: ./Tutorial number

添加Library(Step 2)

現(xiàn)在我們嘗試添加一個(gè)library到我們的工程。這個(gè)lib提供一個(gè)自定義的計(jì)算平方根的函數(shù),用來(lái)替換編譯器提供的函數(shù)。
lib的源文件放到一個(gè)叫MathFunctions的子目錄中,在目錄下新建CMakeList.txt文件,添加如下的一行

 add_library(MathFunctions mysqrt.cxx)

源文件mysqrt.cxx包含一個(gè)函數(shù)mysqrt用于計(jì)算平方根。代碼如下

#include "MathFunctions.h"
#include <stdio.h>

// a hack square root calculation using simple operations
double mysqrt(double x)
{
  if (x <= 0) {
    return 0;
  }

  double result;
  double delta;
  result = x;

  // do ten iterations
  int i;
  for (i = 0; i < 10; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    delta = x - (result * result);
    result = result + 0.5 * delta / result;
    fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
  }
  return result;
}

還需要添加一個(gè)頭文件MathFunctions.h以提供接口給main函數(shù)調(diào)用

double mysqrt(double x);

現(xiàn)在的目錄結(jié)構(gòu)
.
├── build
├── CMakeLists.txt
├── MathFunctions
│ ├── CMakeLists.txt
│ ├── MathFunctions.h
│ └── mysqrt.cxx
├── TutorialConfig.h.in
└── tutorial.cxx

CMakeLists.txt文件需要相應(yīng)做如下改動(dòng)

  • 添加一行add_subdirectory來(lái)保證新加的library在工程構(gòu)建過(guò)程中被編譯。
  • 添加新的頭文件搜索路徑MathFunction/MathFunctions.h。
  • 添加新的library到executable。

CMakeList.txt的最后幾行變成了這樣:

include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions)

# 添加executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Turorial MathFunctions)

最后要修改tutorial.cxx文件來(lái)調(diào)用自定義的mysqrt函數(shù)
最后編譯一下試試

  $ cmake ..
  $ make
  $ ./Tutorial

看一下編譯的log

Scanning dependencies of target MathFunctions
[ 50%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.o
Linking CXX static library libMathFunctions.a
[ 50%] Built target MathFunctions
Scanning dependencies of target Tutorial
[100%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
Linking CXX executable Tutorial
[100%] Built target Tutorial

這里編譯生成了新的庫(kù)libMathFunctions.a

現(xiàn)在我們考慮把MathFunctions庫(kù)配置成可選的

首先在最頂層的CMakeList.txt文件添加一個(gè)option

#需要用自定義的數(shù)學(xué)函數(shù)么?
option (USE_MYMATH
            "Use tutorial provided math implementation" ON)

運(yùn)行ccmake ..會(huì)跳出來(lái)配置的GUI,在GUi中會(huì)看到新添加的這個(gè)選項(xiàng),用戶可以根據(jù)需要進(jìn)行修改。
下一個(gè)改變是依據(jù)配置來(lái)判斷是否編譯和鏈接MathFunctions庫(kù)。按照如下所示的修改CMakeList.txt的末尾幾行:

# add the MathFunctions library?
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
  add_subdirectory (MathFunctions)
  SET (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)

# add executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Turorial ${EXTRA_LIBS})

這個(gè)例子里還是用了變量(EXTRA_LIBS)來(lái)收集后面link進(jìn)可執(zhí)行文件的時(shí)候任意可選的庫(kù)。這是一個(gè)常用的方法,在工程非常大有很多optional的組件的時(shí)候,可以讓這個(gè)編譯文件保持干凈。
代碼的修改就更直接了(用宏定義隔離開(kāi)):

// 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 "MathFunctions.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);
#else
  double outputValue = sqrt(inputValue);
#endif
 
  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}

在源代碼里面同樣可以使用USE_MYMATH,只要在TutorialConfig.h.in里面添加一行

#cmakedefine USE_MYMATH

安裝測(cè)試 (Step 3)

下一步我們會(huì)添加install規(guī)則和testing到工程。install規(guī)則非常直接。對(duì)于MathFunctions庫(kù),我們通過(guò)在MathFunctions的CMakeList文件中添加如下兩行來(lái)安裝庫(kù)和頭文件。

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

對(duì)于應(yīng)用程序,為了安裝executable和配置頭文件,需要在最上層的CMakeList.txt文件中添加下面幾行

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

到了這里你就可以構(gòu)建整個(gè)tutorial了,通過(guò)命令make install,系統(tǒng)會(huì)自動(dòng)安裝對(duì)應(yīng)的頭文件,庫(kù)以及可執(zhí)行文件。CMake變量CMAKE_INSTALL_PREFIX用來(lái)指定這些文件需要安裝到哪個(gè)根目錄。
添加測(cè)試用例也很直接,只要在最上層的CMakeList.txt文件添加一系列的基礎(chǔ)測(cè)試來(lái)驗(yàn)證應(yīng)用程序是否正常工作。

include(CTest)

# does the application run
add_test (TutorialRuns Tutorial 25)

# does it sqrt of 25
add_test (TutorialComp25 Tutorial 25)
set_tests_properties (TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")

# does it handle negative numbers
add_test (TutorialNegative Tutorial -25)
set_tests_properties (TutorialNegative PROPERTIES PASS_REGULAR_EXPRESSION "-25 is 0")

# does it handle small numbers
add_test (TutorialSmall Tutorial 0.0001)
set_tests_properties (TutorialSmall PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01")

# does the usage message work?
add_test (TutorialUsage Tutorial)
set_tests_properties (TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")

編譯完成后可以通過(guò)在命令行運(yùn)行ctest來(lái)執(zhí)行這些測(cè)試用例。如果你希望添加很多測(cè)試用例來(lái)測(cè)試不同的輸入值,這個(gè)時(shí)候推薦你創(chuàng)建一個(gè)宏,這樣添加新的case會(huì)更輕松:

#define a macro to simplify adding tests, then use it
macro (do_test arg result)
  add_test (TutorialComp${arg} Tutorial ${arg})
  set_tests_properties (TutorialComp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro (do_test)
 
# do a bunch of result based tests
do_test (25 "25 is 5")
do_test (-25 "-25 is 0")

每次調(diào)用do_test,都會(huì)添加一個(gè)新的test case到工程。

添加系統(tǒng)回顧 (Step 4)

下一步我們考慮在工程中添加一些代碼,這些代碼會(huì)依賴的某些特性在運(yùn)行的目標(biāo)平臺(tái)上可能沒(méi)有。比如說(shuō),我們添加了一些代碼,這些代碼需要用到log和exp函數(shù),但某些目標(biāo)平臺(tái)上可能沒(méi)有這些庫(kù)函數(shù)。如果平臺(tái)有l(wèi)og函數(shù)那么我們就是用log來(lái)計(jì)算平方根,我們首先通過(guò)CheckFunctionExists.cmake來(lái)測(cè)試一下是否有這些函數(shù),在最上層的CMakeList文件中添加如下內(nèi)容

# does this system provide the log and exp functions?
include (CheckFunctionExists)
check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP)

Next we modify the TutorialConfig.h.in to define those values if CMake found them on the platform as follows:
下一步,如果CMake發(fā)現(xiàn)平臺(tái)有我們需要的這些函數(shù),則需要修改TutorialConfig.h.in來(lái)定義這些值

// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP

有一點(diǎn)很重要,就是log和exp的測(cè)試工作需要在配置TutorialConfig.h前完成。最后在mysqrt函數(shù)中我們可以提供一個(gè)可選的實(shí)現(xiàn)方式:

// if we have both log and exp then use them
#if defined (HAVE_LOG) && defined (HAVE_EXP)
  result = exp(log(x)*0.5);
#else // otherwise use an iterative approach
  . . .

添加生成文件和生成器 (Step 5)

這一節(jié)中我們會(huì)演示一下怎么添加一個(gè)生成的源文件到引用程序的構(gòu)建過(guò)程中。例如說(shuō),我們希望在構(gòu)建過(guò)程中創(chuàng)建一個(gè)預(yù)先計(jì)算好的平方根表,然后把這個(gè)表格編譯進(jìn)我們的應(yīng)用程序。首先我們需要一個(gè)能生成這張表的程序。在MathFunctions子目錄中,定義一個(gè)新的源文件MakeTable.cxx:

// 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(static_cast<double>(i));
    fprintf(fout,"%g,\n",result);
    }
 
  // close the table with a zero
  fprintf(fout,"0};\n");
  fclose(fout);
  return 0;
}

注意到這里的需要傳遞正確的輸出文件給app,然后才會(huì)生成table。下一步是在MathFunctions的CMakeList.txt添加相應(yīng)的命令來(lái)編譯生成可執(zhí)行文件MakeTable,然后在編譯過(guò)程中運(yùn)行這個(gè)程序。如下所示的添加一些命令:

# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
 
# add the command to generate the source code
add_custom_command (
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  )
 
# add the binary tree directory to the search path for 
# include files
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
 
# add the main library
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h  )
  • 首先添加可執(zhí)行的MakeTable。
  • 然后我們添加一個(gè)用戶命令指定怎么通過(guò)允許MakeTable來(lái)生成Table.h。
  • 下一步需要讓CMAKE知道m(xù)ysqrt.cxx依賴生成的Table.h。把生成的Table.h添加到MathFunctions庫(kù)的資源列表中。
  • 還需要添加當(dāng)前的bin的目錄添加到include的list中,這樣mysqrt.cxx編譯時(shí)候可以找到Table.h。
  • 最后編譯包含Table.h的mysqrt.cxx來(lái)生成MathFunctions庫(kù)
    到這兒最上層的CMakeList.txt文件就如下面所示:
cmake_minimum_required (VERSION 2.6)
project (Tutorial)

include(CTest) 
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0) 

# does this system provide the log and exp functions?
include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake) check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP) 

# should we use our own math functions
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 ("${PROJECT_BINARY_DIR}")

# add the MathFunctions library?
if (USE_MYMATH) 
  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
  add_subdirectory (MathFunctions) 
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH) 

# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial ${EXTRA_LIBS}) 

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

# does the application run
add_test (TutorialRuns Tutorial 25) 
# does the usage message work?
add_test (TutorialUsage Tutorial)
set_tests_properties (TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number" ) 
#define a macro to simplify adding tests
macro (do_test arg result) 
  add_test (TutorialComp${arg} Tutorial ${arg}) 
  set_tests_properties (TutorialComp${arg} PROPERTIES PASS_REGULAR_EXPRESSION ${result} )
endmacro (do_test) 

# do a bunch of result based tests
do_test (4 "4 is 2")
do_test (9 "9 is 3")
do_test (5 "5 is 2.236")
do_test (7 "7 is 2.645")
do_test (25 "25 is 5")
do_test (-25 "-25 is 0")
do_test (0.0001 "0.0001 is 0.01")

TutorialConfig.h.in 文件如下:

// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
#cmakedefine USE_MYMATH 
// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP

MathFunctions的文件CMakeLists.txt如下:

# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
# add the command to generate the source code
add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h DEPENDS MakeTable COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h )

# add the binary tree directory to the search path 
# for include files
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) 

# add the main library
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h) 
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • CMake學(xué)習(xí) 本篇分享一下有關(guān)CMake的一些學(xué)習(xí)心得以及相關(guān)使用。 本文目錄如下: [1、CMake介紹] [...
    AlphaGL閱讀 12,441評(píng)論 11 79
  • CMake是我非常喜歡且一直使用的工具。它不但能幫助我跨平臺(tái)、跨編譯器,而且最酷的是,它幫我節(jié)約了太多的存儲(chǔ)空間。...
    行之與亦安閱讀 100,695評(píng)論 5 56
  • 文章翻譯自:CMake Tutorial 第一步 | 第二步 | 第三步 | 第四步 | 第五步 | 第六步 | ...
    汪坤閱讀 14,478評(píng)論 1 23
  • 1.安裝 $sudo apt-get install cmake 2.示例:簡(jiǎn)單的文件目錄 sample |—...
    荷包蛋醬閱讀 29,905評(píng)論 0 15
  • 一切智慧都在行動(dòng)中產(chǎn)生。
    斐麗希婭閱讀 221評(píng)論 0 0

友情鏈接更多精彩內(nèi)容