Lely CANopen 交叉构建说明:为什么执行 make install DESTDIR="${PWD}/stage",以及 lib/pkgconfig 的作用
文章目录
- [Lely CANopen 交叉构建说明:为什么执行 `make install DESTDIR="{PWD}/stage"\`,以及 \`lib/pkgconfig\` 的作用](#Lely CANopen 交叉构建说明:为什么执行 `make install DESTDIR="{PWD}/stage"`,以及 `lib/pkgconfig` 的作用)
-
- 结论
- 先看完整流程
- [为什么要执行 `make install DESTDIR="{PWD}/stage"\`](#为什么要执行 `make install DESTDIR="{PWD}/stage"`)
-
- [`DESTDIR` 是 staging 根目录,不是最终安装前缀](#
DESTDIR是 staging 根目录,不是最终安装前缀) - [`DESTDIR` 和 `prefix` 不要混用](#
DESTDIR和prefix不要混用)
- [`DESTDIR` 是 staging 根目录,不是最终安装前缀](#
- [`make` 构建了什么](#
make构建了什么) - 日志中的关键字是什么意思
-
- [`make all-recursive`](#
make all-recursive) - [`CC` / `CXX` / `CCLD` / `CXXLD`](#
CC/CXX/CCLD/CXXLD) - [`.lo`、`.la`、`.so`、`.a` 的区别](#
.lo、.la、.so、.a的区别) - [`copying selected object files to avoid basename conflicts...`](#
copying selected object files to avoid basename conflicts...) - [`libtool: warning: relinking ...`](#
libtool: warning: relinking ...) - [`libtool: warning: remember to run 'libtool --finish ...'`](#
libtool: warning: remember to run 'libtool --finish ...')
- [`make all-recursive`](#
- [`make install DESTDIR` 安装了什么](#
make install DESTDIR安装了什么) - [`lib/pkgconfig` 是什么](#
lib/pkgconfig是什么) -
- [它保存的是 `.pc` 元数据文件](#它保存的是
.pc元数据文件) - [为什么 Lely 会安装多个 `.pc` 文件](#为什么 Lely 会安装多个
.pc文件) - [`pkg-config --cflags` 和 `pkg-config --libs` 分别解决什么问题](#
pkg-config --cflags和pkg-config --libs分别解决什么问题)
- [它保存的是 `.pc` 元数据文件](#它保存的是
- [加入本次 `pkg-config` 环境变量说明](#加入本次
pkg-config环境变量说明) - [后续应用如何使用 staged Lely](#后续应用如何使用 staged Lely)
-
- [编译一个依赖 `liblely-coapp` 的 C++ 应用](#编译一个依赖
liblely-coapp的 C++ 应用) - [CMake 项目示例](#CMake 项目示例)
- [编译一个依赖 `liblely-coapp` 的 C++ 应用](#编译一个依赖
- 安装结果应该如何检查
-
- [检查 staging 安装树](#检查 staging 安装树)
- 检查库架构
- [检查 pkg-config 输出](#检查 pkg-config 输出)
- 常见问题
-
- [为什么 `make install` 阶段还会 relink](#为什么
make install阶段还会 relink) - [为什么 `pkg-config --libs liblely-coapp` 输出了一串 Lely 库](#为什么
pkg-config --libs liblely-coapp输出了一串 Lely 库) - [为什么使用 `PKG_CONFIG_LIBDIR`,而不是只设置 `PKG_CONFIG_PATH`](#为什么使用
PKG_CONFIG_LIBDIR,而不是只设置PKG_CONFIG_PATH) - [`PKG_CONFIG_SYSROOT_DIR` 会影响所有路径吗](#
PKG_CONFIG_SYSROOT_DIR会影响所有路径吗) - [`lib/pkgconfig` 目录需要部署到目标板吗](#
lib/pkgconfig目录需要部署到目标板吗)
- [为什么 `make install` 阶段还会 relink](#为什么
- 推荐的最终命令模板
- 关键判断标准
- 总结
- 参考资料

结论
在 Lely CANopen / lely-core 的交叉编译流程中:
sh
make -j"$(nproc)"
make install DESTDIR="${PWD}/stage"
这两条命令不是重复动作。它们分别解决两个问题:
make:把源码编译、链接成目标平台可用的库和工具。make install DESTDIR="${PWD}/stage":把已构建产物按最终安装目录布局复制到 staging 目录,便于检查、打包、部署和给后续应用交叉编译使用。
本次构建还安装了 lib/pkgconfig/*.pc 文件。它们不是库本体,而是给 pkg-config 查询的构建元数据,用来自动输出依赖 Lely 库时需要的 -I、-L、-l 等编译和链接参数。
先看完整流程
#mermaid-svg-Xm1WsSZXnTxXXoCs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Xm1WsSZXnTxXXoCs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Xm1WsSZXnTxXXoCs .error-icon{fill:#552222;}#mermaid-svg-Xm1WsSZXnTxXXoCs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xm1WsSZXnTxXXoCs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xm1WsSZXnTxXXoCs .marker.cross{stroke:#333333;}#mermaid-svg-Xm1WsSZXnTxXXoCs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xm1WsSZXnTxXXoCs p{margin:0;}#mermaid-svg-Xm1WsSZXnTxXXoCs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xm1WsSZXnTxXXoCs .cluster-label text{fill:#333;}#mermaid-svg-Xm1WsSZXnTxXXoCs .cluster-label span{color:#333;}#mermaid-svg-Xm1WsSZXnTxXXoCs .cluster-label span p{background-color:transparent;}#mermaid-svg-Xm1WsSZXnTxXXoCs .label text,#mermaid-svg-Xm1WsSZXnTxXXoCs span{fill:#333;color:#333;}#mermaid-svg-Xm1WsSZXnTxXXoCs .node rect,#mermaid-svg-Xm1WsSZXnTxXXoCs .node circle,#mermaid-svg-Xm1WsSZXnTxXXoCs .node ellipse,#mermaid-svg-Xm1WsSZXnTxXXoCs .node polygon,#mermaid-svg-Xm1WsSZXnTxXXoCs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xm1WsSZXnTxXXoCs .rough-node .label text,#mermaid-svg-Xm1WsSZXnTxXXoCs .node .label text,#mermaid-svg-Xm1WsSZXnTxXXoCs .image-shape .label,#mermaid-svg-Xm1WsSZXnTxXXoCs .icon-shape .label{text-anchor:middle;}#mermaid-svg-Xm1WsSZXnTxXXoCs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Xm1WsSZXnTxXXoCs .rough-node .label,#mermaid-svg-Xm1WsSZXnTxXXoCs .node .label,#mermaid-svg-Xm1WsSZXnTxXXoCs .image-shape .label,#mermaid-svg-Xm1WsSZXnTxXXoCs .icon-shape .label{text-align:center;}#mermaid-svg-Xm1WsSZXnTxXXoCs .node.clickable{cursor:pointer;}#mermaid-svg-Xm1WsSZXnTxXXoCs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Xm1WsSZXnTxXXoCs .arrowheadPath{fill:#333333;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xm1WsSZXnTxXXoCs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xm1WsSZXnTxXXoCs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Xm1WsSZXnTxXXoCs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xm1WsSZXnTxXXoCs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Xm1WsSZXnTxXXoCs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xm1WsSZXnTxXXoCs .cluster text{fill:#333;}#mermaid-svg-Xm1WsSZXnTxXXoCs .cluster span{color:#333;}#mermaid-svg-Xm1WsSZXnTxXXoCs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Xm1WsSZXnTxXXoCs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Xm1WsSZXnTxXXoCs rect.text{fill:none;stroke-width:0;}#mermaid-svg-Xm1WsSZXnTxXXoCs .icon-shape,#mermaid-svg-Xm1WsSZXnTxXXoCs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xm1WsSZXnTxXXoCs .icon-shape p,#mermaid-svg-Xm1WsSZXnTxXXoCs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Xm1WsSZXnTxXXoCs .icon-shape .label rect,#mermaid-svg-Xm1WsSZXnTxXXoCs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xm1WsSZXnTxXXoCs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Xm1WsSZXnTxXXoCs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Xm1WsSZXnTxXXoCs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 加载 Yocto SDK 环境
配置 lely-core: ../configure
生成 Makefile / config.h / libtool
make -j
编译 .lo / .o
链接 .la / .so / .a / 工具
make install DESTDIR=stage
安装 include/lely 头文件
安装 lib 动态库/静态库
安装 lib/pkgconfig/*.pc
安装 bin 工具
安装 etc 默认 DCF
后续应用通过 pkg-config 获取 cflags/libs
形成可打包/可部署 staging 目录
对应关系可以简化成:
| 阶段 | 命令 | 主要作用 | 输出位置 |
|---|---|---|---|
| SDK 环境 | source <YOCTO_SDK>/environment-setup-armv8a-poky-linux |
设置交叉编译器、sysroot、CFLAGS、LDFLAGS 等 | 当前 shell 环境 |
| 配置 | ../configure --host=... --prefix=... |
生成适合目标平台和功能裁剪的 Makefile | <BUILD_DIR> |
| 构建 | make -j"$(nproc)" |
编译对象文件并链接库/工具 | <BUILD_DIR>/src、<BUILD_DIR>/tools 等 |
| 安装到 staging | make install DESTDIR="${PWD}/stage" |
把构建产物复制成最终安装布局 | <LELY_STAGE><INSTALL_PREFIX> |
| 查询依赖参数 | pkg-config --cflags/--libs liblely-coapp |
给后续应用编译输出头文件路径和链接参数 | 标准输出 |
为什么要执行 make install DESTDIR="${PWD}/stage"
DESTDIR 是 staging 根目录,不是最终安装前缀
GNU Automake 文档把 DESTDIR 定义为 staged installation。包仍然按最终路径配置,例如 --prefix=/usr;执行 make install DESTDIR=/tmp/stage 时,安装动作会被重定向到 /tmp/stage/usr/...,便于检查安装结果、生成文件清单或打包部署。
本次流程可以抽象成:
text
DESTDIR = <LELY_STAGE>
prefix = <INSTALL_PREFIX>
实际写入路径 = <LELY_STAGE><INSTALL_PREFIX>
例如:
text
<LELY_STAGE><INSTALL_PREFIX>/include
<LELY_STAGE><INSTALL_PREFIX>/lib
<LELY_STAGE><INSTALL_PREFIX>/lib/pkgconfig
<LELY_STAGE><INSTALL_PREFIX>/bin
<LELY_STAGE><INSTALL_PREFIX>/etc
#mermaid-svg-53m8U5DqZaUZ7Ujx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-53m8U5DqZaUZ7Ujx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-53m8U5DqZaUZ7Ujx .error-icon{fill:#552222;}#mermaid-svg-53m8U5DqZaUZ7Ujx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-53m8U5DqZaUZ7Ujx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-53m8U5DqZaUZ7Ujx .marker.cross{stroke:#333333;}#mermaid-svg-53m8U5DqZaUZ7Ujx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-53m8U5DqZaUZ7Ujx p{margin:0;}#mermaid-svg-53m8U5DqZaUZ7Ujx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-53m8U5DqZaUZ7Ujx .cluster-label text{fill:#333;}#mermaid-svg-53m8U5DqZaUZ7Ujx .cluster-label span{color:#333;}#mermaid-svg-53m8U5DqZaUZ7Ujx .cluster-label span p{background-color:transparent;}#mermaid-svg-53m8U5DqZaUZ7Ujx .label text,#mermaid-svg-53m8U5DqZaUZ7Ujx span{fill:#333;color:#333;}#mermaid-svg-53m8U5DqZaUZ7Ujx .node rect,#mermaid-svg-53m8U5DqZaUZ7Ujx .node circle,#mermaid-svg-53m8U5DqZaUZ7Ujx .node ellipse,#mermaid-svg-53m8U5DqZaUZ7Ujx .node polygon,#mermaid-svg-53m8U5DqZaUZ7Ujx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-53m8U5DqZaUZ7Ujx .rough-node .label text,#mermaid-svg-53m8U5DqZaUZ7Ujx .node .label text,#mermaid-svg-53m8U5DqZaUZ7Ujx .image-shape .label,#mermaid-svg-53m8U5DqZaUZ7Ujx .icon-shape .label{text-anchor:middle;}#mermaid-svg-53m8U5DqZaUZ7Ujx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-53m8U5DqZaUZ7Ujx .rough-node .label,#mermaid-svg-53m8U5DqZaUZ7Ujx .node .label,#mermaid-svg-53m8U5DqZaUZ7Ujx .image-shape .label,#mermaid-svg-53m8U5DqZaUZ7Ujx .icon-shape .label{text-align:center;}#mermaid-svg-53m8U5DqZaUZ7Ujx .node.clickable{cursor:pointer;}#mermaid-svg-53m8U5DqZaUZ7Ujx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-53m8U5DqZaUZ7Ujx .arrowheadPath{fill:#333333;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-53m8U5DqZaUZ7Ujx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-53m8U5DqZaUZ7Ujx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-53m8U5DqZaUZ7Ujx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-53m8U5DqZaUZ7Ujx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-53m8U5DqZaUZ7Ujx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-53m8U5DqZaUZ7Ujx .cluster text{fill:#333;}#mermaid-svg-53m8U5DqZaUZ7Ujx .cluster span{color:#333;}#mermaid-svg-53m8U5DqZaUZ7Ujx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-53m8U5DqZaUZ7Ujx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-53m8U5DqZaUZ7Ujx rect.text{fill:none;stroke-width:0;}#mermaid-svg-53m8U5DqZaUZ7Ujx .icon-shape,#mermaid-svg-53m8U5DqZaUZ7Ujx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-53m8U5DqZaUZ7Ujx .icon-shape p,#mermaid-svg-53m8U5DqZaUZ7Ujx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-53m8U5DqZaUZ7Ujx .icon-shape .label rect,#mermaid-svg-53m8U5DqZaUZ7Ujx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-53m8U5DqZaUZ7Ujx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-53m8U5DqZaUZ7Ujx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-53m8U5DqZaUZ7Ujx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} configure --prefix=
记录最终安装路径语义
make install DESTDIR=
临时安装根
这一步的价值是:
- 不把 AArch64 目标库安装到 x86_64 构建主机的系统目录。
- 可以直接检查最终会安装哪些文件。
- 可以把
<LELY_STAGE>打包进 SDK 或 rootfs 生成流程。 - 可以让后续应用把
<LELY_STAGE>当成 Lely 的临时 sysroot/staging root。 - 可以保留
--prefix的最终部署路径语义。
DESTDIR 和 prefix 不要混用
二者职责不同:
| 项目 | 作用 | 是否进入 .pc / .la 等安装元数据 |
典型值 |
|---|---|---|---|
--prefix |
最终安装前缀 | 是 | /usr、/usr/local、<INSTALL_PREFIX> |
DESTDIR |
临时安装根目录 | 否,通常只用于安装时重定向 | <BUILD_DIR>/stage |
如果目标板最终运行时也使用 <INSTALL_PREFIX>,当前配置是匹配的。
如果最终要部署到目标板的 /usr,更常见的配置应改成:
sh
../configure \
--host=aarch64-poky-linux \
--prefix=/usr \
--disable-python \
--disable-cython \
--disable-tests \
--disable-unit-tests
make -j"$(nproc)"
make install DESTDIR="${PWD}/stage"
这样安装树会是:
text
stage/usr/include
stage/usr/lib
stage/usr/lib/pkgconfig
stage/usr/bin
而不是:
text
stage/<INSTALL_PREFIX>/include
stage/<INSTALL_PREFIX>/lib
stage/<INSTALL_PREFIX>/lib/pkgconfig
stage/<INSTALL_PREFIX>/bin
make 构建了什么
日志显示 make -j"$(nproc)" 进入了 doc、include、src、python、pkgconfig 和 tools 等目录。真正发生大量编译和链接的是 src 和 tools。
#mermaid-svg-oxI1a1giE50gsAWn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oxI1a1giE50gsAWn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oxI1a1giE50gsAWn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oxI1a1giE50gsAWn .error-icon{fill:#552222;}#mermaid-svg-oxI1a1giE50gsAWn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oxI1a1giE50gsAWn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oxI1a1giE50gsAWn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oxI1a1giE50gsAWn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oxI1a1giE50gsAWn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oxI1a1giE50gsAWn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oxI1a1giE50gsAWn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oxI1a1giE50gsAWn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oxI1a1giE50gsAWn .marker.cross{stroke:#333333;}#mermaid-svg-oxI1a1giE50gsAWn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oxI1a1giE50gsAWn p{margin:0;}#mermaid-svg-oxI1a1giE50gsAWn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oxI1a1giE50gsAWn .cluster-label text{fill:#333;}#mermaid-svg-oxI1a1giE50gsAWn .cluster-label span{color:#333;}#mermaid-svg-oxI1a1giE50gsAWn .cluster-label span p{background-color:transparent;}#mermaid-svg-oxI1a1giE50gsAWn .label text,#mermaid-svg-oxI1a1giE50gsAWn span{fill:#333;color:#333;}#mermaid-svg-oxI1a1giE50gsAWn .node rect,#mermaid-svg-oxI1a1giE50gsAWn .node circle,#mermaid-svg-oxI1a1giE50gsAWn .node ellipse,#mermaid-svg-oxI1a1giE50gsAWn .node polygon,#mermaid-svg-oxI1a1giE50gsAWn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oxI1a1giE50gsAWn .rough-node .label text,#mermaid-svg-oxI1a1giE50gsAWn .node .label text,#mermaid-svg-oxI1a1giE50gsAWn .image-shape .label,#mermaid-svg-oxI1a1giE50gsAWn .icon-shape .label{text-anchor:middle;}#mermaid-svg-oxI1a1giE50gsAWn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oxI1a1giE50gsAWn .rough-node .label,#mermaid-svg-oxI1a1giE50gsAWn .node .label,#mermaid-svg-oxI1a1giE50gsAWn .image-shape .label,#mermaid-svg-oxI1a1giE50gsAWn .icon-shape .label{text-align:center;}#mermaid-svg-oxI1a1giE50gsAWn .node.clickable{cursor:pointer;}#mermaid-svg-oxI1a1giE50gsAWn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oxI1a1giE50gsAWn .arrowheadPath{fill:#333333;}#mermaid-svg-oxI1a1giE50gsAWn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oxI1a1giE50gsAWn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oxI1a1giE50gsAWn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oxI1a1giE50gsAWn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oxI1a1giE50gsAWn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oxI1a1giE50gsAWn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oxI1a1giE50gsAWn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oxI1a1giE50gsAWn .cluster text{fill:#333;}#mermaid-svg-oxI1a1giE50gsAWn .cluster span{color:#333;}#mermaid-svg-oxI1a1giE50gsAWn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oxI1a1giE50gsAWn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oxI1a1giE50gsAWn rect.text{fill:none;stroke-width:0;}#mermaid-svg-oxI1a1giE50gsAWn .icon-shape,#mermaid-svg-oxI1a1giE50gsAWn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oxI1a1giE50gsAWn .icon-shape p,#mermaid-svg-oxI1a1giE50gsAWn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oxI1a1giE50gsAWn .icon-shape .label rect,#mermaid-svg-oxI1a1giE50gsAWn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oxI1a1giE50gsAWn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oxI1a1giE50gsAWn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oxI1a1giE50gsAWn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} liblely-libc
liblely-util
liblely-tap
liblely-can
liblely-co
liblely-ev
liblely-io
liblely-io2
liblely-coapp
can2udp / coctl / cocat / cocatd / dcf2c
主要库产物
| 构建产物 | 类型 | 作用 |
|---|---|---|
liblely-libc.la |
兼容层库 | 提供 C11、POSIX 以及部分 C++ 兼容能力,是多个 Lely 库的基础。 |
liblely-tap.la |
测试辅助库 | TAP 测试协议实现,主要服务测试套件。 |
liblely-util.la |
工具基础库 | 提供数据结构、错误处理、诊断、内存/文件缓冲等公共功能。 |
liblely-can.la |
CAN 基础库 | 提供 CAN 帧、CAN 网络对象和帧处理辅助接口。 |
liblely-co.la |
CANopen 协议栈库 | 提供对象字典、NMT、PDO、SDO、SYNC、EMCY、LSS 等 CANopen 服务。 |
liblely-io.la |
旧 I/O 库 | 旧版 I/O 抽象。Lely 文档中该库属于 deprecated 方向。 |
liblely-ev.la |
事件库 | 提供事件循环、future、task、strand、fiber executor 等异步基础设施。 |
liblely-io2.la |
新 I/O 库 | 提供异步 I/O,重点服务 timer、signal handler 和 CAN device。 |
liblely-coapp.la |
C++ CANopen 应用库 | C++ 层 CANopen 应用封装,后续 C++ master/slave 应用通常优先链接它。 |
主要工具产物
| 工具 | 作用 |
|---|---|
can2udp |
CAN 与 UDP 转发/隧道相关工具。 |
coctl |
CANopen control tool,可用于命令行控制 CANopen master/slave 过程。 |
cocat |
CANopen cat 工具。 |
cocatd |
CANopen cat daemon。 |
dcf2c |
把 EDS/DCF 转成 C 静态设备描述,适合不想在运行时解析 DCF 的场景。 |
日志中的关键字是什么意思
make all-recursive
text
make all-recursive
Making all in src
Making all in tools
表示 Automake 生成的顶层 Makefile 正在递归进入子目录执行 all 目标。doc、include、pkgconfig 等目录里可能没有需要编译的内容,所以会看到:
text
Nothing to be done for 'all'.
这不是错误,只表示该目录在当前目标下没有新增工作。
CC / CXX / CCLD / CXXLD
| 日志前缀 | 含义 |
|---|---|
CC |
使用 C 编译器编译 C 源文件。 |
CXX |
使用 C++ 编译器编译 C++ 源文件。 |
CCLD |
使用 C 链接器驱动进行链接。 |
CXXLD |
使用 C++ 链接器驱动进行链接。 |
示例:
text
CC liblely_co_la-nmt.lo
CC liblely_co_la-pdo.lo
CCLD liblely-co.la
含义是:先编译 CANopen NMT、PDO 等模块,再链接出 liblely-co.la 这个 libtool library。
.lo、.la、.so、.a 的区别
| 后缀 | 含义 | 是否最终部署重点 |
|---|---|---|
.lo |
libtool object,构建中间文件 | 否 |
.la |
libtool archive,描述库依赖、版本和安装路径的元数据 | 通常会安装,但目标运行时不直接加载它 |
.so.* |
动态库实体 | 是 |
.so |
开发链接用符号链接 | 开发包需要 |
.a |
静态库 | 静态链接或 SDK 场景需要 |
copying selected object files to avoid basename conflicts...
这通常出现在 io2 目录,因为不同子目录中可能存在同名源文件或对象文件。libtool/Makefile 为避免对象文件 basename 冲突,会复制选定对象文件。这不是错误。
libtool: warning: relinking ...
安装 libtool 库时,可能出现:
text
libtool: warning: relinking 'liblely-xxx.la'
含义是安装阶段根据最终 -rpath、依赖库路径和 staging 前缀重新链接库,使安装后的库记录正确的安装目录语义。GNU Libtool 文档说明,使用临时 staging area 时,libtool --mode=install 会根据安装目标路径和构建时的安装路径进行处理,必要时会触发 relink。
在这类交叉构建中,只要后续没有 error、No such file、undefined reference 或安装命令失败,单独的 relinking warning 通常不是失败信号。
libtool: warning: remember to run 'libtool --finish ...'
示例:
text
libtool: warning: remember to run 'libtool --finish <INSTALL_PREFIX>/lib'
含义是 libtool 提示最终安装到真实运行目录后,可能还需要让系统动态链接器识别库路径,例如更新动态链接器缓存或设置运行时库搜索路径。
在 staging 场景下,这条 warning 需要按部署方式判断:
| 部署方式 | 是否需要手动处理 |
|---|---|
打包进 rootfs 的标准库目录,如 /usr/lib |
通常由 rootfs/package 机制处理 |
部署到非标准目录,如 <INSTALL_PREFIX>/lib |
需要确保目标板运行时能找到 .so |
| 只用于交叉编译应用,不在构建主机运行目标程序 | 构建主机上通常不需要执行 libtool --finish |
目标板运行时可能需要:
sh
export LD_LIBRARY_PATH=<INSTALL_PREFIX>/lib:${LD_LIBRARY_PATH}
或者把库路径加入目标系统的动态链接器配置。实际做法取决于 rootfs、发行版和部署策略。
make install DESTDIR 安装了什么
日志显示安装阶段主要复制了以下内容:
#mermaid-svg-dVKO8jnt4aVSIkXp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dVKO8jnt4aVSIkXp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dVKO8jnt4aVSIkXp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dVKO8jnt4aVSIkXp .error-icon{fill:#552222;}#mermaid-svg-dVKO8jnt4aVSIkXp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dVKO8jnt4aVSIkXp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dVKO8jnt4aVSIkXp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dVKO8jnt4aVSIkXp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dVKO8jnt4aVSIkXp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dVKO8jnt4aVSIkXp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dVKO8jnt4aVSIkXp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dVKO8jnt4aVSIkXp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dVKO8jnt4aVSIkXp .marker.cross{stroke:#333333;}#mermaid-svg-dVKO8jnt4aVSIkXp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dVKO8jnt4aVSIkXp p{margin:0;}#mermaid-svg-dVKO8jnt4aVSIkXp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dVKO8jnt4aVSIkXp .cluster-label text{fill:#333;}#mermaid-svg-dVKO8jnt4aVSIkXp .cluster-label span{color:#333;}#mermaid-svg-dVKO8jnt4aVSIkXp .cluster-label span p{background-color:transparent;}#mermaid-svg-dVKO8jnt4aVSIkXp .label text,#mermaid-svg-dVKO8jnt4aVSIkXp span{fill:#333;color:#333;}#mermaid-svg-dVKO8jnt4aVSIkXp .node rect,#mermaid-svg-dVKO8jnt4aVSIkXp .node circle,#mermaid-svg-dVKO8jnt4aVSIkXp .node ellipse,#mermaid-svg-dVKO8jnt4aVSIkXp .node polygon,#mermaid-svg-dVKO8jnt4aVSIkXp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dVKO8jnt4aVSIkXp .rough-node .label text,#mermaid-svg-dVKO8jnt4aVSIkXp .node .label text,#mermaid-svg-dVKO8jnt4aVSIkXp .image-shape .label,#mermaid-svg-dVKO8jnt4aVSIkXp .icon-shape .label{text-anchor:middle;}#mermaid-svg-dVKO8jnt4aVSIkXp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dVKO8jnt4aVSIkXp .rough-node .label,#mermaid-svg-dVKO8jnt4aVSIkXp .node .label,#mermaid-svg-dVKO8jnt4aVSIkXp .image-shape .label,#mermaid-svg-dVKO8jnt4aVSIkXp .icon-shape .label{text-align:center;}#mermaid-svg-dVKO8jnt4aVSIkXp .node.clickable{cursor:pointer;}#mermaid-svg-dVKO8jnt4aVSIkXp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dVKO8jnt4aVSIkXp .arrowheadPath{fill:#333333;}#mermaid-svg-dVKO8jnt4aVSIkXp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dVKO8jnt4aVSIkXp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dVKO8jnt4aVSIkXp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dVKO8jnt4aVSIkXp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dVKO8jnt4aVSIkXp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dVKO8jnt4aVSIkXp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dVKO8jnt4aVSIkXp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dVKO8jnt4aVSIkXp .cluster text{fill:#333;}#mermaid-svg-dVKO8jnt4aVSIkXp .cluster span{color:#333;}#mermaid-svg-dVKO8jnt4aVSIkXp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dVKO8jnt4aVSIkXp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dVKO8jnt4aVSIkXp rect.text{fill:none;stroke-width:0;}#mermaid-svg-dVKO8jnt4aVSIkXp .icon-shape,#mermaid-svg-dVKO8jnt4aVSIkXp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dVKO8jnt4aVSIkXp .icon-shape p,#mermaid-svg-dVKO8jnt4aVSIkXp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dVKO8jnt4aVSIkXp .icon-shape .label rect,#mermaid-svg-dVKO8jnt4aVSIkXp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dVKO8jnt4aVSIkXp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dVKO8jnt4aVSIkXp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dVKO8jnt4aVSIkXp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} include/lely/...
lib/
lib/pkgconfig/
bin/
etc/
公共 C/C++ 头文件
动态库 .so.* 和 .so 符号链接
静态库 .a
libtool .la
liblely-*.pc
can2udp / coctl / cocat / cocatd / dcf2c
coctl.dcf / cocatd.dcf
目标安装树
text
<LELY_STAGE><INSTALL_PREFIX>/
├── include/
│ └── lely/
│ ├── can/
│ ├── co/
│ ├── coapp/
│ ├── ev/
│ ├── io/
│ ├── io2/
│ ├── libc/
│ ├── tap/
│ └── util/
├── lib/
│ ├── liblely-*.so.*
│ ├── liblely-*.so
│ ├── liblely-*.a
│ ├── liblely-*.la
│ └── pkgconfig/
│ ├── liblely-can.pc
│ ├── liblely-co.pc
│ ├── liblely-coapp.pc
│ ├── liblely-ev.pc
│ ├── liblely-io.pc
│ ├── liblely-io2.pc
│ ├── liblely-libc.pc
│ ├── liblely-libc_rt.pc
│ ├── liblely-tap.pc
│ └── liblely-util.pc
├── bin/
│ ├── can2udp
│ ├── coctl
│ ├── cocat
│ ├── cocatd
│ └── dcf2c
└── etc/
├── coctl.dcf
└── cocatd.dcf
其中 lib/pkgconfig 是后续应用交叉编译时最容易被忽略、但实际很关键的一部分。
lib/pkgconfig 是什么
它保存的是 .pc 元数据文件
lib/pkgconfig 目录用于存放 pkg-config 的 .pc 文件。.pc 文件描述一个库的:
- 头文件目录。
- 库文件目录。
- 链接库名。
- 版本号。
- 依赖库关系。
- 静态链接时可能额外需要的依赖。
pkg-config 的核心用途就是从这些 .pc 文件中读取元数据,然后输出编译器和链接器需要的参数。
一个典型 .pc 文件结构如下:
pkgconfig
prefix=<INSTALL_PREFIX>
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: liblely-coapp
Description: Lely C++ CANopen application library
Version: <VERSION>
Requires: liblely-io2 liblely-ev liblely-co
Libs: -L${libdir} -llely-coapp
Cflags: -I${includedir}
实际字段以 lely-core 生成结果为准。这里重点是理解字段用途:
| 字段 | 作用 |
|---|---|
prefix |
最终安装前缀,来自 ../configure --prefix=...。 |
libdir |
库搜索目录,通常是 ${prefix}/lib。 |
includedir |
头文件搜索目录,通常是 ${prefix}/include。 |
Requires |
依赖的其他 pkg-config module。 |
Libs |
链接当前库需要的 -L 和 -l 参数。 |
Cflags |
编译时需要的 -I 等参数。 |
为什么 Lely 会安装多个 .pc 文件
日志显示安装了这些 .pc 文件:
text
liblely-can.pc
liblely-co.pc
liblely-coapp.pc
liblely-ev.pc
liblely-io.pc
liblely-io2.pc
liblely-libc.pc
liblely-libc_rt.pc
liblely-tap.pc
liblely-util.pc
这是因为 lely-core 不是单个库,而是一组分层库。每个库都有自己的 .pc 文件,后续应用只需要声明最上层依赖,例如 liblely-coapp,pkg-config 会根据 .pc 中的依赖关系输出它需要的其他 Lely 库。
#mermaid-svg-gxmRgEvGZdMzEmMf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gxmRgEvGZdMzEmMf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gxmRgEvGZdMzEmMf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gxmRgEvGZdMzEmMf .error-icon{fill:#552222;}#mermaid-svg-gxmRgEvGZdMzEmMf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gxmRgEvGZdMzEmMf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gxmRgEvGZdMzEmMf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gxmRgEvGZdMzEmMf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gxmRgEvGZdMzEmMf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gxmRgEvGZdMzEmMf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gxmRgEvGZdMzEmMf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gxmRgEvGZdMzEmMf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gxmRgEvGZdMzEmMf .marker.cross{stroke:#333333;}#mermaid-svg-gxmRgEvGZdMzEmMf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gxmRgEvGZdMzEmMf p{margin:0;}#mermaid-svg-gxmRgEvGZdMzEmMf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gxmRgEvGZdMzEmMf .cluster-label text{fill:#333;}#mermaid-svg-gxmRgEvGZdMzEmMf .cluster-label span{color:#333;}#mermaid-svg-gxmRgEvGZdMzEmMf .cluster-label span p{background-color:transparent;}#mermaid-svg-gxmRgEvGZdMzEmMf .label text,#mermaid-svg-gxmRgEvGZdMzEmMf span{fill:#333;color:#333;}#mermaid-svg-gxmRgEvGZdMzEmMf .node rect,#mermaid-svg-gxmRgEvGZdMzEmMf .node circle,#mermaid-svg-gxmRgEvGZdMzEmMf .node ellipse,#mermaid-svg-gxmRgEvGZdMzEmMf .node polygon,#mermaid-svg-gxmRgEvGZdMzEmMf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gxmRgEvGZdMzEmMf .rough-node .label text,#mermaid-svg-gxmRgEvGZdMzEmMf .node .label text,#mermaid-svg-gxmRgEvGZdMzEmMf .image-shape .label,#mermaid-svg-gxmRgEvGZdMzEmMf .icon-shape .label{text-anchor:middle;}#mermaid-svg-gxmRgEvGZdMzEmMf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gxmRgEvGZdMzEmMf .rough-node .label,#mermaid-svg-gxmRgEvGZdMzEmMf .node .label,#mermaid-svg-gxmRgEvGZdMzEmMf .image-shape .label,#mermaid-svg-gxmRgEvGZdMzEmMf .icon-shape .label{text-align:center;}#mermaid-svg-gxmRgEvGZdMzEmMf .node.clickable{cursor:pointer;}#mermaid-svg-gxmRgEvGZdMzEmMf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gxmRgEvGZdMzEmMf .arrowheadPath{fill:#333333;}#mermaid-svg-gxmRgEvGZdMzEmMf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gxmRgEvGZdMzEmMf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gxmRgEvGZdMzEmMf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gxmRgEvGZdMzEmMf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gxmRgEvGZdMzEmMf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gxmRgEvGZdMzEmMf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gxmRgEvGZdMzEmMf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gxmRgEvGZdMzEmMf .cluster text{fill:#333;}#mermaid-svg-gxmRgEvGZdMzEmMf .cluster span{color:#333;}#mermaid-svg-gxmRgEvGZdMzEmMf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gxmRgEvGZdMzEmMf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gxmRgEvGZdMzEmMf rect.text{fill:none;stroke-width:0;}#mermaid-svg-gxmRgEvGZdMzEmMf .icon-shape,#mermaid-svg-gxmRgEvGZdMzEmMf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gxmRgEvGZdMzEmMf .icon-shape p,#mermaid-svg-gxmRgEvGZdMzEmMf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gxmRgEvGZdMzEmMf .icon-shape .label rect,#mermaid-svg-gxmRgEvGZdMzEmMf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gxmRgEvGZdMzEmMf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gxmRgEvGZdMzEmMf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gxmRgEvGZdMzEmMf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 你的 CANopen C++ 应用
pkg-config liblely-coapp
-I/include
-L/lib -llely-coapp ...
liblely-coapp
liblely-io2
liblely-ev
liblely-co
liblely-can
liblely-util
liblely-libc
pkg-config --cflags 和 pkg-config --libs 分别解决什么问题
| 命令 | 输出内容 | 用在编译/链接的哪一步 |
|---|---|---|
pkg-config --cflags liblely-coapp |
头文件路径等编译参数,例如 -I.../include |
编译 .c/.cpp 时 |
pkg-config --libs liblely-coapp |
库目录和库名,例如 -L.../lib -llely-coapp ... |
链接可执行程序时 |
pkg-config --libs --static liblely-coapp |
静态链接所需的更完整依赖 | 静态链接时 |
pkg-config --modversion liblely-coapp |
版本号 | 配置检查或诊断时 |
pkg-config --exists liblely-coapp |
只检查是否存在 | 脚本判断时 |
后续应用可以这样使用:
sh
${CXX} main.cpp -o canopen_app \
$(pkg-config --cflags liblely-coapp) \
$(pkg-config --libs liblely-coapp)
如果是 Makefile:
make
CXXFLAGS += $(shell pkg-config --cflags liblely-coapp)
LDLIBS += $(shell pkg-config --libs liblely-coapp)
如果使用 Autotools,也可以通过 PKG_CHECK_MODULES 让配置阶段自动检查并填充 <PREFIX>_CFLAGS 和 <PREFIX>_LIBS。
加入本次 pkg-config 环境变量说明
你补充的操作可以脱敏整理为:
sh
source <YOCTO_SDK>/environment-setup-armv8a-poky-linux
export WORK_ROOT=<WORK_ROOT>
export LELY_STAGE=${WORK_ROOT}/lely-core/build-imx8p/stage
export PKG_CONFIG_SYSROOT_DIR=${LELY_STAGE}
export PKG_CONFIG_LIBDIR=${LELY_STAGE}<INSTALL_PREFIX>/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/share/pkgconfig
pkg-config --cflags liblely-coapp
pkg-config --libs liblely-coapp
source environment-setup-armv8a-poky-linux 做什么
这一步加载 Yocto SDK 的交叉编译环境,通常会设置:
| 变量 | 常见作用 |
|---|---|
CC / CXX |
指向目标平台交叉编译器。 |
AR / RANLIB / STRIP |
指向目标平台 binutils 工具。 |
SDKTARGETSYSROOT |
指向 Yocto SDK 自带目标 sysroot。 |
CFLAGS / CXXFLAGS |
附带目标架构、sysroot、安全编译参数等。 |
LDFLAGS |
附带目标链接参数。 |
PKG_CONFIG_* |
有些 SDK 会设置默认 pkg-config 交叉环境。 |
加载 SDK 后再设置 Lely 相关 PKG_CONFIG_*,目的是让后续应用既能找到 Lely 自己的 .pc 文件,也能继续找到 Yocto sysroot 里的系统库 .pc 文件。
PKG_CONFIG_SYSROOT_DIR=${LELY_STAGE} 做什么
PKG_CONFIG_SYSROOT_DIR 用于交叉编译 sysroot 前缀处理。对 .pc 文件里的 -I${includedir}、-L${libdir} 这类路径,pkg-config 会把 sysroot 加到路径前面。
假设 liblely-coapp.pc 中写的是:
pkgconfig
prefix=<INSTALL_PREFIX>
includedir=${prefix}/include
libdir=${prefix}/lib
Cflags: -I${includedir}
Libs: -L${libdir} -llely-coapp
设置:
sh
export PKG_CONFIG_SYSROOT_DIR=<LELY_STAGE>
查询结果就会变成:
text
-I<LELY_STAGE><INSTALL_PREFIX>/include
-L<LELY_STAGE><INSTALL_PREFIX>/lib -llely-coapp ...
这正好指向 staging 目录中的目标头文件和目标库,而不是构建主机上的路径。
PKG_CONFIG_LIBDIR=... 做什么
PKG_CONFIG_LIBDIR 指定 .pc 文件搜索目录。交叉编译时更推荐明确设置它,而不是只追加 PKG_CONFIG_PATH,因为要避免 pkg-config 意外搜索到构建主机上的 x86_64 .pc 文件。
本次设置可以拆开看:
text
PKG_CONFIG_LIBDIR=
${LELY_STAGE}<INSTALL_PREFIX>/lib/pkgconfig
:${SDKTARGETSYSROOT}/usr/lib/pkgconfig
:${SDKTARGETSYSROOT}/usr/share/pkgconfig
| 路径 | 作用 |
|---|---|
${LELY_STAGE}<INSTALL_PREFIX>/lib/pkgconfig |
找到本次 staged install 生成的 liblely-*.pc。 |
${SDKTARGETSYSROOT}/usr/lib/pkgconfig |
找到 Yocto 目标 sysroot 中库相关 .pc 文件。 |
${SDKTARGETSYSROOT}/usr/share/pkgconfig |
找到 Yocto 目标 sysroot 中架构无关 .pc 文件。 |
这样配置后,pkg-config 查询 liblely-coapp 时会优先从 Lely staging 目录读取:
text
<LELY_STAGE><INSTALL_PREFIX>/lib/pkgconfig/liblely-coapp.pc
再根据它的 Requires、Libs、Cflags 输出完整参数。
本次查询输出说明
脱敏后的查询输出是:
sh
pkg-config --cflags liblely-coapp
# -I<LELY_STAGE><INSTALL_PREFIX>/include
pkg-config --libs liblely-coapp
# -L<LELY_STAGE><INSTALL_PREFIX>/lib -llely-coapp -llely-io2 -llely-ev -llely-co -llely-can -llely-util -llely-libc -lrt
逐项解释:
| 输出片段 | 含义 |
|---|---|
-I<LELY_STAGE><INSTALL_PREFIX>/include |
编译阶段搜索 Lely 头文件,例如 <lely/coapp/master.hpp>。 |
-L<LELY_STAGE><INSTALL_PREFIX>/lib |
链接阶段搜索 Lely 库文件目录。 |
-llely-coapp |
链接 C++ CANopen application library。 |
-llely-io2 |
链接新 I/O 异步接口库。 |
-llely-ev |
链接事件库。 |
-llely-co |
链接 CANopen 协议栈库。 |
-llely-can |
链接 CAN 基础库。 |
-llely-util |
链接公共工具库。 |
-llely-libc |
链接 Lely 兼容层库。 |
-lrt |
链接 POSIX realtime 库。某些平台/工具链上 timer、clock 等接口需要它。 |
这说明 lib/pkgconfig/liblely-coapp.pc 和它依赖的 .pc 文件已经能被当前 shell 中的 pkg-config 正确找到。
推荐增加的检查命令
为了确认没有误用主机 .pc 文件,可以执行:
sh
pkg-config --path liblely-coapp
pkg-config --modversion liblely-coapp
pkg-config --print-errors --exists liblely-coapp
pkg-config --cflags --libs liblely-coapp
期望 --path 输出在:
text
<LELY_STAGE><INSTALL_PREFIX>/lib/pkgconfig/liblely-coapp.pc
如果输出跑到 /usr/lib/pkgconfig、/usr/share/pkgconfig 或主机其他路径,说明交叉编译环境有污染风险。
后续应用如何使用 staged Lely
编译一个依赖 liblely-coapp 的 C++ 应用
sh
source <YOCTO_SDK>/environment-setup-armv8a-poky-linux
export WORK_ROOT=<WORK_ROOT>
export LELY_STAGE=${WORK_ROOT}/lely-core/build-imx8p/stage
export PKG_CONFIG_SYSROOT_DIR=${LELY_STAGE}
export PKG_CONFIG_LIBDIR=${LELY_STAGE}<INSTALL_PREFIX>/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/share/pkgconfig
${CXX} main.cpp -o canopen_app \
$(pkg-config --cflags liblely-coapp) \
$(pkg-config --libs liblely-coapp)
如果你的应用还依赖 Yocto sysroot 中的其他库,例如 libsocketcan、glib-2.0 或其他目标库,也应通过同一个交叉 pkg-config 环境查询,避免混入主机依赖。
CMake 项目示例
cmake
find_package(PkgConfig REQUIRED)
pkg_check_modules(LELY_COAPP REQUIRED liblely-coapp)
add_executable(canopen_app main.cpp)
target_include_directories(canopen_app PRIVATE ${LELY_COAPP_INCLUDE_DIRS})
target_link_directories(canopen_app PRIVATE ${LELY_COAPP_LIBRARY_DIRS})
target_link_libraries(canopen_app PRIVATE ${LELY_COAPP_LIBRARIES})
target_compile_options(canopen_app PRIVATE ${LELY_COAPP_CFLAGS_OTHER})
执行 CMake 前仍需要在 shell 中准备好 PKG_CONFIG_SYSROOT_DIR 和 PKG_CONFIG_LIBDIR。
安装结果应该如何检查
检查 staging 安装树
sh
find <LELY_STAGE><INSTALL_PREFIX> -maxdepth 3 -type f | sort
重点检查:
text
<LELY_STAGE><INSTALL_PREFIX>/include/lely/coapp/master.hpp
<LELY_STAGE><INSTALL_PREFIX>/lib/liblely-coapp.so
<LELY_STAGE><INSTALL_PREFIX>/lib/liblely-coapp.a
<LELY_STAGE><INSTALL_PREFIX>/lib/pkgconfig/liblely-coapp.pc
<LELY_STAGE><INSTALL_PREFIX>/bin/dcf2c
检查库架构
sh
file <LELY_STAGE><INSTALL_PREFIX>/lib/liblely-coapp.so.*
file <LELY_STAGE><INSTALL_PREFIX>/bin/dcf2c
期望结果应显示目标架构,例如 AArch64,而不是构建主机架构。
检查 pkg-config 输出
sh
pkg-config --path liblely-coapp
pkg-config --cflags liblely-coapp
pkg-config --libs liblely-coapp
如果 --cflags 和 --libs 都指向 <LELY_STAGE><INSTALL_PREFIX>,说明 staged Lely 对后续应用编译是可见的。
常见问题
为什么 make install 阶段还会 relink
因为 libtool 需要在安装阶段确认库的最终 rpath、依赖关系和安装目录语义。使用 DESTDIR 时,它会把临时 staging 根和最终 prefix 区分开。日志出现 relink warning 并不等于失败,关键看最终是否安装出 .so、.a、.la 和符号链接。
为什么 pkg-config --libs liblely-coapp 输出了一串 Lely 库
因为 liblely-coapp 是高层 C++ 应用库,它依赖 io2、ev、co、can、util、libc 等底层库。pkg-config 会根据 .pc 中的依赖关系输出链接所需库,避免你手工维护顺序。
为什么使用 PKG_CONFIG_LIBDIR,而不是只设置 PKG_CONFIG_PATH
PKG_CONFIG_PATH 通常是追加搜索路径;交叉编译时,如果默认搜索路径仍包含构建主机的 /usr/lib/pkgconfig,就可能混入主机库参数。PKG_CONFIG_LIBDIR 用于指定基础搜索路径,更适合隔离目标 sysroot 和 staged dependency。
PKG_CONFIG_SYSROOT_DIR 会影响所有路径吗
它主要影响 .pc 输出中的编译/链接路径,例如 -I、-L 后面的路径。对 pkg-config --variable 查询出来的普通变量,不一定自动加 sysroot。因此交叉编译脚本中不要假设所有变量都会被 sysroot 化。
lib/pkgconfig 目录需要部署到目标板吗
如果目标板只运行程序,通常不需要 .pc 文件;目标板运行时需要的是 .so、配置文件和可执行文件。
如果目标板本身还要进行本机编译,或者你要把该目录作为 SDK 给其他工程使用,就应保留 lib/pkgconfig/*.pc。
推荐的最终命令模板
下面是脱敏后的推荐命令模板:
sh
# 1. 加载 Yocto SDK 交叉编译环境
source <YOCTO_SDK>/environment-setup-armv8a-poky-linux
# 2. 配置工作目录
export WORK_ROOT=<WORK_ROOT>
export BUILD_DIR=${WORK_ROOT}/lely-core/build-imx8p
export LELY_STAGE=${BUILD_DIR}/stage
# 3. 配置 Lely 源码
mkdir -p "${BUILD_DIR}"
cd "${BUILD_DIR}"
../configure \
--host=aarch64-poky-linux \
--prefix=<INSTALL_PREFIX> \
--disable-python \
--disable-cython \
--disable-tests \
--disable-unit-tests
# 4. 构建 Lely 库和工具
make -j"$(nproc)"
# 5. 安装到 staging 目录
make install DESTDIR="${LELY_STAGE}"
# 6. 配置 pkg-config,让后续应用找到 staged Lely
export PKG_CONFIG_SYSROOT_DIR=${LELY_STAGE}
export PKG_CONFIG_LIBDIR=${LELY_STAGE}<INSTALL_PREFIX>/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/share/pkgconfig
# 7. 验证 pkg-config 查询结果
pkg-config --path liblely-coapp
pkg-config --cflags liblely-coapp
pkg-config --libs liblely-coapp
关键判断标准
构建和安装是否成功,可以按下面几项判断:
| 检查项 | 期望结果 |
|---|---|
make 结束 |
没有 Error,生成 liblely-*.la 和工具。 |
make install DESTDIR=... 结束 |
staging 中出现 include、lib、lib/pkgconfig、bin、etc。 |
file liblely-coapp.so.* |
显示目标架构,例如 AArch64。 |
pkg-config --path liblely-coapp |
指向 <LELY_STAGE><INSTALL_PREFIX>/lib/pkgconfig/liblely-coapp.pc。 |
pkg-config --cflags liblely-coapp |
输出 -I<LELY_STAGE><INSTALL_PREFIX>/include。 |
pkg-config --libs liblely-coapp |
输出 -L<LELY_STAGE><INSTALL_PREFIX>/lib 和 Lely 依赖库。 |
总结
make 负责"把源码变成库和工具";make install DESTDIR="${PWD}/stage" 负责"把库和工具整理成可部署目录树"。
对本次 Lely CANopen 交叉构建来说,DESTDIR 的主要意义是安全地形成 staging 安装树,而 lib/pkgconfig/*.pc 的主要意义是让后续应用可以通过 pkg-config 自动拿到正确的交叉编译参数。
你补充的:
sh
export PKG_CONFIG_SYSROOT_DIR=${LELY_STAGE}
export PKG_CONFIG_LIBDIR=${LELY_STAGE}<INSTALL_PREFIX>/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/lib/pkgconfig:${SDKTARGETSYSROOT}/usr/share/pkgconfig
是把 staged Lely 接入后续应用交叉编译流程的关键步骤。只要 pkg-config --path liblely-coapp 指向 staging 里的 .pc 文件,并且 --cflags、--libs 输出指向 <LELY_STAGE><INSTALL_PREFIX>,后续应用就可以用这套 Lely 头文件和 AArch64 库进行交叉编译。
参考资料
- GNU Automake Manual, "Building Binary Packages Using DESTDIR": https://www.gnu.org/software/automake/manual/html_node/DESTDIR.html
- GNU Libtool Manual, "Install mode": https://www.gnu.org/software/libtool/manual/html_node/Install-mode.html
- GNU Libtool Manual, "Installing libraries": https://www.gnu.org/software/libtool/manual/html_node/Installing-libraries.html
- Lely CANopen Documentation, "Installation": https://opensource.lely.com/canopen/docs/installation/
- Lely CANopen Documentation, "Library overview": https://opensource.lely.com/canopen/docs/overview/
- pkg-config Guide: https://people.freedesktop.org/\~dbn/pkg-config-guide.html
- pkgconf manual page: https://man.archlinux.org/man/pkgconf.1.en
- Autotools Mythbuster, "Supporting Cross-Compilation": https://autotools.info/pkgconfig/cross-compiling.html