Apollo 应用与源码分析:CyberRT概念与Bazel

CyberRT 调度中间件

基本概念

Apollo在3.5版本中推出了Cyber RT替代了原先的ROS。

和ROS & ROS2中相似,Cyber RT中支持两种数据交换模式:

  1. 一种是Publish-Subscribe模式,经常使用于数据流处理中节点间通讯。即发布者(Publisher)在channel(ROS中对应地称为topic)上发布消息,订阅该channel的订阅者(Subscriber)便会收到消息数据;
  2. 一种就是常见的Service-Client模式,经常使用于客户端与服务端的请求与响应。本质上它是能够基于前者实现的。

Node是整个数据拓扑网络中的基本单元。一个Node中能够建立多个读者/写者,服务端/客户端。

读者和写者分别对应ReaderWriter,用于Publish-Subscribe模式。

服务端和客户端分别对应ServiceClient,用于Service-Client模式。

使用CyberRT需要了解CyberRT 本身的API,同时需要了解Bazel 编译工具,protobuf 通信协议,另外因为CyberRT本质上是基于FastDDS的所以也需要了解FastDDS以及Fast RTPS。

使用方法

Bazel

概念

Bazel 是一个类似于 Make 的工具,是 Google 为其内部软件开发的特点量身定制的工具,如今 Google 使用它来构建内部大多数的软件。它的功能有诸多亮点:

  1. 多语言支持:目前 Bazel 默认支持 Java、Objective-C 和 C++,但可以被扩展到其他任何变成语言。

高级构建描述语言:项目是使用一种叫 BUILD 的语言来描述的,它是一种简洁的文本语言,它把一个项目视为一个集合,这个集合由一些互相关联的库、二进制文件和测试用例组成。相反,像 Make 这样的工具,需要去描述每个文件如何调用编译器。

  1. 多平台支持:同一套工具和相同的 BUILD 文件可以用来为不同的体系结构构建软件,甚至是不同的平台。在 Google,Bazel 被同时用在数据中心系统中的服务器应用和手机端的移动应用上。
  2. 可重复性:在 BUILD 文件中,每个库、测试用例和二进制文件都需要明确指定它们的依赖关系。当一个源码文件被修改时,Bazel 凭这些依赖来判断哪些部分需要重新构建,以及哪些任务可以并行进行。这意味着所有构建都是增量的,并且相同构建总是产生一样的结果。
  3. 可伸缩性:Bazel 可以处理大型项目;在 Google,一个服务器软件有十万行代码是很常见的,在什么都不改的前提下重新构建这样一个项目,大概只需要 200 毫秒。

对于为什么要重新发明一个构建工具而不直接使用 Make,Google 认为 Make 控制得太细,最终的结果完全依靠开发人员能正确编写规则。很久以前,Google 使用自动生成的臃肿的 Makefile 来构建他们的软件,速度太慢,结果不可靠,最终影响了研发人员的效率和公司的敏捷性。所以他们做了 Bazel。Bazel 的规则层次更高,比如,对于“Java 测试”、“C++ 二进制文件”,它都有定义好的内建规则,而这些规则都已经被无数的测试证明是正确和稳定的。

安装

参考:Installing Bazel on Ubuntu

sudo apt install curl
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
sudo apt-get update 
sudo apt-get install bazel # 安装bazel
sudo apt-get install --only-upgrade bazel # 升级bazel到最新版本

查看bazel 的版本

bazel version

文件组成

bazel中对于文件架构的概念有两个:workspace和package。

workspace是表示整个项目的,也叫repo,必须在项目的根目录下建一个WORKSPACE文件来定义项目的根目录,bazel会忽略所有项目子目录下的WORKSPACE文件。

package是项目中的模块,也就是一个一个包,包在组织上比较随意,可以根据项目需求来定,你想哪个文件夹中的东西成为一个包,就在那个文件夹的目录里创建一个BUILD文件即可,包的管理范围包括子目录里的东西,但不包括子包所包括的内容。比如:

.../project/
	WORKSPACE
	lib/
		BUILD
		...
	src/
		BUILD
		...
		other1/
			BUILD
		other2/
			...

lib,src和other1分别为一个包,但是other1中含有一个BUILD,因此other1中有一个子包(上文提到:子包不属于父包),所以other1里面的东西不属于src包,但是other2里面的东西属于src包。

WORKSPACE(工作区)

WORKSPACE是Bazel一个概念,它实质上是一个目录。这个目录是bazel工作时的一个基准目录。

Bazel规定,项目源文件和Bazel构建出的目标文件,均放在此目录。要把一个目录设置为WORKSPACE, 则必须在此目录创建一个新的空文件,名为WORKSPACE. Bazel构建项目时,所有的输入项和依赖项,必须位于同一个工作区内。每个工作目录内,可以有多个项目(目录)。bazel 编译或者执行其它命令时,是在WORKSPACE中。

BUILD文件(package)

