將現有系統轉換為 CMake¶
對許多人來說,使用 CMake 的第一件事就是將現有的專案從使用舊的建置系統轉換為使用 CMake。這可能是一個相當容易的過程,但有一些問題需要考慮。本節將討論這些問題,並提供一些關於如何有效地將專案轉換為 CMake 的建議。轉換到 CMake 時要考慮的第一個問題是專案的目錄結構。
原始碼目錄結構¶
大多數小型專案會將其原始碼放在頂層目錄或名為 src
或 source
的目錄中。即使所有原始碼都位於子目錄中,我們強烈建議為頂層目錄建立一個 CMakeLists 檔案。這樣做有兩個原因。首先,有些人可能會感到困惑,他們必須在專案的子目錄而不是主目錄中執行 CMake。其次,您可能想要從其他目錄安裝文件或其他支援檔案。透過在專案頂層放置一個 CMakeLists 檔案,您可以使用 add_subdirectory
指令來逐步進入文件目錄,該目錄的 CMakeLists 檔案可以安裝文件(您可以為沒有目標或原始碼的文件目錄建立一個 CMakeLists 檔案)。
對於在多個目錄中具有原始碼的專案,有幾種選擇。許多基於 Makefile 的專案使用的一種選擇是在頂層目錄中建立一個單一的 Makefile,其中列出所有要在其子目錄中編譯的原始檔。例如:
SOURCES=\
subdir1/foo.cxx \
subdir1/foo2.cxx \
subdir2/gah.cxx \
subdir2/bar.cxx
此方法與 CMake 配合使用效果一樣好,使用類似的語法:
set(SOURCES
subdir1/foo.cxx
subdir1/foo2.cxx
subdir1/gah.cxx
subdir2/bar.cxx
)
另一種選擇是讓每個子目錄建置一個或多個函式庫,然後將其連結到可執行檔中。在這種情況下,每個子目錄都會定義自己的原始檔清單並新增適當的目標。第三種選擇是前兩種的混合;每個子目錄都可以有一個列出其原始碼的 CMakeLists 檔案,但頂層 CMakeLists 檔案不會使用 add_subdirectory
指令來進入子目錄。相反,頂層 CMakeLists 檔案會使用 include
指令來包含每個子目錄的 CMakeLists 檔案。例如,頂層 CMakeLists 檔案可能包含以下程式碼:
# collect the files for subdir1
include(subdir1/CMakeLists.txt)
foreach(FILE ${FILES})
set(subdir1Files ${subdir1Files} subdir1/${FILE})
endforeach()
# collect the files for subdir2
include(subdir2/CMakeLists.txt)
foreach(FILE ${FILES})
set(subdir2Files ${subdir2Files} subdir2/${FILE})
endforeach()
# add the source files to the executable
add_executable(foo ${subdir1Files} ${subdir2Files})
而子目錄中的 CMakeLists 檔案可能如下所示:
# list the source files for this directory
set(FILES
foo1.cxx
foo2.cxx
)
您使用哪種方法完全取決於您。對於大型專案,在進行變更時,擁有多個共用函式庫肯定可以縮短建置時間。對於較小的專案,其他兩種方法各有優點。這裡的主要建議是選擇一種策略並堅持下去。
建置目錄¶
下一個要考慮的問題是將產生的物件檔、函式庫和可執行檔放在哪裡。有幾種不同的常用方法,其中一些方法比其他方法更適合 CMake。建議的策略是將二進位檔案放入與原始碼樹結構相同的單獨樹狀結構中。例如,如果原始碼樹看起來像這樣:
foo/
subdir1
subdir2
則二進位樹可能看起來像這樣:
foobin/
subdir1
subdir2
對於某些 Windows 產生器(例如 Visual Studio),建置檔案實際上會保存在與所選組態相符的子目錄中;例如,debug、release 等。
如果您需要從一個原始碼樹支援多種架構,我們強烈建議使用如下的目錄結構:
projectfoo/
foo/
subdir1
subdir2
foo-linux/
subdir1
subdir2
foo-osx/
subdir1
subdir2
foo-solaris/
subdir1
subdir2
這樣,每個架構都有自己的建置目錄,並且不會干擾任何其他架構。請記住,不僅物件檔案保存在二進位目錄中,而且通常會寫入二進位樹的任何已設定的檔案。主要在 UNIX 專案中發現的另一種樹狀結構是將不同架構的二進位檔案保存在原始碼樹的子目錄中(見下文)。CMake 無法很好地處理此結構,因此我們建議切換到上面顯示的單獨建置樹結構。
foo/
subdir1/
linux
solaris
hpux
subdir2/
linux
solaris
hpux
CMake 提供三個變數來控制二進位目標的寫入位置。它們是 CMAKE_RUNTIME_OUTPUT_DIRECTORY
、CMAKE_LIBRARY_OUTPUT_DIRECTORY
和 CMAKE_ARCHIVE_OUTPUT_DIRECTORY
變數。這些變數用於初始化函式庫和可執行檔的屬性,以控制它們的寫入位置。設定這些變數使專案能夠將所有函式庫和可執行檔放在單一目錄中。對於具有許多子目錄的專案,這可以真正節省時間。典型的實作方式如下所示:
# Setup output directories.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
在此範例中,所有「執行階段」二進位檔案都將寫入專案二進位樹的 bin
子目錄,包括所有平台上的可執行檔和 Windows 上的 DLL。其他二進位檔案將寫入 lib
目錄。此方法對於使用共用函式庫 (DLL) 的專案非常有用,因為它會將所有共用函式庫收集到一個目錄中。如果將可執行檔放置在同一個目錄中,則它們在 Windows 上執行時可以更容易地找到所需的共用函式庫。
關於目錄結構的最後一點注意事項:使用 CMake,在專案內擁有專案是完全可以接受的。例如,在 Visualization Toolkit 的原始碼樹內有一個目錄,其中包含 zlib 壓縮函式庫的完整副本。在為該函式庫編寫 CMakeLists 檔案時,我們使用 project
指令來建立名為 VTKZLIB 的專案,即使它位於 VTK 原始碼樹和專案內。這對 VTK 沒有實際影響,但它確實允許我們獨立於 VTK 建置 zlib,而無需修改其 CMakeLists 檔案。
轉換專案時有用的 CMake 指令¶
有一些 CMake 指令可以讓轉換現有專案的工作更輕鬆、更快速。file
指令搭配 GLOB
引數,可讓您快速設定一個變數,其中包含符合 glob 表達式的所有檔案的清單。例如:
# collect up the source files
file(GLOB SRC_FILES "*.cxx")
# create the executable
add_executable(foo ${SRC_FILES})
這會將 SRC_FILES
變數設定為目前原始碼目錄中所有 .cxx
檔案的清單。然後,它會使用這些原始檔建立一個可執行檔。Windows 開發人員應該注意,glob 比對會區分大小寫。
轉換 UNIX Makefile¶
如果您的專案目前基於標準 UNIX Makefile,那麼將其轉換為 CMake 應該相當簡單。基本上,對於專案中具有 Makefile 的每個目錄,您都會建立一個相符的 CMakeLists 檔案。您如何處理目錄中的多個 Makefile 取決於它們的功能。如果將其他 Makefile(或 Makefile 類型的檔案)簡單地包含在主要 Makefile 中,您可以建立相符的 CMake 輸入 (.cmake) 檔案,並以類似的方式將它們包含到主要 CMakeLists 檔案中。如果不同的 Makefile 旨在在命令列上針對不同的情況調用,請考慮建立一個主要 CMakeLists 檔案,該檔案使用一些邏輯來根據 CMake 選項選擇要 include
的檔案。
Makefile 通常會包含要編譯的物件檔清單。這些可以轉換為 CMake 變數,如下所示:
OBJS= \
foo1.o \
foo2.o \
foo3.o
變成:
set(SOURCES
foo1.c
foo2.c
foo3.c
)
雖然物件檔通常列在 Makefile 中,但在 CMake 中,重點是原始檔。如果您在 Makefile 中使用了條件陳述式,則可以將它們轉換為 CMake if
指令。由於 CMake 處理產生相依性,因此可以消除大多數相依性或產生相依性的規則。在您有規則來建置函式庫或可執行檔的地方,請將它們替換為 add_library
或 add_executable
指令。
有些 UNIX 建置系統(以及原始碼)會大量使用系統架構來判斷要編譯哪些檔案或使用哪些旗標。通常,此資訊會儲存在名為 ARCH
或 UNAME
的 Makefile 變數中。在這些情況下,第一個選擇是將與架構相關的程式碼替換為更通用的測試。對於某些軟體套件來說,有太多與架構相關的程式碼,以至於這樣的變更不太合理,或者您可能因為其他原因想要根據架構來做出決策。在這些情況下,您可以使用變數 CMAKE_SYSTEM_NAME
和 CMAKE_SYSTEM_VERSION
。它們提供有關主機電腦的作業系統和版本的相當詳細的資訊。
轉換基於 Autoconf 的專案¶
基於 Autoconf 的專案主要包含三個關鍵部分。第一個是驅動流程的 configure.in 檔案。第二個是將成為最終 Makefile 的 Makefile.in 檔案,第三個部分是執行 configure 後產生的其餘已設定的檔案。在將基於 autoconf 的專案轉換為 CMake 時,請從 configure.in 和 Makefile.in 檔案開始。
Makefile.in 檔案可以轉換為 CMake 語法,如前面關於轉換 UNIX Makefiles 的章節中所述。完成此操作後,將 configure.in 檔案轉換為 CMake 語法。autoconf 中的大多數函數(巨集)在 CMake 中都有對應的命令。下面列出了一些基本轉換的簡短表格
- AC_ARG_WITH
使用
option
命令。- AC_CHECK_HEADER
使用
CheckIncludeFile
模組中的check_include_file
巨集。- AC_MSG_CHECKING
使用帶有
STATUS
引數的message
命令。- AC_SUBST
在使用
configure_file
命令時自動完成。- AC_CHECK_LIB
使用
CheckLibraryExists
模組中的check_libary_exists
巨集。- AC_CONFIG_SUBDIRS
使用
add_subdirectory
命令。- AC_OUTPUT
使用
configure_file
命令。- AC_TRY_COMPILE
使用
try_compile
命令。
如果您的 configure 腳本使用 AC_TRY_COMPILE
執行測試編譯,您可以使用相同的程式碼用於 CMake。如果程式碼很短,可以直接放入您的 CMakeLists 檔案中,或者最好將其放入專案的原始碼檔案中。對於需要此類測試的大型專案,我們通常會將此類檔案放入 CMake 子目錄中。
在您依賴 autoconf 來設定檔案的地方,您可以使用 CMake 的 configure_file
命令。基本方法是相同的,並且我們通常將要設定的輸入檔案命名為具有 .in
副檔名的檔案,就像 autoconf 所做的那樣。此命令會將輸入檔案中任何以 ${VAR}
或 @VAR@
引用的變數替換為 CMake 確定的值。如果未定義變數,則將其替換為空白。或者,僅會替換 @VAR@
形式的變數,而會忽略 ${VAR}
。這對於設定使用 ${VAR}
作為評估變數的語法的語言檔案很有用。您也可以使用 C 前置處理器透過 #cmakedefine VAR
有條件地定義變數。如果定義了變數,則 configure_file
會將 #cmakedefine
轉換為 #define
;如果未定義,則會變成註解掉的 #undef
。例如
/* what byte order is this system */
#cmakedefine CMAKE_WORDS_BIGENDIAN
/* what size is an INT */
#cmakedefine SIZEOF_INT @SIZEOF_INT@
轉換基於 Windows 的工作區¶
將 Visual Studio 解決方案轉換為 CMake 需要幾個步驟。首先,您需要在原始碼目錄的頂層建立一個 CMakeLists 檔案。與往常一樣,此檔案應以 cmake_minimum_required
和 project
命令開始,該命令定義了 CMake 專案的名稱。這將成為最終 Visual Studio 解決方案的名稱。接下來,將所有原始碼檔案加入 CMake 變數中。對於具有多個目錄的大型專案,請在每個目錄中建立一個 CMakeLists 檔案,如本章開頭關於原始碼目錄結構的章節中所述。然後,您將使用 add_library
和 add_executable
加入您的程式庫和可執行檔。預設情況下,add_executable
假設您的可執行檔是主控台應用程式。將 WIN32
引數加入 add_executable
表示它是 Windows 應用程式(使用 WinMain 而不是 main)。
Visual Studio 支援一些不錯的功能,而 CMake 可以加以利用。其中之一是對類別瀏覽的支援。通常在 CMake 中,只有原始碼檔案會加入到目標,而不是標頭檔。如果您將標頭檔加入到目標,它們將會顯示在工作區中,然後您就可以像平常一樣瀏覽它們。Visual Studio 也支援檔案群組的概念。預設情況下,CMake 會為原始碼檔案和標頭檔建立群組。透過使用 source_group
命令,您可以建立自己的群組並將檔案指派給它們。如果您的工作區中有任何自訂建置步驟,可以使用 add_custom_command
命令將這些步驟加入到您的 CMakeLists 檔案中。可以使用 add_custom_target
命令加入 Visual Studio 中的自訂目標(公用程式目標)。