docs/框架设计/构建及编译/包管理.md
前提条件
阅读使用该文档前请确保已经参考 包管理安装方式 文档 完成 Apollo 环境管理工具以及容器环境的安装
本教程涵盖使用软件包管理下载,安装与构建 Apollo 各模块的基础知识。您将设置工作空间并构建一个简单的 C++ 项目,该项目将说明 Apollo 中软件包管理的关键概念,如 .workspace.json 和 .env 文件。完成本教程后,请参阅 Apollo 研发工具 - buildtool,了解软件包管理中关键工具 buildtool 的高级用法。
在本教程中,您将学到:
Apollo 在包管理模式下,以工程来组织和管理软件包,Apollo 官方提供了多个工程示例,这些工程可以在 Apollo 中的 github 主页中找到。在本教程中,我们使用 application-pnc 工程。您可以从 Apollo 的 github 库来获取示例工程:
git clone https://github.com/ApolloAuto/application-pnc.git
该工程声明依赖了和 pnc 相关的软件包,可以通过部署该工程来迅速调试、仿真 Apollo 的 pnc 软件包,该工程的目录结构如下:
application-pnc
├── .workspace.json
├── .env
├── WORKSPACE
├── core
│ ├── BUILD
│ └── cyberfile.xml
└── profiles
└── default
如您所见,这个工程里面并无任何 Apollo 源码,仅有一些配置文件(.workspace.json和.env)与 core 模块,下面将通过部署该工程,逐步介绍上述提到的概念
在部署整个工程之前,您需要先对工作空间的各个文件有一定了解。工作空间,即整个工程目录,存放着模块源码。它还包含一些供 Apollo 编译工具链识别的特殊文件:
WORKSPACE: 也即是 bazel 中的 WORKSPACE 文件,该文件会将该目录及其内容标识为 Bazel 工作区,并位于项目目录结构的根目录下。除非您需要额外引入可供bazel识别的第三方库,否则一般该文件不需要修改。
.workspace.json: 该文件同样位于项目的根目录下,标明了该工程从何处下载的软件包,以及软件包的版本,该文件内容如下:
{ "repositories": [{ "name": "apollo-core", "version": "9.0.0-alpha2-r28" }] }
其中repositories字段中包含软件包仓库信息,name为软件包仓库名称,当前, Apollo 有两个仓库可供用户选择:
而version则是为该工程下载软件包版本。
当您想把整个工程从9.0.0-alpha2-r28版本升级到9.0.0-alpha2-r29版本时,简单将该文件修改至如下内容即可:
{ "repositories": [{ "name": "apollo-core", "version": "9.0.0-alpha2-r29" }] }
然后重新部署工程,工程依赖的软件包就会自动升级至9.0.0-alpha2-r29版本。
注意:由于不同版本下 Apollo 各模块的对外接口可能会存在变更,因此升级版本后工程的源码可能需要做额外的适配工作
.env: Apollo 当前还需要运行在 docker 容器中,该文件指定了启动容器时使用的镜像的仓库以及tag等信息,该文件的内容如下:
APOLLO_ENV_NAME=pnc
APOLLO_ENV_WORKLOCAL=1
APOLLO_ENV_CONTAINER_REPO='registry.baidubce.com/apollo/apollo-env-gpu'
APOLLO_ENV_CONTAINER_TAG='9.0-latest'
其中:
APOLLO_ENV_NAME 指定了容器的名称。实际开发中,经常会在一个开发环境中启动多个 Apollo 工程。通过配置该变量,解决了不同容器名称冲突的问题。APOLLO_ENV_CONTAINER_REPO 指定了镜像仓库APOLLO_ENV_CONTAINER_TAG 指定了镜像的tag除上述提到的文件以外,该工程在 core 路径下还包括 core 这一模块的"源码",尽管该模块不包括任何 c++ 传统意义上的源码。
在 Apollo 软件包管理模式中,一个模块的源码包括以下内容:
cyberfile.xml(必须): 模块的描述文件,描述了该模块的名称、所在工作空间路径、依赖等版本信息BUILD(必须): 即 bazel 的 BUILD 文件,描述了模块中 c++ 源码, python 源码,配置文件,数据文件等该组织成什么形式进行编译接下来,将以 core 模块为例,简单对模块涉及的源码文件进行介绍
cyberfile.xml 位于工作区模块的根目录下。包含 cyberfile.xml 文件的目录该模块的源码路径,其子目录不允许有 cyberfile.xml 文件,即软件包不支持嵌套。
core 的 cyberfile.xml 内容如下:
<package>
<name>core</name>
<version>local</version>
<description>
depends of apollo core
</description>
<maintainer email="[email protected]">Apollo Maintainer</maintainer>
<type>module</type>
<src_path>//core</src_path>
<license>Apache License 2.0</license>
<author>Apollo</author>
<!-- basic -->
<depend repo_name="common" type="binary">common</depend>
<depend repo_name="cyber" type="binary">cyber</depend>
<depend repo_name="common-msgs" type="binary">common-msgs</depend>
<!-- dreamview && monitor -->
<depend repo_name="dreamview" type="binary">dreamview</depend>
<depend repo_name="monitor" type="binary">monitor</depend>
<depend repo_name="studio-connector" type="binary">studio-connector</depend>
<depend repo_name="sim-obstacle" type="binary">sim-obstacle</depend>
<!-- external_command -->
<depend repo_name="external-command-action" type="binary">external-command-action</depend>
<depend repo_name="external-command-demo" type="binary">external-command-demo</depend>
<depend repo_name="external-command-lane-follow" type="binary">external-command-lane-follow</depend>
<depend repo_name="external-command-process" type="binary">external-command-process</depend>
<depend repo_name="external-command-processor-base" type="binary">external-command-processor-base</depend>
<depend repo_name="external-command-valet-parking" type="binary">external-command-valet-parking</depend>
<depend repo_name="old-routing-adpter" type="binary">old-routing-adpter</depend>
<depend repo_name="routing" type="binary">routing</depend>
<!-- planning -->
<depend repo_name="planning" type="binary">planning</depend>
<depend repo_name="planning-scenario-bare-intersection-unprotected" type="binary">planning-scenario-bare-intersection-unprotected</depend>
<depend repo_name="planning-scenario-emergency-pull-over" type="binary">planning-scenario-emergency-pull-over</depend>
<depend repo_name="planning-scenario-emergency-stop" type="binary">planning-scenario-emergency-stop</depend>
<depend repo_name="planning-scenario-lane-follow" type="binary">planning-scenario-lane-follow</depend>
<depend repo_name="planning-scenario-park-and-go" type="binary">planning-scenario-park-and-go</depend>
<depend repo_name="planning-lane-follow-map" type="binary">planning-lane-follow-map</depend>
<depend repo_name="planning-scenario-pull-over" type="binary">planning-scenario-pull-over</depend>
<depend repo_name="planning-scenario-stop-sign-unprotected" type="binary">planning-scenario-stop-sign-unprotected</depend>
<depend repo_name="planning-scenario-traffic-light-protected" type="binary">planning-scenario-traffic-light-protected</depend>
<depend repo_name="planning-scenario-traffic-light-unprotected-left-turn" type="binary">planning-scenario-traffic-light-unprotected-left-turn</depend>
<depend repo_name="planning-scenario-traffic-light-unprotected-right-turn" type="binary">planning-scenario-traffic-light-unprotected-right-turn</depend>
<depend repo_name="planning-scenario-valet-parking" type="binary">planning-scenario-valet-parking</depend>
<depend repo_name="planning-scenario-yield-sign" type="binary">planning-scenario-yield-sign</depend>
<depend repo_name="planning-task-fallback-path" type="binary">planning-task-fallback-path</depend>
<depend repo_name="planning-task-lane-borrow-path" type="binary">planning-task-lane-borrow-path</depend>
<depend repo_name="planning-task-lane-change-path" type="binary">planning-task-lane-change-path</depend>
<depend repo_name="planning-task-lane-follow-path" type="binary">planning-task-lane-follow-path</depend>
<depend repo_name="planning-task-open-space-fallback-decider" type="binary">planning-task-open-space-fallback-decider</depend>
<depend repo_name="planning-task-open-space-pre-stop-decider" type="binary">planning-task-open-space-pre-stop-decider</depend>
<depend repo_name="planning-task-open-space-roi-decider" type="binary">planning-task-open-space-roi-decider</depend>
<depend repo_name="planning-task-open-space-trajectory-partition" type="binary">planning-task-open-space-trajectory-partition</depend>
<depend repo_name="planning-task-open-space-trajectory-provider" type="binary">planning-task-open-space-trajectory-provider</depend>
<depend repo_name="planning-task-path-decider" type="binary">planning-task-path-decider</depend>
<depend repo_name="planning-task-path-reference-decider" type="binary">planning-task-path-reference-decider</depend>
<depend repo_name="planning-task-path-time-heuristic" type="binary">planning-task-path-time-heuristic</depend>
<depend repo_name="planning-task-piecewise-jerk-speed" type="binary">planning-task-piecewise-jerk-speed</depend>
<depend repo_name="planning-task-piecewise-jerk-speed-nonlinear" type="binary">planning-task-piecewise-jerk-speed-nonlinear</depend>
<depend repo_name="planning-task-pull-over-path" type="binary">planning-task-pull-over-path</depend>
<depend repo_name="planning-task-reuse-path" type="binary">planning-task-reuse-path</depend>
<depend repo_name="planning-task-rss-decider" type="binary">planning-task-rss-decider</depend>
<depend repo_name="planning-task-rule-based-stop-decider" type="binary">planning-task-rule-based-stop-decider</depend>
<depend repo_name="planning-task-speed-bounds-decider" type="binary">planning-task-speed-bounds-decider</depend>
<depend repo_name="planning-task-speed-decider" type="binary">planning-task-speed-decider</depend>
<depend repo_name="planning-task-st-bounds-decider" type="binary">planning-task-st-bounds-decider</depend>
<depend repo_name="planning-traffic-rules-backside-vehicle" type="binary">planning-traffic-rules-backside-vehicle</depend>
<depend repo_name="planning-traffic-rules-crosswalk" type="binary">planning-traffic-rules-crosswalk</depend>
<depend repo_name="planning-traffic-rules-destination" type="binary">planning-traffic-rules-destination</depend>
<depend repo_name="planning-traffic-rules-keepclear" type="binary">planning-traffic-rules-keepclear</depend>
<depend repo_name="planning-traffic-rules-rerouting" type="binary">planning-traffic-rules-rerouting</depend>
<depend repo_name="planning-traffic-rules-stop-sign" type="binary">planning-traffic-rules-stop-sign</depend>
<depend repo_name="planning-traffic-rules-traffic-light" type="binary">planning-traffic-rules-traffic-light</depend>
<depend repo_name="planning-traffic-rules-yield-sign" type="binary">planning-traffic-rules-yield-sign</depend>
<depend repo_name="planning-traffic-rules-reference-line-end" type="binary">planning-traffic-rules-reference-line-end</depend>
<!-- transform -->
<depend repo_name="transform" type="binary">transform</depend>
<!-- prediction -->
<depend repo_name="prediction" type="binary">prediction</depend>
<!-- control -->
<depend repo_name="control-controller-demo-control-task" type="binary">control-controller-demo-control-task</depend>
<depend repo_name="control-controller-lat-based-lqr-controller" type="binary">control-controller-lat-based-lqr-controller</depend>
<depend repo_name="control-controller-lon-based-pid-controller" type="binary">control-controller-lon-based-pid-controller</depend>
<depend repo_name="control-controller-mpc-controller" type="binary">control-controller-mpc-controller</depend>
<depend repo_name="control" type="binary">control</depend>
<!-- map -->
<depend repo_name="map" type="binary">map</depend>
<builder>bazel</builder>
</package>
其中,一些比较重要的属性:
BUILD 文件即 bazel 的 BUILD 文件,它告诉 Bazel 如何编译所需的输出,例如可执行的二进制文件或库。
在软件包管理模式中,Apollo 在 bazel 的基础上,提供了一些宏,规则,例如 core 的 BUILD 文件
load("//tools:apollo_package.bzl", "apollo_package")
package(default_visibility = ["//visibility:public"])
apollo_package()
其中 apollo_package 是软件包管理模式新增的宏,在软件包管理模式下,所有模块源码的BUILD文件中都需要添加该宏,保证模块源码可以正常的编译,部署。
除此之外,Apollo为c++源码、python源码、proto源码提供了相应的规则,例如在cyber源码的 BUILD 文件中:
load("//tools:apollo_package.bzl", "apollo_cc_library", "apollo_package")
apollo_cc_library(
name = "cyber_state",
srcs = ["state.cc",],
hdrs = ["state.h",],
deps = ["//cyber/common:cyber_common",],
)
在软件包管理模式下,请使用 Apollo 提供的规则,这些规则分别有:
apollo_cc_library, apollo_cc_binary, apollo_cc_test规则的使用方式与 bazel 原生规则一致。
apollo_component, apollo_plugin将在下文中介绍如何使用这两个新增规则。
启动 Apollo 容器需要使用 Apollo 环境管理工具 aem,该工具的安装可以查阅包管理安装方式 文档。
cd application-pnc && aem start
启动容器后 aem 会产生类似以下的输出:
The home directory `/home/apollo' already exists. Not copying from `/etc/skel'.
[ OK ] Congratulations! You have successfully finished setting up Apollo Dev Environment.
[ OK ] To login into the newly created apollo_neo_dev_pnc container, please run the following command:
[ OK ] aem enter
[ OK ] Enjoy!
aem enter
当进入容器后,可以看见 in-dev-docker 字样。
上文提过,application-pnc 工程内部没有任何 c++ 源码,只有一个 core 模块的,并声明了该模块的依赖,通过编译该模块,就可以在当前环境下迅速下载需要的软件包,达到部署工程的目的。
通过以下命令来编译 core 模块:
buildtool build -p core
-p 参数指定了要编译模块的路径,假如不指定该参数,buildtool将会编译工作空间下的所有模块
输入命令后,buildtool 将会自动下载依赖,这个过程根据网络状况,可能会持续 30 - 60 分钟,请耐心等待
[buildtool] 2023-10-27 14:15:04 INFO Import depends...
[buildtool] 2023-10-27 14:15:04 INFO Preprocess 3rd-osqp
[buildtool] 2023-10-27 14:15:04 INFO Install 3rd-osqp...
[buildtool] 2023-10-27 14:15:04 INFO install 3rd-osqp...
[buildtool] 2023-10-27 14:15:07 INFO 3rd-osqp successfully installed
[buildtool] 2023-10-27 14:15:07 INFO PostProcess 3rd-osqp
[buildtool] 2023-10-27 14:15:07 INFO Import depends...
[buildtool] 2023-10-27 14:15:07 INFO Preprocess libhdf5-dev
[buildtool] 2023-10-27 14:15:08 INFO Install libhdf5-dev...
[buildtool] 2023-10-27 14:15:16 INFO libhdf5-dev successfully installed
[buildtool] 2023-10-27 14:15:16 INFO PostProcess libhdf5-dev
buildtool 部署工程完毕后,当前环境已经安装了 pnc 仿真需要的软件和工具,可以启动 dreamview 来对 planning 进行调试
通过以下命令启动 dreamview
aem bootstrap
然后从浏览器访问 localhost:8888 就可以访问 dreamview ,启动仿真和 pnc 进行调试。
或者手动启动 planning component:
mainboard -d /apollo/modules/planning/planning_base/dag/planning.dag
除部署以外,开发者可能想要查看或修改某个模块,插件的源码,可以通过以下命令安装某个模块:
buildtool install planning-task-lane-follow-path
该命令安装了 planning-task-lane-follow-path 的源码到工作空间下,源码可以在 modules/planning/planning_base/tasks/lane_follow_path 下找到。
您可以修改上述的代码,改变task的行为,然后编译使其生效。
buildtool build -p modules/planning/
上述命令会编译modules/planning路径下的所有模块。
然后再通过dreamview重启 planning ,或者命令行重新运行 planning 来进一步调试。
当您想舍弃本地编译的结果,恢复planning-task-lane-follow-path最原始的效果,可以通过以下命令来重装模块:
rm -f modules/planning/planning_base/tasks/lane_follow_path && buildtool reinstall planning-task-lane-follow-path
然后重启 planning 即可。
除了修改 Apollo 源码以外,您可能会想自己创建一个 Apollo 组件,也即是 component。
buildtool 提供了一个便捷的命令创建组建模版:
buildtool create --template component sample_component
--template 参数声明了创建的模版是 component,sample_component 声明了该 component 的源码路径
执行完毕后,会有类似以下输出:
[buildtool] 2023-10-27 14:54:25 INFO Reconfigure apollo enviroment setup
template_vars: {'package_path': 'sample_component', 'package_name': 'sample-component', 'target_name': 'sample_component', 'class_name': 'SampleComponent', 'base_class_name': 'SampleComponentBase', 'config_message_name': 'SampleComponentConfig', 'namespaces': [], 'includes': [], 'dependencies': [], 'build_dependencies': [], 'channel_name': '/apollo/sample_component', 'channel_message_type': 'apollo::SampleComponentMsg', 'package_type': 'module', 'email': '[email protected]', 'author': 'Apollo Developer', 'description': 'This is a demo package', 'config_file_name': 'sample_component'}
[buildtool] 2023-10-27 14:54:25 INFO sample_component/proto/sample_component.proto create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/sample_component.h create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/sample_component.cc create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/proto/BUILD create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/BUILD create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/dag/sample_component.dag create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/launch/sample_component.launch create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/cyberfile.xml create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/conf/sample_component.pb.txt create success
[buildtool] 2023-10-27 14:54:25 INFO sample_component/conf/sample_component.conf create success
[buildtool] 2023-10-27 14:54:25 INFO create component package sample-component on sample_component success
工作空间下会多出了 sample_component 目录:
sample_component/
|-- BUILD
|-- conf
| |-- sample_component.conf
| `-- sample_component.pb.txt
|-- cyberfile.xml
|-- dag
| `-- sample_component.dag
|-- launch
| `-- sample_component.launch
|-- proto
| |-- BUILD
| `-- sample_component.proto
|-- sample_component.cc
`-- sample_component.h
其中:
该示例 component 会监听 /apollo/sample_component channel,接收到消息后在终端打出相应消息。
由于该示例是一个 component,因此在 BUILD 文件中使用了apollo_component规则:
apollo_component(
name = "libsample_component_component.so",
srcs = [
"sample_component.cc",
],
hdrs = [
"sample_component.h",
],
deps = [
"//cyber",
"//sample_component/proto:sample_component_proto",
],
)
apollo_component规则声明了该 target 是一个组件,其中:
该示例可以直接编译:
buildtool build -p sample_component
编译完成后直接运行即可:
mainboard -d sample_component/dag/sample_component.dag
您也可以修改该 component 初始化和接收到消息的行为,修改代码完毕后直接编译即可
与组件一致,可以使用 buildtool 的 create 命令来创建一个插件:
buildtool create --template plugin --base_class_name apollo::planning::Task sample_plugin --dependencies planning
插件需要继承一个插件基类,上述命令的 --base_class_name 参数声明继承 planning 中的 Task 类,sample_plugin 声明了该插件的源码路径,--dependencies 参数在 cyberfile.xml 中添加了对 planning 的依赖
工作空间会多出 sample_plugin 目录:
sample_plugin/
|-- BUILD
|-- conf
| `-- sample_plugin.pb.txt
|-- cyberfile.xml
|-- plugin_sample_plugin_description.xml
|-- proto
| |-- BUILD
| `-- sample_plugin.proto
|-- sample_plugin.cc
`-- sample_plugin.h
相较于组件,插件增加了一个描述文件 plugin_sample_plugin_description.xml:
<library path="sample_plugin/libsample_plugin.so">
<class type="apollo::SamplePlugin" base_class="apollo::planning::Task"></class>
</library>
该文件描述插件动态库的名称,以及插件的类名和集成的基类名,以便cyber能够正确加载该插件。
与组件不同,由于插件基类的定义不同,上述命令创建的插件模版仅是一个最基础的模版,无法直接编译,需要开发者补齐相应信息才能够编译,接下来将介绍开发者需要补齐的信息。
在 create 命令中,指定了继承的基类是 apollo::planning::Task,而实际上,sample_plugin.h 只是单纯继承了,apollo::planning::Task,并未引入相应的头文件:
#pragma once
#include <memory>
#include "cyber/plugin_manager/plugin_manager.h"
namespace apollo {
class SamplePlugin : public apollo::planning::Task {
public:
bool Init();
};
CYBER_PLUGIN_MANAGER_REGISTER_PLUGIN(apollo::SamplePlugin, apollo::planning::Task)
} // namespace apollo
开发者可以参考planning的接口文档来添加apollo::planning::Task对应的头文件,并根据基类定义修改函数签名:
#pragma once
#include <memory>
#include "cyber/plugin_manager/plugin_manager.h"
#include "modules/planning/planning_base/task_base/task.h"
namespace apollo {
class SamplePlugin : public apollo::planning::Task {
public:
bool Init(const std::string &config_dir, const std::string &name,
const std::shared_ptr<apollo::planning::DependencyInjector> &injector) override;
apollo::common::Status Execute(apollo::planning::Frame *frame,
apollo::planning::ReferenceLineInfo *reference_line_info) override;
};
CYBER_PLUGIN_MANAGER_REGISTER_PLUGIN(apollo::SamplePlugin, apollo::planning::Task)
} // namespace apollo
由于函数签名被修改了,相应的实现也需要对照修改,以下是 create 命令初始化的 cc 文件:
#include <memory>
#include "sample_plugin/sample_plugin.h"
namespace apollo {
bool SamplePlugin::Init() {
return true;
}
} // namespace apollo
修改后的 cc 文件:
#include <memory>
#include "sample_plugin/sample_plugin.h"
namespace apollo {
bool SamplePlugin::Init(const std::string &config_dir, const std::string &name,
const std::shared_ptr<apollo::planning::DependencyInjector> &injector) {
return true;
}
apollo::common::Status SamplePlugin::Execute(apollo::planning::Frame *frame,
apollo::planning::ReferenceLineInfo *reference_line_info) {
apollo::planning::Task::Execute(frame, reference_line_info);
return apollo::common::Status::OK();
}
} // namespace apollo
同样,插件的BUILD文件本身也没引入基类对应的target,开发者可以参考依赖模块的BUILD文件,来添加头文件对应的target
以下是 create 命令初始化的 BUILD 文件:
load("//tools:apollo.bzl", "cyber_plugin_description")
load("//tools:apollo_package.bzl", "apollo_cc_library", "apollo_package", "apollo_plugin")
load("//tools:cpplint.bzl", "cpplint")
package(default_visibility = ["//visibility:public"])
filegroup(
name = "sample_plugin_files",
srcs = glob([
"conf/**",
]),
)
apollo_plugin(
name = "libsample_plugin.so",
srcs = [
"sample_plugin.cc",
],
hdrs = [
"sample_plugin.h",
],
description = ":plugin_sample_plugin_description.xml",
deps = [
"//cyber",
"//sample_plugin/proto:sample_plugin_proto",
],
)
apollo_package()
apollo::planning::Task对应的target是"//modules/planning/planning_base:apollo_planning_planning_base",因此修改 BUILD 文件后:
load("//tools:apollo.bzl", "cyber_plugin_description")
load("//tools:apollo_package.bzl", "apollo_cc_library", "apollo_package", "apollo_plugin")
load("//tools:cpplint.bzl", "cpplint")
package(default_visibility = ["//visibility:public"])
filegroup(
name = "sample_plugin_files",
srcs = glob([
"conf/**",
]),
)
apollo_plugin(
name = "libsample_plugin.so",
srcs = [
"sample_plugin.cc",
],
hdrs = [
"sample_plugin.h",
],
description = ":plugin_sample_plugin_description.xml",
deps = [
"//cyber",
"//sample_plugin/proto:sample_plugin_proto",
"//modules/planning/planning_base:apollo_planning_planning_base",
],
)
apollo_package()
这个 BUILD 文件中使用了 apollo_plugin 规则来声明该 target 生成一个插件,其中:
修改完毕后,就可以通过以下命令进行编译:
buildtool build -p sample_plugin
编译完毕后,该插件就可被 planning component 加载使用。按照上述步骤编译的插件只是一个空插件,插件本身没有任何的行为,要插件来规划车辆轨迹,还需要进一步编写相应的业务代码。另外,插件被 planning 加载并调用需要修改 planning 的配置文件,详细可参阅 planning 的入门教程,此处不再过多叙述。
目前为止,apollo支持引入以下类型的第三方库:
对于第一类第三方库,简单在需要依赖该第三方库模块的cyberfile.xml中添加 depend 的标签即可,假设您的模块依赖curl库,在cyberfile.xml中添加以下依赖信息:
<depend>libcurl4-openssl-dev</depend>
然后在该模块的 BUILD 文件中添加需要链接的动态库:
apollo_cc_library(
name = "sample_lib",
srcs = [
"sample.cc",
],
hdrs = [
"sample.h",
],
copts = STUDIO_CONNECTOR_COPTS,
linkopts = ["-lcurl"],
)
在源文件中添加相应"#include"即可:
#include <curl/curl.h>
对于第二类第三方库,本质上和用户在工作空间开发的三方库无异,可当作普通模块处理。即用户为其编写cyberfile.xml与 BUILD文件即可正常编译与使用。
对于第三类第三方库,处理方式类似第二类,也可当作普通模块处理,但是在编写BUILD文件时,对于动态库需要额外的声明,例如以下的cancard驱动:
can_card
|-- BUILD
|-- cyberfile.xml
|-- include
| `-- bcan.h
`-- lib
`-- libbcan.so
cyberfile.xml中,需要依赖bazel-extend-tools, 3rd-bazel-skylib, 3rd-gpus这几个软件包:
<package format="2">
<name>can-card</name>
<version>local</version>
<description>
cancard Lib.
</description>
<maintainer email="[email protected]">Apollo</maintainer>
<license>Apache License 2.0</license>
<type>module</type>
<depend>bazel-extend-tools</depend>
<depend expose="False">3rd-bazel-skylib</depend>
<depend expose="False">3rd-gpus</depend>
<src_path url="https://github.com/ApolloAuto/apollo">//can_card</src_path>
</package>
BUILD中,对于动态库,需要使用原生的cc_library声明:
load("//tools:apollo_package.bzl", "apollo_package", "apollo_cc_binary", "apollo_cc_library")
package(
default_visibility = ["//visibility:public"],
)
native.cc_library( # 用 bazel 原生 cc_library 规则声明该第三方库的动态库
name = "bcan",
srcs = ["lib/libbcan.so"],
tags = ["shared_library"], # 标记该类型属于第三方动态库
hdrs = ["include/bcan.h"],
)
apollo_cc_library( # 创建 apollo_cc_library 让其他模块可以依赖该 target
name = "sample_can",
deps = [":bcan"]
)
apollo_package()
对于需要依赖该第三方库的模块,cyberfile.xml需添加对于该第三方库的依赖:
<depend type="binary" repo_name="can-card">can-card</depend>
BUILD文件中直接依赖该第三方库声明的apollo_cc_library即可:
apollo_cc_library(
name = "drivers-sample-canbus",
hdrs = ["sample-canbus.h"],
srcs = ["sample-canbus.cc"],
deps = ["//can_card:sample_can"],
)
然后正常编译即可。
开发者在本地开发完毕后,可能需要将本机开发的模块进行打包,并部署到另一台主机的需求,这里以上述编译的插件为例,介绍软件包管理模式下如何打包与部署。
buildtool通过以下命令对模块进行打包:
buildtool release -p sample_plugin core
-p 参数指明了要打包模块的源码路径,当不添加该参数时,buildtool 会尝试将工作空间下所有模块进行打包
打包完成后,buildtool 会有类似输出:
[buildtool] 2023-10-27 19:21:53 INFO update the local cache
[buildtool] 2023-10-27 19:21:53 INFO update complete
[buildtool] 2023-10-27 19:21:53 INFO Reconfigure apollo enviroment setup
[buildtool] 2023-10-27 19:21:53 INFO use src code of planning-task-lane-follow-path in workspace
[buildtool] 2023-10-27 19:21:56 INFO Analyzing dependencies topological graph...
[buildtool] 2023-10-27 19:21:56 INFO building package process started, it will take while...
dpkg-deb: building package 'apollo-neo-sample-plugin' in 'apollo-neo-sample-plugin_9.0.0-alpha2-r28_amd64.deb'.
dpkg-deb: building package 'apollo-neo-core' in 'apollo-neo-core_9.0.0-alpha2-r28_amd64.deb'.
[buildtool] 2023-10-27 19:21:57 INFO Done! Productions is in /apollo_workspace/.deb_local
[buildtool] 2023-10-27 19:21:57 INFO Compress the release files...
[buildtool] 2023-10-27 19:21:57 INFO Release complete, the output files: release.tar.gz
工作空间下会产生一个 release.tar.gz 文件,该文件可用于后续到其他主机上部署。
在部署之前,确保另外一台主机下载好了 aem 工具来启动 Apollo 容器
首先,创建一个用于部署用的工作空间:
mkdir deployment_workspace
然后将上一步的 release.tar.gz 与 .env 文件拷贝到新的主机中
接下来同样通过 aem 启动容器
cd deployment_workspace && aem start
启动完毕后,在容器内调用 buildtool 部署工程:
buildtool deploy -f release.tar.gz
这个过程buildtool会自动部署 sample-plugin 与 core 这两个模块,并自动从网络下载需要的依赖,根据网络状况,这个过程可能需要30-60分钟不等。
部署完毕后,就可以通过 aem bootstrap start 命令启动 dreamview,或手动通过 mainboard 启动 planning,就和刚才开发的环境一样。
恭喜!通过阅读本文档,您已经掌握了 Apollo 软件包管理的一些基础概念与开发模式,接下来您可以: