Blade工程组织

1,687 阅读1分钟

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战

本文和大家聊聊Blade的工程组织,借助腾讯广告的后台开源框架flare为大家展开介绍

flare的工程组织

flare是一个C++后台框架,为用户提供了进程级别的多纤程模型(fiber),借助fiber,实现了高性能高并发的RPC框架,支持ProtoBuf、Http等协议。flare的地址在github.com/Tencent/fla…

flare工程使用blade构建,我们看一下它的工程结构:

flare (master)$ tree -L 1
.
├── BLADE_ROOT
├── build64_release
├── flare
├── hdr_dep_missing_suppress.conf
├── hdrs_missing_suppress.conf
├── legacy_public_targets.conf
├── README.md
└── thirdparty

在flare的工程中

  • BLADE_ROOT文件标识了工程的根目录
  • 同名的flare文件夹放着本工程自己的源码
  • thirdparty文件夹中放着第三方的依赖
  • build64_release文件夹中放编译结果

第三方依赖

在工程的thirdparty的文件夹中,放着第三方的依赖,blade提倡通过源码进行依赖,统一静态编译生成一个大的二进制文件,因此在thirdparty中放着的都是各个第三方依赖的源码。

头文件依赖

在c++的依赖世界中,未了方便大家使用,很多库直接是only Header的,只需要头文件,这种情况下直接写一个BUILD文件,将头文件全部放入构建目标中即可,例如我之前添加的jwt-cpp的依赖,自己编写了如下BUILD文件

cc_library(
    name = "jwt-cpp",
    hdrs = glob("./**/*.h"),
    deps = [
        "//thirdparty/openssl:ssl",
        "//thirdparty/openssl:crypto",
    ],
    visibility = 'PUBLIC',    
)

glob函数是blade提供的内置函数,可以直接使用通配符将文件包含到构建目标中,上述构建目标中,没有cc文件,而是将该目录下所有层级的所有头文件都包括进来了。

cpp源码依赖

但是第三方依赖的源码基本都不是使用blade进行编译的,如果强行更改成blade,那需要对第三方依赖的各级文件夹进行理解,为每个文件夹编写BUILD文件,十分的繁琐。

blade提供了cmake构建目标和autoconf构建目标,编写好之后,只需要将第三方依赖的源码压缩包放在工程中,在编译时会自动解压,按照cmake或autotools等的规则进行编译,最终编译为一个blade可以依赖的静态库。

autotools_build

用于构建用 GNU Autotools 或者兼容的类似构建系统用法的包。

我们约定外部构建工具构建的代码都是以压缩包的形式存在在代码库中,经过解压、configuremakemake install 等过程, 安装到 blade-bin(也就是 build64_release)下的同路径的输出目录下。安装后的目录至少存在 includelib 两个子目录。

某些包,比如 openssl,并不是真正的用 Autotools 构建的项目,但是其构建过程 Config、make、make install 和安装后的目录布局类似于 Autotools 包, 因此也可以用本规则构建。

示例

nghttp2

include('//thirdparty/foreign_build.bld')  # 引入规则

autotools_build(
    name = 'nghttp2_build',
    source_package = 'nghttp2-1.41.0.tar.bz2',
    package_name = 'nghttp2',
    lib_names = ['nghttp2'],  # 库文件名为 lib/libnghttp2.a
    # 只生成库,不生成可执行文件,不生成动态库,开启 PIC
    configure_options = '--enable-lib-only --disable-shared --with-pic',
    # 生成转发头文件时,去掉路径中多余的 `nghttp2` 片段
    strip_include_prefix = 'nghttp2',
)

foreign_cc_library(
    name = 'nghttp2',
    install_dir = '',
    deps = [
      # 需要先构建出来
      ':nghttp2_build',
    ],
)

因为只有一个库,可以用 autotools_cc_library 来简化代码:

autotools_cc_library(
    name = 'nghttp2',
    source_package = 'nghttp2-1.41.0.tar.bz2',
    package_name = 'nghttp2',
    configure_options = '--enable-lib-only --disable-shared --with-pic',
    install_dir = '',
    strip_include_prefix = 'nghttp2',
)

cmake_build

CMake 的构建过程类似 Autotools,不过其对应于 configure 的阶段是生成 Makefile,我们称之为 generate

CMake 鼓励 out-of-source 构建模式,也就是生成的文件不在源代码目录中,很多库也只支持这种构建模式, 因此我们也采用这种模式,因此和 Autotools 构建比起来,还会额外生成一个构建目录。

特有属性:

  • cmake_options 类似 autotools_build 的 configure_options,具体参考 CMake 文档。 CMAKE_INSTALL_PREFIX 的值由规则内部生成,请勿自行指定。
  • lib_dir 库所在的子目录名,默认为 lib,但是某些包(比如 opencv)在 64 位构建下是在 lib64 目录下。

cmake_build 的绝大多数属性同 autotools_build,不再赘述。

foreign_cc_library

参见 Blade 文档

foreign_cc_library 的 deps 里要带上对构建包的目标的依赖,比如对于 openssl,crypto 需要依赖 openssl_build。 需要注意的是,对于包含多个库的包,这些库之间往往存在依赖关系,需要正确描述,比如对于 openssl,ssl 就依赖 crypto。

autotools_cc_library

对于只有一个库的包(或者你只需要一个库)的情况,可以用 autotools_cc_library 来简化规则。

cmake_cc_library

类似于 autotools_cc_library。

打包package

我们的工程通常有一些配置文件需要一起打包上传到服务器部署,blade提供了package规则,可以方便的将工程打包为压缩包。

package(
    name = 'server_package',
    type = 'tgz',
    srcs = [
        # executable
        ('$(location //server:server)', 'bin/server'),
        # conf
        ('//server/conf/server.conf', 'conf/server.conf'),
    ]
)

type是文件的类型,目前支持的有zip, tar, tar.gz, tgz, tar.bz2, tbz,type会作为输出文件的扩展名。

由于打包规则执行比较慢,而且开发阶段一般用不到,因此默认不运行,需要加--generate-package才会运行。