BUILD文件的作用类似 Makefile之于Make, xml文件之于Maven, gradle文件之于Gradle. BUILD文件中包含bazel的各种不同类型的指令,其中包含构建规则,它指出Bazel如何利用给定的输入,构建出指定的输出。如利用什么.cpp文件,构建出库或者可执行程序。BUILD文件中的每一条编译指令被称为一个target,它指向一系列的源文件和依赖,一个target也可以指向别的target。

举个例子,下面这个hello-world的target利用了Bazel内置的cc_binary编译指令,来从hello-world.cc源文件(没有其他依赖项)构建一个可执行二进制文件。指令里面有些属性是强制的,比如name,有些属性则是可选的,srcs表示的是源文件。

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],

cc_binary即声明了一个构建规则,用于编译生成一个可执行文件。可执行文件名(目标名)由name属性指定,name属性的值的类型可以看出是string类型。srcs属性指定了源文件,srcs属性的值的类型可以看出是list of strings。

*_binary 规则:指定生成相应语言的可执行程序。cc_binary表示c++可执行程序,jave_binary表示java可执行程序。

*_library 规则:指定生成相应语言的库。

*_test 规则:是一个特殊的bianry规则,通常用于自动化测试。

构建的三个阶段

  1. loading phase 加载阶段
    bazel遍历当前工程下,所有子文件夹,找到其中的BUILD文件,加载外部依赖,生成一个个package和target。
  2. analysis phase 分析阶段
    根据各个BUILD中定义的目标和输入输出信息,建立一个内存中的build graph。
  3. executing phase 执行阶段
    根据build graph逐个运行各个规则定义的动作,产生最终的目标。

BUILD文件规则

cc_binary

目标文件编译规则,为一个二进制可执行文件。name必须唯一,srcs指定了源文件,linkopts指定了链接规则,deps指定了依赖文件

cc_library

库文件编译规则,name指定了编译为库文件后的文件名,srcs和hdrs指定源文件和头文件,deps指定需要依赖的其他文件

cc_test

测试文件规则

package

通用方法,定义的值会作用到下面的每个子rule中。default_visibility指定了这个包的默认可见规则。可见的情况下才能被其他package调用。

licenses

通用方法,默认的license

load

通用方法,加载.bzl文件

filegroup

通用方法,为多个编译目标target指定一个名字,glob是一个帮助函数,指定了目录中哪些文件会include,哪些会exclude。visibility指定了target的可见性,也就是可以被哪些package调用

name

属性来命名规则

deps

属性来描述规则之间的依赖关系。使用冒号来分隔包名和规则名。如果某条规则所依赖的规则在其他目录下,就用"//"开头,如果在同一目录下,可以忽略包名而用冒号开头。

linkopts

指定了链接规则

样例

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("//tools:cpplint.bzl", "cpplint")

package(default_visibility = ["//visibility:public"])

cc_library(
    name = "io",
    deps = [
        ":poll_data",
        ":poll_handler",
        ":poller",
        ":session",
    ],
)

cc_library(
    name = "poll_data",
    hdrs = ["poll_data.h"],
)

cc_library(
    name = "poll_handler",
    srcs = ["poll_handler.cc"],
    hdrs = ["poll_handler.h"],
    deps = [
        ":poll_data",
        ":poller",
        "//cyber/common:log",
        "//cyber/croutine",
    ],
)

cc_library(
    name = "poller",
    srcs = ["poller.cc"],
    hdrs = ["poller.h"],
    deps = [
        ":poll_data",
        "//cyber/base:atomic_rw_lock",
        "//cyber/common:log",
        "//cyber/common:macros",
        "//cyber/scheduler:scheduler_factory",
        "//cyber/time",
    ],
)

cc_test(
    name = "poller_test",
    size = "small",
    srcs = ["poller_test.cc"],
    deps = [
        ":poller",
        "//cyber:cyber_core",
        "@com_google_googletest//:gtest",
    ],
)

cc_library(
    name = "session",
    srcs = ["session.cc"],
    hdrs = ["session.h"],
    deps = [
        ":poll_handler",
        "//cyber/common:log",
    ],
)

cc_binary(
    name = "tcp_echo_client",
    srcs = ["example/tcp_echo_client.cc"],
    deps = [
        "//cyber:cyber_core",
    ],
)

cc_binary(
    name = "tcp_echo_server",
    srcs = ["example/tcp_echo_server.cc"],
    deps = [
        "//cyber:cyber_core",
    ],
)

cc_binary(
    name = "udp_echo_client",
    srcs = ["example/udp_echo_client.cc"],
    deps = [
        "//cyber:cyber_core",
    ],
)

cc_binary(
    name = "udp_echo_server",
    srcs = ["example/udp_echo_server.cc"],
    deps = [
        "//cyber:cyber_core",
    ],
)

cpplint()

你可能感兴趣的:(自动驾驶,开发语言,自动驾驶)