安裝
GN 本來只是 Chromium 自己在用 (很久之前介紹過),隨著另一個大專案 Fuchsia 也開始採用了,Google 終於決定要給它個名份,所以它有自己的網址了: https://gn.googlesource.com。
Google 也直接提供 binary 讓你下載使用了,雖然自己編譯安裝也是很簡單。
從源碼編譯的步驟:
git clone https://gn.googlesource.com/gn
cd gn
python build/gen.py
ninja -C out
# 編譯完成後,binary 會在 out/gn
怎麼用?
Brett Wilson 的 Using GN build 是我看過最好的入門介紹。
基本流程
- 建立專案根目錄的
.gn檔案,指定 buildconfig:
# .gn
buildconfig = "//build/BUILDCONFIG.gn"
- 建立
BUILD.gn定義 targets:
# BUILD.gn
executable("my_app") {
sources = [
"main.cc",
"app.cc",
"app.h",
]
deps = [
"//base",
"//third_party/foo",
]
}
- 生成 ninja 建置檔:
gn gen out/Default
- 執行建置:
ninja -C out/Default
優點
簡單易用的 dependency tree 管理能力
GN 會根據 BUILD.gn 建立所謂的 dependency tree,確保建置的正確性與效率:
# 範例:建立 library 與其依賴關係
static_library("my_lib") {
sources = [ "lib.cc", "lib.h" ]
public_deps = [ "//base" ] # 會傳遞給依賴此 library 的 target
deps = [ "//internal/util" ] # 只有自己會用到
}
executable("my_app") {
sources = [ "main.cc" ]
deps = [ ":my_lib" ] # 會自動繼承 my_lib 的 public_deps
}
gn check 可以檢查 code (經由 #include 形成的)的相依關係是否符合 build structure:
# 檢查所有 targets
gn check out/Default
# 只檢查特定 target
gn check out/Default //my/target:name
如果你在 code 中 include 了某個 header,但沒有在 BUILD.gn 中宣告對應的 dependency,gn check 就會報錯。
豐富的文檔
GN 內建完整的文檔系統,可以直接在命令列查詢:
# 查看所有可用命令
gn help
# 查看特定命令的說明
gn help gen
gn help check
# 查看特定函數或變數的說明
gn help executable
gn help sources
gn help deps
所有的 BUILD.gn 語法、內建函數、變數都可以透過 gn help 查到,非常方便。
內建分析及除錯功能
GN 提供強大的分析工具,幫助你理解和除錯複雜的建置結構:
gn desc - 查看 target 的詳細資訊:
# 顯示 target 的所有屬性
gn desc out/Default //my/target:name
# 只顯示特定屬性
gn desc out/Default //my/target:name sources
gn desc out/Default //my/target:name deps --tree
gn path - 找出兩個 targets 之間的依賴路徑:
# 為什麼 target_a 會依賴到 target_b?
gn path out/Default //target_a //target_b
gn refs - 查看誰依賴了這個 target:
# 列出所有依賴 //base 的 targets
gn refs out/Default //base
# 以樹狀結構顯示
gn refs out/Default //base --tree
gn analyze - 分析哪些 targets 需要重新建置:
# 當某些檔案改變時,哪些 targets 會受影響
gn analyze out/Default input.json output.json
可客製化的彈性
GN 的 template 功能讓你可以建立可重複使用的建置模式:
# 定義一個 template
template("my_custom_library") {
static_library(target_name) {
forward_variables_from(invoker, "*")
# 自動加入一些共通設定
configs += [ "//build:common_config" ]
if (!defined(invoker.sources)) {
sources = []
}
sources += [ "//build/version.cc" ]
}
}
# 使用 template
my_custom_library("foo") {
sources = [ "foo.cc" ]
deps = [ "//base" ]
}
這樣可以將專案中重複的建置邏輯抽象化,讓 BUILD.gn 檔案更簡潔易讀。
可能的坑
1. includes vs include_dirs
如果你從其他建置系統(如 CMake 或 Make)轉過來,可能會習慣性地寫 includes,但在 GN 中正確的變數名稱是 include_dirs:
# ❌ 錯誤
static_library("foo") {
sources = [ "foo.cc" ]
includes = [ "include" ] # 這不會生效!
}
# ✅ 正確
static_library("foo") {
sources = [ "foo.cc" ]
include_dirs = [ "include" ]
}
2. public_deps vs deps
混淆這兩者會導致建置時找不到 header 或 linking 錯誤:
deps: 只有當前 target 自己會用到的依賴public_deps: 會傳遞給依賴此 target 的其他 targets
# 如果你的 header 檔會 include 另一個 library 的 header
# 就要用 public_deps,否則使用你 library 的人會找不到 header
static_library("my_lib") {
sources = [ "lib.h", "lib.cc" ]
public_deps = [ "//base" ] # lib.h 有 #include "base/base.h"
}
3. 路徑表示法
GN 使用特殊的路徑表示法,需要注意:
//代表專案根目錄(有.gn檔案的地方):用來指定同一個 BUILD.gn 內的 target- 相對路徑要用
./或直接寫檔名
deps = [
"//base", # 根目錄下的 base 目錄
"//third_party/foo", # 根目錄下的 third_party/foo
":my_lib", # 同一個 BUILD.gn 內的 my_lib target
]
4. gn format 自動格式化
養成使用 gn format 的習慣,可以避免很多格式上的問題:
# 格式化單一檔案
gn format BUILD.gn
# 格式化整個目錄
gn format --all