1. 程式人生 > >GYP 簡介——與cmake比較

GYP 簡介——與cmake比較

說起專案構建工具,Linux 使用者最熟悉的恐怕就是 Autotools,它將編譯安裝這個步驟大大簡化。但對於專案作者來說,想要使用 Autotools 生成有效的配置檔案著實需要下一番功夫,用現在流行的話來說就是使用者體驗不夠友好。對 Unix shell 的依賴,也使得 Autotools 天生對於跨平臺支援不佳。

後來我從大貓同學那裡聽說了 CMake,CMake 使用 C++ 編寫,原生支援跨平臺,不需要像 Autotools 那樣寫一堆的配置檔案,只需一個 CMakeLists.txt 檔案即可。簡潔的使用方式,強大的功能使得我立馬對 CMake 情有獨鍾。在後來的使用過程中,雖然會遇到一些因為使用習慣帶來的小困擾,但我對於 CMake 還是基本滿意的。直到我發現了 GYP。

GYP(Generate Your Projects)是由 Chromium 團隊開發的跨平臺自動化專案構建工具,Chromium 便是通過 GYP 進行專案構建管理。為什麼我要選擇 GYP,而放棄 CMake 呢?功能上 GYP 和 CMake 很是相似,在我看來,它們的最大區別在於配置檔案的編寫方式和其中蘊含的思想。

編寫 CMake 配置檔案相比 Autotools 來說已經簡化很多,一個最簡單的配置檔案只需要寫上原始檔及生成型別(可執行檔案、靜態庫、動態庫等)即可。對分支語句和迴圈語句的支援也使得 CMake 更加靈活。但是,CMake 最大的問題也是在這個配置檔案,請看下面這個示例檔案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
cmake_minimum_required(VERSION 2.8)
project(VP8 CXX)
add_definitions(-Wall)
cmake_policy(SET CMP0015 NEW)
include_directories("include")
link_directories("lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY
"../lib")
set(VP8SRC VP8Encoder.cpp VP8Decoder.cpp) if(X86) set(CMAKE_SYSTEM_NAME Darwin) set(CMAKE_SYSTEM_PROCESSOR i386) set(CMAKE_OSX_ARCHITECTURES "i386") add_library(vp8 STATIC ${VP8SRC}) elseif(IPHONE) if(SIMULATOR) set(PLATFORM "iPhoneSimulator") set(PROCESSOR i386) set(ARCH "i386") else() set(PLATFORM "iPhoneOS") set(PROCESSOR arm) set(ARCH "armv7") endif() set(SDKVER "4.0") set(DEVROOT "/Developer/Platforms/${PLATFORM}.platform/Developer") set(SDKROOT "${DEVROOT}/SDKs/${PLATFORM}${SDKVER}.sdk") set(CMAKE_OSX_SYSROOT "${SDKROOT}") set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR ${PROCESSOR}) set(CMAKE_CXX_COMPILER "${DEVROOT}/usr/bin/g++") set(CMAKE_OSX_ARCHITECTURES ${ARCH}) include_directories(SYSTEM "${SDKROOT}/usr/include") link_directories(SYSTEM "${SDKROOT}/usr/lib") add_definitions(-D_PHONE) add_library(vp8-armv7-darwin STATIC ${VP8SRC}) endif()

你能一眼看出這個配置檔案幹了什麼嗎?其實這個配置檔案想要產生的目標(target)只有一個,就是通過 ${VP8SRC} 編譯生成的靜態庫,但因為加上了條件判斷,及各種平臺相關配置,使得這個配置檔案看起來很是複雜。在我看來,編寫 CMake 配置檔案是一種線性思維,對於同一個目標的配置可能會零散分佈在各個地方。而 GYP 則相當不同,GYP 的配置檔案更多地強調模組化、結構化。看看下面這個示例檔案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
  'targets': [
    {
      'target_name': 'foo',
      'type': '<(library)',
      'dependencies': [
        'bar',
      ],
      'defines': [
        'DEFINE_FOO',
        'DEFINE_A_VALUE=value',
      ],
      'include_dirs': [
        '..',
      ],
      'sources': [
        'file1.cc',
        'file2.cc',
      ],
      'conditions': [
        ['OS=="linux"', {
          'defines': [
            'LINUX_DEFINE',
          ],
          'include_dirs': [
            'include/linux',
          ],
        }],
        ['OS=="win"', {
          'defines': [
            'WINDOWS_SPECIFIC_DEFINE',
          ],
        }, { # OS != "win",
          'defines': [
            'NON_WINDOWS_DEFINE',
          ],
        }]
      ],
    }
  ],
}

我們可以立馬看出上面這個配置檔案的輸出目標只有一個,也就是 foo,它是一個庫檔案(至於是靜態的還是動態的這需要在生成專案時指定),它依賴的目標、巨集定義、包含的標頭檔案路徑、原始檔是什麼,以及根據不同平臺設定的不同配置等。這種定義配置檔案的方式相比 CMake 來說,讓我覺得更加舒服,也更加清晰,特別是當一個輸出目標的配置越來越多時,使用 CMake 來管理可能會愈加混亂。

配置檔案的編寫方式是我區分 GYP 和 CMake 之間最大的不同點,當然 GYP 也有一些小細節值得注意,比如支援跨平臺專案工程檔案輸出,Windows 平臺預設是 Visual Studio,Linux 平臺預設是 Makefile,Mac 平臺預設是 Xcode,這個功能 CMake 也同樣支援,只是缺少了 Xcode。Chromium 團隊成員也撰文詳細比較了 GYP 和 CMake 之間的優缺點,在開發 GYP 之前,他們也曾試圖轉到 SCons(這個我沒用過,有經驗的同學可以比較一下),但是失敗了,於是 GYP 就誕生了。

當然 GYP 也不是沒有缺點,相反,我覺得它的「缺點」一大堆:

  • 文件不夠完整,專案不夠正式,某些地方還保留著 Chromium 的影子,看起來像是還沒有完全獨立出來。
  • 大量的括號巢狀,很容易讓人看暈,有過 Lisp 使用經驗的同學可以對號入座。對於有括號恐懼症,或者不使用現代編輯器的同學基本可以繞行。
  • 為了支援跨平臺,有時不得不加入某些特定平臺的配置資訊,比如只適用於 Visual Studio 的RuntimeLibrary 配置,這不利於跨平臺配置檔案的編寫,也無形中增加了編寫複雜度。
  • 不支援 make clean,唯一的方法就是將輸出目錄整個刪除或者手動刪除其中的某些檔案。

如果你已經打算嘗試 GYP,那一定記得在生成專案工程檔案時加上 --depth 引數,譬如:

$ gyp --depth=. foo.gyp

這也是一個從 Chromium 專案遺留下來的歷史問題。

也許你根本用不上跨平臺特性,但是 GYP 依然值得嘗試。我編寫了一份 GYP 配置檔案的模板,有興趣的同學可以參考。GYP 和 CMake 分別代表了兩種迥異的「風格」,至於孰優孰劣,還得仁者見仁,智者見智。