[翻譯]CMAKE官方教程

本篇文章講對官方教程進行翻譯。本篇文章將構建完整的CMAKE項目分紅了7個步驟。緩存

第一步

一個最基礎的項目就是從源代碼中構建一個可執行程序。對一個簡單的項目來講,CMakeLists.txt有兩行代碼是必需的。咱們的教程就從這裏開始。CMakeLists.txt文件看上去是這樣的:bash

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)
複製代碼

咱們注意到,這個例子的指令都是小寫的。實際上,CMAKE支持小寫命令,大寫命令或者大小寫混用的命令。tutorial.cxx的將實現一個計算一個數字的平方根的功能,它的第一個版本是很是簡單的:app

// 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,"Usage: %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;
}
複製代碼

增長版本號&配置頭文件

咱們要往CMakeLists.txt添加的第一個特性爲咱們的可執行文件和咱們的項目增長版本號。你徹底能夠在源代碼中作這件事情,可是在CMakeLists.txt中會更加的靈活。爲了增長版本號,咱們須要對CMakeLists.txt作一些改動:ide

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
 
# 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 executable
add_executable(Tutorial tutorial.cxx)
複製代碼

既然配置文件會被寫入二進制樹,所以咱們必須在搜索路徑中添加配置文件所在路徑。緊接着,咱們在源碼樹中建立TutorialConfig.h,內容以下:函數

// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
複製代碼

當CMAKE在配置這個頭文件中的@Tutorial_VERSION_MAJOR@@Tutorial_VERSION_MINOR@時,會從CMakeLists.txt中獲取值進行替換。 接着,咱們修改tutotial.cxx引用頭文件,從而可使用版本號。源代碼以下:工具

// 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,"Usage: %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;
}
複製代碼

主要的變化是引用了TutorialConfig.h頭文件和在使用信息中打印了版本號。測試

引用庫(第二步)

如今,咱們將爲項目添加庫。這個庫將包含咱們對計算平方根的實現。可執行文件將引用這個庫用以取代編譯器提供的標準平方根實現。在這個教程中,咱們會將庫添加到名爲MathFunctions的子路徑。這在CMakeLists.txt須要一行指令:ui

add_library(MathFunctions mysqrt.cxx)
複製代碼

mysqrt.cxx有一個名爲mysqrt的函數,提供了與編譯器提供平方根計算類似的功能。爲了可以使用新的庫,咱們在 CMakeLists.txt中添加了子路徑,使得這個庫可以被CMAKE編譯。咱們也添加了其餘的搜索目錄,使得MathFunctions/MathFunctions.h頭文件可以被引用。最後一個變化是添加了新的庫到可執行文件中。CMakeLists.txt的最後幾行是這樣的:this

include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions) 
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)
複製代碼

如今,讓咱們考慮一下爲MathFunctions庫提供選項。在這個教程中,咱們沒什麼必要這麼作,可是對於一個更大的庫或者須要依賴其餘第三方庫的庫來講,這可能頗有必要。第一步是在CMakeLists.txt中添加選項:spa

# should we use our own math functions?
option (USE_MYMATH 
        "Use tutorial provided math implementation" ON) 
複製代碼

這麼作,會讓CMAKE GUI工具顯示一個可供用戶修改的選項,默認值是on。設置會被保存在緩存中,用戶沒必要每次都進行設置。下一個變化是條件編譯連接MathFunctions。爲了實現這個功能,咱們在CMakeLists.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 the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial  ${EXTRA_LIBS})
複製代碼

使用了USE_MYMATH來決定MathFunctions是否須要被編譯和使用。注意到使用變量(本案例是EXTRA_LIBS)爲後續連接成可執行程序收集可選庫。這個作法會使得更大的項目更加清晰的處理可選組件問題。相應的,咱們須要在源代碼中進行處理:

// 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.在ToturialConfig.h頭文件中新增如下代碼,就能夠爲CMAKE提供該變量。

#cmakedefine USE_MYMATH
複製代碼

安裝和測試(第三步)

下一步咱們添加安裝規則和爲項目增長測試支持。安裝規則至關的簡單。對MathFunctions庫來講,咱們在CMakeLists.txt中添加兩行就能夠完成對庫和頭文件的配置。

install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
複製代碼

對應用來講,在CMakeLists.txt中添加如下的幾行完成安裝可執行文件和配置頭文件:

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

這就是所有了。在這個點上,你應該能夠構建這個教程了,完成安裝。這個項目會安裝合適的頭文件,依賴庫和可執行文件。CMAKE中的變量CMAKE_INSTALL_PREFIX會決定文件被安裝的根路徑。添加測試支持一樣很簡單。在CMakeLists.txt咱們能夠添加一系列的基礎測試保證應用正常運行。

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")
複製代碼

