Chromium GN構建工具的使用
Chromium整體的構建過程大體如下:
這個過程大體為,先由gn工具根據各個模塊的.gn配置文件,或gyp工具根據各個模塊的.gyp配置文件,產生.ninja文件,再由ninja工具產生最終的目標文件,比如靜態庫、動態庫、exe可執行文件或者是apk文件等等。gyp工具是用Python寫的,gn是用C寫的,gn增量構建最快。整個Chromium項目,在構建系統方面,也是逐漸在全部轉向gn構建。
gn工具不僅僅在我們構建chromium時可以用來產生.ninja文件,它還可以幫助我們描繪chromium的項目地圖,比如獲取某個模塊依賴的所有其它模塊,依賴某個模塊的所有其它模塊、構建參數等信息,可以幫助我們對構建配置的有效性進行檢查等。在基於chromium模塊進行開發時,gn工具可以為我們提供巨大的幫助。
這里我們來看一下gn工具的用法,逐個來看gn提供的每個命令。
gn args
這個命令有兩個作用,一是生成.ninja構建配置文件,二是查看當前構建環境的配置參數。
生成.ninja
生成.ninja是gn工具的基本功能。如在 Chromium Android編譯指南 中所示,要構建chromium,或其中的模塊,需要先生成.ninja文件對整個構建環境進行配置。為Android版chromium做構建配置時,所執行的命令為:
buildtools/linux64/gn args out/Default
這個命令的參數是輸出目錄的路徑。這個命令啟動系統的默認編輯器,創建out/Default/args.gn
文件,我們在編輯器中加入我們自己的配置項,如:
target_os = "android"
target_cpu = "arm" # (default)
is_debug = false # (default)
# Other args you may want to set:
is_component_build = false
is_clang = false
symbol_level = 1 # Faster build with fewer symbols. -g1 rather than -g2
enable_incremental_javac = false # Much faster; experimental
android_ndk_root = "~/dev_tools/Android/android-ndk-r10e"
android_sdk_root = "~/dev_tools/Android/sdk"
android_sdk_build_tools_version = "23.0.2"
disable_file_support = true
disable_ftp_support = true
enable_websockets = false
gn根據創建的out/Default/args.gn
文件,及系統中其它的配置,產生ninja文件。
查看當前構建環境的配置參數
gn args命令還可以查看當前構建環境的可以配置的參數,參數的默認值及默認值的配置文件等。chromium用到了許多的配置參數。對於這些配置參數中的大多數,即使用戶不指定,chromium的構建系統也會提供默認值。當我們在為不知道可以對構建做些什么樣的配置,各個配置項的含義,或者配置項默認值的設置位置而抓狂時,這個命令會顯得非常有價值。
對於Chromium的Android構建環境,能配置的參數及其相關信息大體如下:
$ buildtools/linux64/gn args --list out/Default/
......
android_ndk_root Default = "//third_party/android_tools/ndk"
//build/config/android/config.gni:66
android_ndk_version Default = "r10e"
//build/config/android/config.gni:67
android_sdk_build_tools_version Default = "23.0.1"
//build/config/android/config.gni:71
android_sdk_root Default = "//third_party/android_tools/sdk"
//build/config/android/config.gni:69
......
use_platform_icu_alternatives Default = true
//url/features.gni:10
Enables the use of ICU alternatives in lieu of ICU. The flag is used
for Cronet to reduce the size of the Cronet binary.
......
這個功能的gn args命令參數為--list [output_dir]
。可見gn向我們展示了能為構建定制的每個參數,參數的默認值,及設置參數默認值的文件的位置。 這個工具展示了每一個標記配置項的名稱,默認值,創建該標記配置項的配置文件,以及標記配置項作用的說明。
gn gen
這個命令也是用於產生.ninja文件的,其參數如下:
usage: gn gen [<ide options>] <out_dir>
這個命令根據當前的代碼樹及配置,產生ninja文件,並把它們放在給定的目錄下。輸出目錄可以是源碼庫的絕對地址,比如//out/foo
,也可以是相對於當前目錄的地址,如:out/foo
。上面的gn args out/Default
實際上等價於,啟動編輯器編輯參數創建out/Default/args.gn
文件之后,執行gn gen <out_dir>
。
實際在構建chromium及其子模塊時,這個命令會更加常用。對於特定的平台,通常我們在完成構建環境的配置之后,就不會經常有修改構建配置參數文件的需要了。
gn clean
這個命令用於對歷史構建進行清理。
usage: gn clean <out_dir>
它會刪除輸出目錄下除args.gn
文件外的所有內容,並創建一個可以重新產生構建配置的ninja構建環境。這個命令也比較常用,用來消除歷史構建的影響。
gn desc
這個命令用於顯示關於一個給定target或config的信息。
usage: gn desc <out_dir> <label or pattern> [<what to show>] [--blame] [--format=json]
構建參數等相關信息,取自給出的<out_dir>
。<label or pattern>
可以是一個target標簽,一個config標簽,或一個標簽模式 (參考"gn help label_pattern")。標簽模式只匹配targets。
比如我們要查看chromium的net模塊相關的信息:
$ gn desc out/Default net
Target //net:net
Type: source_set
Toolchain: //build/toolchain/android:arm
visibility
*
testonly
false
check_includes
true
allow_circular_includes_from
sources
......
//net/spdy/spdy_buffer.cc
//net/spdy/spdy_buffer.h
//net/spdy/spdy_buffer_producer.cc
//net/spdy/spdy_buffer_producer.h
......
public
[All headers listed in the sources are public.]
configs (in order applying, try also --tree)
......
//third_party/boringssl:external_config
//net:net_resources_grit_config
//base:android_system_libs
//sdch:sdch_config
//third_party/zlib:zlib_config
//net:jni_includes_net_jni_headers
public_configs (in order applying, try also --tree)
//net:net_config
//third_party/protobuf:using_proto
//third_party/protobuf:protobuf_config
//build/config/compiler:no_size_t_to_int_warning
//third_party/boringssl:external_config
all_dependent_configs (in order applying, try also --tree)
//base/allocator:wrap_malloc_symbols
asmflags
-fno-strict-aliasing
--param=ssp-buffer-size=4
-fstack-protector
-funwind-tables
-fPIC
......
cflags
-fno-strict-aliasing
--param=ssp-buffer-size=4
-fstack-protector
-funwind-tables
-fPIC
......
cflags_cc
-fno-threadsafe-statics
-fvisibility-inlines-hidden
-std=gnu++11
-Wno-narrowing
-fno-rtti
-isystem../../../../../../~/dev_tools/Android/android-ndk-r10e/sources/cxx-stl/llvm-libc++/libcxx/include
-isystem../../../../../../~/dev_tools/Android/android-ndk-r10e/sources/cxx-stl/llvm-libc++abi/libcxxabi/include
-isystem../../../../../../~/dev_tools/Android/android-ndk-r10e/sources/android/support/include
-fno-exceptions
cflags_objcc
-fno-threadsafe-statics
-fvisibility-inlines-hidden
-std=gnu++11
-fno-rtti
-fno-exceptions
defines
......
DISABLE_FTP_SUPPORT=1
GOOGLE_PROTOBUF_NO_RTTI
GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER
HAVE_PTHREAD
include_dirs
//
//out/Default/gen/
/usr/include/kerberosV/
//third_party/protobuf/src/
//out/Default/gen/protoc_out/
//third_party/protobuf/src/
......
ldflags
-Wl,--fatal-warnings
-fPIC
......
Direct dependencies (try also "--all", "--tree", or even "--all --tree")
//base:base
......
libs
c++_static
/home/~/dev_tools/Android/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9/libgcc.a
c
atomic
log
lib_dirs
~/dev_tools/Android/android-ndk-r10e/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/
可以看到gn desc命令向我們展示了,編譯net模塊時,包含的所有源文件,該模塊發布的頭文件,依賴的庫,依賴的頭文件路徑,依賴的庫文件的路徑,依賴的其它模塊,編譯參數,鏈接參數,預定義宏等等等,應有盡有。在我們不喜歡chromium的構建系統及其項目的文件組織結構,而想要將某個模塊轉換為其它的文件組織結構,比如Eclipse或其它IDE常見的項目文件組織方式時,或者要將編譯出來的動態鏈接庫so文件用在其它項目里,被預定義宏的差異搞得焦頭爛額時,這個命令就能派上用場了。
借助於這個工具,我們可以很方便的開發自動化的工具,來將chromium的模塊單獨抽出來用在其它地方,比如我們可以提取net模塊發布的頭文件,將net模塊用在android中。我們之前就有做過這樣一個小工具:
#!/usr/bin/env python
import os
import shutil
import sys
def print_usage_and_exit():
print sys.argv[0] + " [chromium_src_root]" + "[out_dir]" + " [target_name]" + " [targetroot]"
exit(1)
def copy_file(src_file_path, target_file_path):
if os.path.exists(target_file_path):
return
if not os.path.exists(src_file_path):
return
target_dir_path = os.path.dirname(target_file_path)
if not os.path.exists(target_dir_path):
os.makedirs(target_dir_path)
shutil.copy(src_file_path, target_dir_path)
def copy_all_files(source_dir, all_files, target_dir):
for one_file in all_files:
source_path = source_dir + os.path.sep + one_file
target_path = target_dir + os.path.sep + one_file
copy_file(source_path, target_path)
if __name__ == "__main__":
if len(sys.argv) < 4 or len(sys.argv) > 5:
print_usage_and_exit()
chromium_src_root = sys.argv[1]
out_dir = sys.argv[2]
target_name = sys.argv[3]
target_root_path = "."
if len(sys.argv) == 5:
target_root_path = sys.argv[4]
target_root_path = os.path.abspath(target_root_path)
os.chdir(chromium_src_root)
cmd = "gn desc " + out_dir + " " + target_name
outputs = os.popen(cmd).readlines()
source_start = False
all_headers = []
public_start = False
public_headers = []
for output_line in outputs:
output_line = output_line.strip()
if output_line.startswith("sources"):
source_start = True
continue
elif source_start and len(output_line) == 0:
source_start = False
continue
elif source_start and output_line.endswith(".h"):
output_line = output_line[1:]
all_headers.append(output_line)
elif output_line == "public":
public_start = True
continue
elif public_start and len(output_line) == 0:
public_start = False
continue
elif public_start:
public_headers.append(output_line)
if len(public_headers) == 1:
public_headers = all_headers
if len(public_headers) > 1:
copy_all_files(chromium_src_root, public_headers, target_dir=target_root_path)
這個命令還可以幫我們dump出模塊的整個依賴樹,如net模塊的依賴樹:
$ gn desc out/Default //net deps --tree
//base:base
//base:base_jni_headers
//base:android_runtime_jni_headers
//base:android_runtime_jni_headers__jni_Runtime
//base:base_jni_headers__jni_gen
//base:base_paths
//base:base_static
//base:build_date
......
//url:url_features
這個命令可以為我們展示不同類型信息的每一項具體來自於哪個文件。如net模塊的C編譯標記選項(cflags)的信息如下:
$ gn desc out/Default //net cflags --blame
From //build/config/compiler:compiler
(Added by //build/config/BUILDCONFIG.gn:461)
-fno-strict-aliasing
--param=ssp-buffer-size=4
-fstack-protector
-funwind-tables
-fPIC
......
gn ls
這個命令展示給定構建目錄下,匹配某個模式的所有targets。
usage: gn ls <out_dir> [<label_pattern>] [--all-toolchains] [--as=...] [--type=...] [--testonly=...]
默認情況下,除非明確地提供了工具鏈參數,只有默認工具鏈中的target會被匹配。如果沒有指定標簽參數,則顯示所有的targets。標簽模式不是常規的正則表達式 (可以參考"gn help label_pattern")。如net下的targets:
$ gn ls out/Default //net/*
......
//net:http_server
//net:net
......
//net:net_quic_proto
......
//net:quic_client
//net:quic_packet_printer
//net:quic_server
......
其它一些關於gn ls命令的例子:
gn ls out/Debug
這個命令會列出所有的targets。
gn ls out/Debug "//base/*"
Lists all targets in the directory base and all subdirectories.
gn ls out/Debug "//base:*"
Lists all targets defined in //base/BUILD.gn.
gn ls out/Debug //base --as=output
Lists the build output file for //base:base
gn ls out/Debug --type=executable
Lists all executables produced by the build.
gn ls out/Debug "//base/*" --as=output | xargs ninja -C out/Debug
Builds all targets in //base and all subdirectories.
gn ls out/Debug //base --all-toolchains
Lists all variants of the target //base:base (it may be referenced in multiple toolchains).
gn path
這命令查找兩個taregets之間的依賴路徑。
usage: gn path <out_dir> <target_one> <target_two>
每個依賴路徑輸出為一個組,組與組之間用新行分割。參數中兩個targets的順序不限。默認情況下,只打印一個路徑。如果某個路徑只包含公共依賴,則會輸出最短的公共路徑。否則,輸出最短的公共或私有路徑。如果指定了--with-data,則數據依賴也會考慮。如果有多個最短路徑,則會從中選出一個。加上--all則會輸出兩個targets之間的所有依賴路徑。
$ gn path out/Default //base //net --all
//net:net --[private]-->
//base:base
......
gn refs
這個命令可以用來查找反向的依賴(也就是引用了某些東西的targets)。
gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* [--all] [--all-toolchains] [--as=...] [--testonly=...] [--type=...]
輸入可以是如下這些:
- Target標簽
- Config標簽
- 標簽模式
- 文件名
- 響應文件
這個命令輸出依賴於參數中的輸入得targets,比如:
$ gn refs out/Default/ net
//:both_gn_and_gyp
//components/cronet/android:cronet
//components/cronet/android:cronet_static
//net:balsa
//net:crypto_message_printer
//net:disk_cache_memory_test
//net:http_server
//net:quic_client
//net:quic_packet_printer
//net:quic_server
//net:simple_quic_tools
//net:stale_while_revalidate_experiment_domains
其它一些關於gn refs命令的例子:
gn refs out/Debug //tools/gn:gn
Find all targets depending on the given exact target name.
gn refs out/Debug //base:i18n --as=buildfiles | xargs gvim
Edit all .gn files containing references to //base:i18n
gn refs out/Debug //base --all
List all targets depending directly or indirectly on //base:base.
gn refs out/Debug "//base/*"
List all targets depending directly on any target in //base or its subdirectories.
gn refs out/Debug "//base:*"
List all targets depending directly on any target in //base/BUILD.gn.
gn refs out/Debug //base --tree
Print a reverse dependency tree of //base:base
gn refs out/Debug //base/macros.h
Print target(s) listing //base/macros.h as a source.
gn refs out/Debug //base/macros.h --tree
Display a reverse dependency tree to get to the given file. This will show how dependencies will reference that file.
gn refs out/Debug //base/macros.h //base/at_exit.h --all
Display all unique targets with some dependency path to a target containing either of the given files as a source.
gn refs out/Debug //base/macros.h --testonly=true --type=executable --all --as=output
Display the executable file names of all test executables potentially affected by a change to the given file.
gn check
這個命令可以用來檢查頭文件依賴的有效性。其參數為:
gn check <out_dir> [<label_pattern>] [--force]
如:
$ gn check out/Default/ net
Header dependency check OK
gn help
這個命令會向我們展示gn工具的幫助信息,可以用來獲取關於上面所有的gn命令,及內置得targets,內建預定義變量的所有相關信息。如:
$ gn help
Commands (type "gn help <command>" for more details):
args: Display or configure arguments declared by the build.
check: Check header dependencies.
clean: Cleans the output directory.
desc: Show lots of insightful information about a target or config.
format: Format .gn file.
gen: Generate ninja files.
help: Does what you think.
ls: List matching targets.
path: Find paths between two targets.
refs: Find stuff referencing a target or file.
Target declarations (type "gn help <function>" for more details):
action: Declare a target that runs a script a single time.
action_foreach: Declare a target that runs a script over a set of files.
bundle_data: [iOS/OS X] Declare a target without output.
copy: Declare a target that copies files.
create_bundle: [iOS/OS X] Build an OS X / iOS bundle.
......