Cobalt编译流程分析

Cobalt Build Flow

1、Cobalt Build Introduction
Cobalt目前使用Pythone+GYP+Ninja进行编译,使用Python来收集信息以及传递参数,例如当前编译平台,Cobalt编译对接的平台。Gyp用来存储编译参数,构建编译框架,例如每个模块下面都有一个xxx.gyp,然后将当前模块编译成一个目标。Ninja用来处理目标依赖,编译链接文件,例如从编译链配置文件中读取编译方式为gcc,读取build.ninja中的依赖,然后依照依赖编译源文件。

2、Python Call Flow
(1), 回顾下之前测试GYP方法,命令为gyp main.gyp –-depth=. 这里直接使用gyp文件,但是这里gyp并不是二进制可执行文件,而是一个shell文件,查看文件内容,我们发现实际执行的是python gyp_main.py。所以查看cobalt的python调用流,实际就是找从开始编译到调用gyp_main.py的中间过程。

(2),为了方便理解,我们第一次可以不用读每一个py文件,只需要修改如下的脚本,就能打印传进gyp的参数,

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/cobalt/build/gyp_cobalt

  parser.add_argument('-v', '--verbose', dest='verbose_count',
                      default=2, action='count',       ##这里0改2
                      help='Verbose level (multiple times for more).')

然后执行如下命令:

cobalt/build/gyp_cobalt -C qa linux-x64x11 –v

就能看到打印了gyp的输入参数

cobalt/build/gyp_cobalt -C qa linux-x64x11 -v

Loading platform configuration for "linux-x64x11".
Searching for ApplicationConfiguration in /home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/linux/x64x11/cobalt
Searching for ApplicationConfiguration in /home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/linux/shared/cobalt
Found ApplicationConfiguration: CobaltLinuxConfiguration in /home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/linux/shared/cobalt/configuration.py
Using platform-specific ApplicationConfiguration for cobalt.
Building config: qa
Retrieving build number from /home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/cobalt/build/build.id
Build Number: 205289
GYP arguments: ['--format=ninja,qtcreator_ninja-linux-x64x11', '--depth=/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src', '--toplevel-dir=/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src', '-DCC_HOST=/home/xiaoshixiu/starboard-toolchains/x86_64-linux-gnu-clang-chromium-298539-1/llvm-build/Release+Asserts/bin/clang', '-Dhost_os=linux', '-Dstarboard_path=starboard/linux/x64x11', '-DOS=starboard', '-Dstarboard_platform_name=linux-x64x11', '-Goutput_dir=out', '-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/build/base_configuration.gypi', '-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/build/common.gypi', '-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/linux/x64x11/gyp_configuration.gypi', '-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/cobalt/build/cobalt_configuration.gypi', '-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/linux/x64x11/cobalt/configuration.gypi', '-Denable_vr=0', '-Dcobalt_version=205289', '-Duse_tsan=0', '-Dcobalt_config=qa', '-Dcobalt_enable_jit=1', '-Dclang=1', '-Dcobalt_fastbuild=0', '-Duse_asan=0', '-Djavascript_engine=v8', '-Dinclude_path_platform_deploy_gypi=starboard/build/default_no_deploy.gypi', '-Dcustom_media_session_client=0', '-Duse_openssl=1', '-Dasan_symbolizer_path=', '-Gqtcreator_session_name_prefix=cobalt', '-Gconfig=linux-x64x11_qa', '/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/cobalt/build/all.gyp']
Done.

有了上述gyp参数,我们就可以直接找第一个gyp文件,也就是all.gyp,然后依照dependence就可以找到整个工程的编译顺序了。

(3)虽然可以直接得到gyp的输入参数,但是我希望知道具体每一个参数的读取方法以及参数值的原始位置,我现在详细分析:
① gyp_cobalt –C参数,我们知道-C参数后面接上build type,也就是debug,devel,qa,gold,那这几个参数是在哪里保存的呢,我们可以看下面的文件:

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/tools/config.py

  DEBUG = 'debug'
  DEVEL = 'devel'
  GOLD = 'gold'
  QA = 'qa'

② gyp_cobalt platform参数,一般在ubuntu上我们配置为linux-x64x11,但是cobalt怎么知道有哪些平台的呢,可以看看下面的文件:

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/tools/platform.py

这个文件里我们可以知道两点:1,cobalt判断支持平台的方法是搜索starboard下面的文件夹,寻找包含以下文件的目录:

_PLATFORM_FILES = [
    'gyp_configuration.gypi',
    'gyp_configuration.py',
    'starboard_platform.gyp',
    'configuration_public.h',
    'atomic_public.h',
    'thread_types_public.h',
]

然后将目录路径的最后一级结点和倒数第二级结点组合,也就组成平台名,通常是操作系统-系统架构+图形架构,例如linux-x64x11。

③从平台中获取配置文件,以linux-x64x11为例,如果我们指定了这个平台,cobalt会从指定文件中读取配置内容,可以看下面这个文件:

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/tools/build.py

在_LoadPlatformConfig(platform_name) 函数中可以看到通过下面的文件来获取配置:

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/linux/x64x11/gyp_configuration.py

然后这个类继承了如下share目录的配置,

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/linux/shared/gyp_configuration.py

而share目录的配置继承了平台配置,

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/build/platform_configuration.py

