在下载完成fuchsia的代码之后,编译代码之前,需要使用fx set命令指定要编译的目标(product.board)。参见以下的fx set命令的帮助信息。其中的PRODUCT和BOARD参数可由fx的命令list-products和命令list-boards获得,也可通过查看products目录和boards目录下的gni文件看到。--build-dir选项指定build输出目录,不指定的话默认目录为:`out/default`,并且此build目录将写入文件.fx-build-dir中,之后的fx命令默认使用此文件中保存的目录为(build)编译目录。
~/fuchsia$ fx help set
usage: fx set PRODUCT.BOARD [--with GNLABEL1,GNLABEL2,...]
[--release] [--auto-dir | --build-dir BUILDDIR]
[--args ARG] [--help-args [ARG]] [--variant VARIANT]
[--with-base GNLABEL1,GNLABEL2,...]
例如,使用如下的set命令。如果需要查看fx的详细执行过程,可增加-x参数,如fx -x set core.x64。
~/fuchsia$ fx set core.x64
fx命令是位于目录scripts/下的shell脚本文件。其包含有目录tools/devshell/lib/的shell脚本文件metrics.sh和vars.sh,需要使用到其中的一些变量和函数。首先需要找到fx指定的命令(此处为set)是否存在,参见函数find_command。按照优先级在以下三个位置查找名称为set的文件:1)tools/devshell;2)tools/devshell/contrib;和3)out/default/tools/。
function find_command {
local -r cmd=$1
local command_path="${fuchsia_dir}/tools/devshell/${cmd}"
if [[ -x "${command_path}" ]]; then
echo "${command_path}"
return 0
fi
local command_path="${fuchsia_dir}/tools/devshell/contrib/${cmd}"
if [[ -x "${command_path}" ]]; then
echo "${command_path}"
return 0
fi
local -r build_dir=$(get_build_dir)
if [[ -n "${build_dir}" ]]; then
local command_path="${build_dir}/tools/${cmd}"
if [[ -x "${command_path}" ]]; then
echo "${command_path}"
return 0
fi
fi
...
}
在执行set命令之前,使用脚本文件metrics.sh中的函数track-command-execution,设置命令跟踪,如果metrics功能开启,将fx命令行信息发送到google的以下网址:https://www.google-analytics.com。使用命令fx metrics status可查看当前状态:
~/fuchsia$ fx metrics status
Collection of metrics is currently disabled for /home/kai/fuchsia
To change it, run fx metrics
~/fuchsia$
之后,根据找到的set文件(存储在command_path变量中),出入fx set之后的参数到set命令文件中,通常情况下位于tools/devshell/set,其为shell脚本文件。参见其主函数main,find_config函数检查参数product_name是否在目录vendor/*/products/和目录products/下存在名称为product_name.gni的文件(注意多了后缀名.gni),对于board_name逻辑相同,只不过目录换做为boards目录。find_config函数返回相应gni的目录。
function main {
cd "${FUCHSIA_DIR}"
local gn_args=""
case "$1" in
*.*)
local product_name="${1%.*}"
local board_name="${1##*.}"
local product="$(find_config "products" "${product_name}")"
local board="$(find_config "boards" "${board_name}")"
if [[ "${product}" == "" ]] || [[ "${board}" == "" ]]; then
exit 1
fi
gn_args+=" import(\"//${board}\") import(\"//${product}\")"
如下目录products和boards下的文件列表,存在core.gni和x64.gni文件。此后,gn_args变量的内容变为:import("//boards/x64.gni") import("//products/core.gni")。
~/fuchsia$ ls products/
bringup.gni core.gni OWNERS README.md router.gni speaker.gni terminal.gni workstation.gni
~/fuchsia$
~/fuchsia$ ls boards/
arm64.gni cleo.gni kirin970.gni mt8167s_ref.gni qemu-arm64.gni toulouse.gni x64.gni
chromebook-x64.gni hikey960.gni msm8x53-som.gni OWNERS qemu-x64.gni vim2.gni
~/fuchsia$
接下来,确定build目录,由于没有指定auto_dir命令选项,此处将build目录指定为out/default。
local config_build_dir
if $auto_dir; then
else
case "$build_dir" in
'')
# Default is "//out/default". Store it as relative.
config_build_dir="out/default"
;;
//*|out/*)
esac
fi
build_dir="${FUCHSIA_DIR}/${config_build_dir}"
由于fx命令仅指定了set core.x64,最终的gn_args参数如下。没有使用--with-base选项和--with选项指定packages,变量base_package_labels和universe_package_labels都为空。
import("//boards/x64.gni") import("//products/core.gni")
# See: fx args --list=base_package_labels
base_package_labels+=[]
# See: fx args --list=cache_package_labels
cache_package_labels+=[]
# See: fx args --list=universe_package_labels
universe_package_labels+=[]
最后,调用buildtools目录下的gn命令。
"${FUCHSIA_DIR}/buildtools/gn" ${gn_cmd} "${build_dir}" \
"${gn_switches[@]}" --args="${gn_args}" "$@"
翻译以上的命令,如下:
buildtools/gn gen /home/kai/work/fuchsia/out/default --check, --export-compile-commands --args= import("//boards/x64.gni") import("//products/core.gni")
# See: fx args --list=base_package_labels
base_package_labels+=[]
# See: fx args --list=cache_package_labels
cache_package_labels+=[]
# See: fx args --list=universe_package_labels
universe_package_labels+=[]
文件buildtools/gn的内容如下,非常简单,调用exec_tool.sh执行命令:
#!/usr/bin/env bash
set -euo pipefail
readonly SCRIPT_ROOT="$(cd $(dirname ${BASH_SOURCE[0]} ) && pwd)"
readonly TOOL_NAME=$(basename "$0")
source "${SCRIPT_ROOT}/exec_tool.sh"
文件buildtools/exec_tool.sh如下,其调用TOOL_PATH变量指定的工具。此处工具为buildtools/linux-x64/gn,gn为二进制的可执行程序,其参数$@即为之前介绍的传递给buildtools/gn脚本的参数。
case "$(uname -s)" in
Darwin)
readonly HOST_PLATFORM="mac-x64"
;;
Linux)
readonly HOST_PLATFORM="linux-x64"
;;
*)
echo "Unknown operating system. Cannot run ${TOOL_NAME}."
exit 1
;;
esac
readonly TOOL_PATH="${SCRIPT_ROOT}/${HOST_PLATFORM}/${TOOL_NAME}"
exec "${TOOL_PATH}" "$@"
GN工具的子命令gen用于生产ninja所用的文件。默认情况下GN工具启动时,将在其目录或者上级目录中查找后缀为.gn的文件,当然也可在命令行使用选项--root指定查找目录。对于fuchsia,根gn文件BUILD.gn位于代码顶层目录,即GN工具所在目录buildtools/linux-x64的上上级目录:
~/fuchsia$ ls
AUTHORS BUILD.gn buildtools
~/fuchsia$
如下为顶层的根BUILD.gn文件,看一下其参数声明部分。首先包含进了四个build目录下的.gni文件;其次由于在调用GN工具时传入的3个labels变量都为空,此处声明的base、cache和universe package labels变量同样都为空。最后一个参数zircon_tracelog可用于指定日志文件,当Zircon的GN运行时产生的日志将存放在此文件中,方便调试。
import("//build/board.gni")
import("//build/config/fuchsia/zircon.gni")
import("//build/testing/platforms.gni")
import("//build/toolchain/goma.gni")
declare_args() {
base_package_labels = []
cache_package_labels = []
universe_package_labels = []
zircon_extra_args = {
}
zircon_extra_ninja_targets = []
zircon_enable_kernel_debugging_features = false
zircon_enable_netsvc_debugging_features = false
# Where to emit a tracelog from Zircon's GN run. No trace will be produced if
# given the empty string. Path can be source-absolute or system-absolute.
zircon_tracelog = ""
}
例如设置zircon_tracelog = "//zircon-gn.log",运行fx set core.x64之后,将在fuchsia目录生成zircon-gn.log日志文件,其为json格式。如下显示其部分内容:
~/fuchsia$ head zircon-gn.log
{"traceEvents":
[{"pid":0,"tid":"140527708751616","ts":0,"ph":"M","name":"thread_name","args":{"name":"Main thread"}},
{"pid":0,"tid":"140527708751616","ts":14938,"ph":"X","dur":39,"name":"Parse args","cat":"setup"},
{"pid":0,"tid":"140527708751616","ts":14978,"ph":"X","dur":613,"name":"Save args file","cat":"setup"},
{"pid":0,"tid":"140527708751616","ts":15592,"ph":"X","dur":1,"name":"Fill Python Path","cat":"setup"},
{"pid":0,"tid":"140527708751616","ts":162,"ph":"X","dur":15432,"name":"DoSetup","cat":"setup"},
{"pid":0,"tid":"140527686739712","ts":15633,"ph":"X","dur":20438,"name":"//public/gn/BUILDCONFIG.gn","cat":"load"},
{"pid":0,"tid":"140527686739712","ts":36073,"ph":"X","dur":1824,"name":"//public/gn/BUILDCONFIG.gn","cat":"parse"},
通过build编译目录的args.gn文件可查看到GN运行的参数,如下:
~/fuchsia$ cat out/default/args.gn
import("//boards/x64.gni")
import("//products/core.gni")
# See: fx args --list=base_package_labels
base_package_labels += []
# See: fx args --list=cache_package_labels
cache_package_labels += []
# See: fx args --list=universe_package_labels
universe_package_labels += []
~/fuchsia$
以上为fuchsia项目的参数定义部分。对于zircon项目,BUILD.gn定义了单独的参数部分,如下:
declare_args() {
zircon_args = {
use_goma = use_goma
goma_dir = goma_dir
if (clang_prefix != default_clang_prefix) {
clang_tool_dir = clang_prefix
}
variants = zircon_variants
default_deps = []
foreach(target, zircon_ninja_targets) {
default_deps += [ ":$target" ]
}
enable_kernel_debugging_features = zircon_enable_kernel_debugging_features
enable_netsvc_debugging_features = zircon_enable_netsvc_debugging_features
forward_variables_from(zircon_extra_args, "*")
}
}
同理,可通过最终生成的build编译目录,查看zircon系统的GN运行参数,如下:
~/fuchsia$ cat out/default.zircon/args.gn
# THIS FILE IS CLOBBERED. DO NOT EDIT!
# Instead, edit //out/default/args.gn to add
# zircon_extra_args = { ... } to override settings below.
forward_variables_from({
default_deps = [ ":legacy-x64" ]
enable_kernel_debugging_features = false
enable_netsvc_debugging_features = false
goma_dir = "/home/kai/goma"
use_goma = false
variants = []
},
"*")
~fuchsia$
顶层根BUILD.gn文件,接下来要进行一个循环调用,如下的第一个exec_script脚本调用,其调用buildtools/gn脚本(原本就从此gn脚本执行到此)。传入的参数如上的gn_cmd变量的定义,由参数可见,此次调用gn脚本gen子命令,意在生成zircon的ninja编译文件。
# The Zircon GN is completely a puppet of this build. This gen runs that gen.
if (current_toolchain == default_toolchain) {
gn_cmd = [
"gen",
"-q",
"--root=" + rebase_path("//zircon", root_build_dir),
"--args=# THIS FILE IS CLOBBERED. DO NOT EDIT!$0x0a" +
"# Instead, edit $root_build_dir/args.gn to add$0x0a" +
"# zircon_extra_args = { ... } to override settings below.$0x0a" +
"forward_variables_from($zircon_args, \"*\")",
"--export-compile-commands=default",
rebase_path(zircon_root_build_dir, root_build_dir),
]
if (zircon_tracelog != "") {
gn_cmd += [ "--tracelog=" + rebase_path(zircon_tracelog, root_build_dir) ]
}
exec_script("//buildtools/gn", gn_cmd)
脚本buildtools/gn此次传入的参数如下。在文件config/fuchsia/zircon.gni中的赋值语句zircon_root_build_dir = "${root_build_dir}.zircon",将zircon_root_build_dir变量赋予了值:out/default.zircon。即此处gn脚本生成的文件存放在out/default.zircon目录下。另外,指定了--root参数选项,为zircon目录,GN工具运行时将使用zircon目录下的BUILD.gn文件。
gen -q --root=../../zircon --args=# THIS FILE IS CLOBBERED. DO NOT EDIT!
# Instead, edit //out/default/args.gn to add
# zircon_extra_args = { ... } to override settings below.
forward_variables_from({
default_deps = [":legacy-x64"]
enable_kernel_debugging_features = false
enable_netsvc_debugging_features = false
goma_dir = "/home/kai/goma"
use_goma = false
variants = []
}, "*") --export-compile-commands=default ../default.zircon
随后,使用python脚本build/zircon/populate_zircon_public.py,目前还不清楚其作用。
125 exec_script("//build/zircon/populate_zircon_public.py",
126 [ rebase_path("$zircon_root_build_dir/legacy_dirs.json") ],
127 "",
128 [ "$zircon_root_build_dir/legacy_dirs.json" ])
129
130 # This file indicates what Ninja invocation must be done to build Zircon prerequisites before *any* Ninja invocation for this build.
132 write_file("$root_build_dir/zircon.json",
133 {
134 dir = rebase_path(zircon_root_build_dir, root_build_dir)
135 targets = zircon_ninja_targets
136 },
137 "json")
138 }
接下来,在build编译目录中创建fx.config文件,写入Fuchsia的编译目录和架构ARCH信息。
144 _relative_build_dir = rebase_path(root_build_dir, "//", "//")
145 _fx_config_lines = [
146 "# Generated by `gn gen`.",
147 "FUCHSIA_BUILD_DIR='${_relative_build_dir}'",
148 "FUCHSIA_ARCH='${target_cpu}'",
149 ]
150 write_file("$root_build_dir/fx.config", _fx_config_lines)
参见以下fx.config的内容。
~/fuchsia$ cat out/default/fx.config
# Generated by `gn gen`.
FUCHSIA_BUILD_DIR='out/default'
FUCHSIA_ARCH='x64'
~/fuchsia$
顶层根BUILD.gn文件的剩余内容主要与测试相关。使用buildtools/gn的ls子命令可查看BUILD.gn文件定义target列表,如下。其中大部分的target都是由gn的group语法定义,只有fuzzers和tests两个target由generated_file定义,在build编译目录out/default/下,可看到fuzzers.json和tests.json两个输出文件。
~/fuchsia$ buildtools/gn ls out/default "//:*"
//:additional_base_packages
//:additional_cache_packages
//:additional_universe_packages
//:breakpad_symbols
//:build_time_checks
//:default
//:fuzzers
//:package_archive
//:recovery_image
//:tests
~/fuchsia$
另外使用以下的--as=buildfile命令选项,可查看顶层的target定义在哪些gn文件中,如下所示,仅有一个文件fuchsia/BUILD.gn。
~/fuchsia$ buildtools/gn ls out/default "//:*" --as=buildfile
/home/kai/fuchsia/BUILD.gn
~/fuchsia$
以下以default目标target为例,看一下其依赖关系的定义。首先其依赖于本文件中的build_time_checks目标;其次依赖于build/images/BUILD.gn文件中的packeages目标;在此依赖于sdk/BUILD.gn文件中的sdk目标。最后的两个依赖关系是依据base_package_labels和cache_package_labels不为空而定义的。
155 group("default") {
156 deps = [
157 ":build_time_checks",
158 "//build/images:packages",
159 "//sdk",
160 ]
161 if (base_package_labels != [] || cache_package_labels != []) {
162 deps += [ "//build/images" ]
163 }
164 if (universe_package_labels != []) {
165 deps += [ "//build/images:updates" ]
166 }
167 }
在多看一级依赖关系,以sdk依赖为例,其位于sdk/BUILD.gn文件中,由group语法定义,内容如下。目标sdk自身又依赖于其文件中的四个target,分别为:core、ddk、images和zircon_sysroot,关于这四个的定义参见文件sdk/BUILD.gn。
17 # This default target should contain all public or partner SDKs.
18 group("sdk") {
19 testonly = true
20
21 public_deps = [
22 ":core",
23 ":ddk",
24 ":images",
25 ":zircon_sysroot",
26 ]
27 }
使用如下的gn子命令ls,查看sdk目标的所有target列表:
~/fuchsia$ buildtools/gn ls out/default "//sdk:*"
//sdk:core
//sdk:core_archive
//sdk:core_archive_manifest
//sdk:core_copy
//sdk:core_manifest_verify
//sdk:core_meta
//sdk:core_meta_verify
//sdk:core_molecule
//sdk:core_verify_api
//sdk:ddk
//sdk:ddk_archive
//sdk:ddk_archive_manifest
...
~/fuchsia$
如下所示,sdk的所有的target并不是定义在同一个gn文件中。
~/fuchsia$ buildtools/gn ls out/default "//sdk:*" --as=buildfile
/home/kai/fuchsia/build/compiled_action.gni
/home/kai/fuchsia/build/sdk/sdk.gni
/home/kai/fuchsia/build/sdk/sdk_atom.gni
/home/kai/fuchsia/build/sdk/sdk_molecule.gni
/home/kai/fuchsia/sdk/BUILD.gn
~/fuchsia$
如下的gn命令,可参考最终编译生成的可执行文件列表,"//*"代码整个工程。以下以appmgr为例,其为在garnet/bin/appmgr/目录下的BUILD.gn定义的bin目标。
~/fuchsia$ buildtools/gn ls out/default "//*" --type=executable
//examples/hello_world/cpp:bin
//garnet/bin/appmgr:bin
使用了gn的executable语法,输出可执行文件的名称为appmgr。sources指定了源码文件。
executable("bin") {
output_name = "appmgr"
sources = [
"appmgr.cc",
"appmgr.h",
"main.cc",
]
deps = [
":lib",
"//sdk/lib/sys/cpp",
]
configs += [ "//build/config/fuchsia:static_cpp_standard_library" ]
assert_no_deps = [ "//garnet/public/lib/fostr/*" ]
}
如下的编译目录,其中out/default/appmgr为最终编译完成的输出文件。目录out/default/obj/garnet/bin/appmgr/下存放编译appmgr所需的源码文件,以及bin.ninja编译文件。
~/fuchsia$ find out/default -name appmgr
out/default/appmgr
out/default/obj/garnet/bin/appmgr
~/fuchsia$
END