Fuchsia编译系统的GN结构

 

在下载完成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

 

你可能感兴趣的:(fuchsia)