综上所述,gyp的输入参数基本都可以从上述三个文件中获取。

这里我们举几个例子

A:首先是编译链参数,具体类和成员是
/src/starboard/linux/shared/gyp_configuration.py文件中的
GetEnvironmentVariables函数
这里Call过程我们不分析,直接放Call的最后地点,如下

def _GetClangInstallPath(clang_spec):
  return os.path.join(
      _GetClangBasePath(clang_spec), 'llvm-build', 'Release+Asserts')

所以最后的结果是DCC_HOST=/home/xiaoshixiu/starboard-toolchains/x86_64-linux-gnu-clang-chromium-298539-1/llvm-build/Release+Asserts/bin/clang,可以看到最后的结果连接了是home的user目录的编译链,这个编译链是自动创建的。

B:然后我们可以找下gyp的include文件,这些文件将被第一个gyp文件所包含。我们可以在如下文件中找到gypi include规则。

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/build/gyp_runner.py

首先

‘-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/build/base_configuration.gypi‘,

 '-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/build/common.gypi'

这两个文件是和平台无关的,无论什么平台都要包含。

然后可以在如下文件中找到平台配置

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/build/platform_configuration.py

平台配置文件使用如下规则:return [os.path.join(platform_info.path, ‘gyp_configuration.gypi’)],也就是只需要在对应平台的目录下找到gyp_configuration.gypi即可。Linux-x64x11对应的是:

'-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/linux/x64x11/gyp_configuration.gypi'

接下来是应用配置,指定这些头文件的脚本在如下文件中:

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/linux/shared/cobalt/configuration.py

而此文件同样引用了如下文件

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/cobalt/build/cobalt_configuration.py

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/build/application_configuration.py

所以最后通过上述两个文件的GetPostIncludes方法,将头文件搜索到。搜索规则是
1、直接引用
'-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/cobalt/build/cobalt_configuration.gypi'
2,在平台下找到cobalt目录,然后在cobalt目录下找configuration.gypi文件,如果找得到就添加到头文件列表中,注意在不同的平台中这个文件不一定都有,从内容上看它和平台配置是有一定的重合的,而且应用名不一定为cobalt。

'-I/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/starboard/linux/x64x11/cobalt/configuration.gypi'

④,接下来我们讨论下宏配置。
我们可以发现gyp输入参数的宏和平台配置文件gypi有一定的重合,说明这些是默认的平台配置。在下面文件中可以发现定义了variables变量。

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/build/platform_configuration.py

variables = {
        'clang': use_clang,
        # Whether to build with clang's Address Sanitizer instrumentation.
        'use_asan': use_asan,
        # Whether to build with clang's Thread Sanitizer instrumentation.
        'use_tsan': use_tsan,
        # Which JavaScript engine to use.  Currently, both SpiderMonkey 45 and
        # V8 are supported.  Note that V8 can only be used on platforms that
        # support JIT.
        'javascript_engine': 'mozjs-45',
        # Disable JIT and run in interpreter-only mode by default. It can be
        # set to 1 to run in JIT mode.  For SpiderMonkey in particular, we
        # have found that disabling JIT often results in faster JavaScript
        # execution and lower memory usage.  Setting this to 0 for engine that
        # requires JIT, or 1 on a platform that does not support JIT, is a
        # usage error.
        'cobalt_enable_jit': 0,
        # TODO: Remove these compatibility variables.
        'cobalt_config': config_name,
        'cobalt_fastbuild': 0,
        'custom_media_session_client': 0,
        'enable_vr': 0,
}

然后这些变量被传到下面文件的

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/build/gyp_runner.py

中的configuration_variables。最后附加到gyp的参数args中。另外也可以在

https://cobalt.googlesource.com/cobalt/+/refs/heads/19.lts.stable/src/starboard/build/gyp_runner.py

中找到common_variables和generator_variables,包含了如下参数:

 common_variables = {
        'OS': 'starboard',
        'CC_HOST': os.environ.get('CC_HOST', os.environ.get('CC', '')),
        'host_os': _GetHostOS(),
        'starboard_path': os.path.relpath(platform.Get(platform_name).path,
                                          source_tree_dir),
        'starboard_platform_name': platform_name,
    }
    _AppendVariables(common_variables, self.common_args)
    # Append generator variables.
    generator_variables = {
        # Set the output folder name; affects all generators but MSVS.
        'output_dir': 'out',
    }

⑤,我们可以画个uml来初步表达上述python执行流程:
在这里插入图片描述
3、GYP Dependency Flow
分析了GYP的输入参数,接下来我们分析gyp文件的依赖树以及不同gyp和gypi的作用。
①,首先分析第一个GYP文件,也就是’/home/xiaoshixiu/workhome/cobalt/cobalt_19_lts_5/cobalt/src/cobalt/build/all.gyp’,我这里用uml把依赖图画出来:
在这里插入图片描述
当然我没有画出cobalt的其他依赖,只是把从cobalt到starboard的依赖画了出来。我们可以发现,带有configuration的gyp/gypi文件都是python脚本执行时动态指定的,而带有platform的gyp/gypi文件都是在文件中指定依赖的。这样我们可以大致分类为,对cobalt的feature进行配置的参数在configuration的gyp/gypi中,而对cobalt的代码源文件进行配置的参数在platform的gyp/gypi中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章