構建完成後,能夠在命令行中運行ctest運行測試。第一個測試簡單的見證應用運行,沒有段錯誤或者其餘崩潰,而且返回0.這個是基本的CTEST測試。接着幾個測試會使用PASS_REGULAR_EXPRESSION測試屬性驗證測試的輸出是否包含字符串。在這個例子彙總,驗證計算平方根的結果和打印的使用信息中的版本號是否正是參數提供的版本號。若是你但願添加其餘測試,測試不一樣的輸入值,你能夠須要考慮建立像下面所示的宏定義:

#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")
複製代碼

添加系統感知(第四步)

接着,讓咱們考慮添加一些平臺相關的代碼。在這個,i 子中,咱們會添加一些取決於平臺是否具有logexp函數的代碼。固然,全部平臺都有這些函數,可是在這個教程中,讓咱們設想如下他們是不常見的。若是一個平臺有log,那麼咱們就使用它來計算平方根。咱們首先測試一些這些函數是否可用,能夠在CMakeLists.txt使用CheckFunctionExists.cmake中的宏定義:

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

接着,咱們修改TutorialConfig.h頭文件的定義,定義是否生效取決於CMAKE是否在平臺上找到來他們。

// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
複製代碼

在配置前測試logexp很重要。配置文件指令會使用CMAKE的當前設置當即配置文件。最終,在平方根計算函數中,咱們基於logexp在當前系統是否可用決定使用哪一個實現。

// 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
  . . .
複製代碼

添加動態文件生成和文件生成器(第五步)

在這一小節,咱們會展現你如何在應用構建流程中添加代碼生成。在這個例子中,咱們會在構建過程當中建立來一個預先計算的品方跟表格,而後將表格編譯到應用中去。爲了實現這個功能,咱們首先須要一個生成表格的程序。在MathFucntions的子路徑中,有一個名爲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;
}
複製代碼

注意到,表格是經過合法的的C++產生的,而且輸出的文件名是經過參數肯定的。因此下一步就是在CMakeLists.txt中設置適當的指令去構建MakeTable的可執行程序,而後將其做爲構建流程的一部分運行。只需幾行代碼就可完成:

# 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  )
複製代碼

首先,MakeTable的可執行程序將做爲獨立的可執行程序。而後咱們添加一個自定義的指令定義如何運行MakeTable產生Table.h。再而後,咱們要讓CMAKE知道mysqrt.cxx依賴於產生的Table.h。經過將生成的Table.h添加到MathFucntions的源代碼列表中實現。另外咱們也要將當前的二進制目錄添加到頭文件搜索路徑(including directories)中,這樣Table.h才能被找到,而且才能被mysqrt.cxx引用到。當這個項目被構建時,首先構建MakeTable可執行程序,而後運行該程序生成Table.h.最後,編譯已經引入來Table.hmysqrt.cxx生成MathFunctions庫。此時,CMakeLists.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. 是這樣的:

// 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
複製代碼

MathFunctionsCMakeLists.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)
複製代碼

建立安裝器(第六步)

下一步,假設咱們須要將程序分發給其餘人使用。咱們但願根據不一樣的平臺同時提供二進制和源代碼分發。這和咱們以前小節作的第三部略有不一樣。在第三步的時候,咱們安裝的是從過源代碼構建出來的二進制。在這個例子中,咱們會構建支持二進制安裝的安裝包和支持諸如cygwin, debuan, RPMs的安裝包。爲來實現這個功能,咱們使用CPACK建立平臺相關的安裝包。確切的說,咱們須要在根CMakeLists.txt的底部添加如下幾行:

# build a CPack driven installer package
include (InstallRequiredSystemLibraries)
set (CPACK_RESOURCE_FILE_LICENSE  
     "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set (CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
include (CPack)
複製代碼

以上就是所有來。咱們從包含InstallRequiredSystemLibraries開始。這個模塊會包含當前運行平臺的運行時庫。接着咱們設置CPACK的變量,定義來License的路徑和版本信息。版本信息使用到咱們以前的定義。最後,咱們包含了CPACK模塊。

接下來,是最多見的構造方式和運行方式。爲了構建二進制包,你須要運行: cpack --config CPackConfig.cmake

爲了建立源代碼包,你須要運行:cpack --config CPackSourceConfig.cmake

提供儀表盤支持(第七步)

很容易的就能夠提交咱們測試結果到儀表盤。在以前的步驟中,咱們已經定義了若干測試用例。咱們只須要運行這些用例而後提交便可。在根CMakeLists.txt中添加指令就能夠增長儀表盤功能:

# enable dashboard scripting
include (CTest)
複製代碼

咱們一樣能夠建立CTestConfig.cmake指定儀表盤名稱。

set (CTEST_PROJECT_NAME "Tutorial")
複製代碼

CTEST會讀取文件中的配置而且運行。

相關文章
相關標籤/搜索