Lely CANopen 交叉构建说明

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` 不要混用](#DESTDIRprefix 不要混用)
    • [`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 install DESTDIR` 安装了什么](#make install DESTDIR 安装了什么)
    • [`lib/pkgconfig` 是什么](#lib/pkgconfig 是什么)
      • [它保存的是 `.pc` 元数据文件](#它保存的是 .pc 元数据文件)
      • [为什么 Lely 会安装多个 `.pc` 文件](#为什么 Lely 会安装多个 .pc 文件)
      • [`pkg-config --cflags` 和 `pkg-config --libs` 分别解决什么问题](#pkg-config --cflagspkg-config --libs 分别解决什么问题)
    • [加入本次 `pkg-config` 环境变量说明](#加入本次 pkg-config 环境变量说明)
      • [`source environment-setup-armv8a-poky-linux` 做什么](#source environment-setup-armv8a-poky-linux 做什么)
      • [`PKG_CONFIG_SYSROOT_DIR={LELY_STAGE}\` 做什么](#`PKG_CONFIG_SYSROOT_DIR={LELY_STAGE}` 做什么)
      • [`PKG_CONFIG_LIBDIR=...` 做什么](#PKG_CONFIG_LIBDIR=... 做什么)
      • 本次查询输出说明
      • 推荐增加的检查命令
    • [后续应用如何使用 staged Lely](#后续应用如何使用 staged Lely)
      • [编译一个依赖 `liblely-coapp` 的 C++ 应用](#编译一个依赖 liblely-coapp 的 C++ 应用)
      • [CMake 项目示例](#CMake 项目示例)
    • 安装结果应该如何检查
      • [检查 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 目录需要部署到目标板吗)
    • 推荐的最终命令模板
    • 关键判断标准
    • 总结
    • 参考资料

结论

在 Lely CANopen / lely-core 的交叉编译流程中:

sh 复制代码
make -j"$(nproc)"
make install DESTDIR="${PWD}/stage"

这两条命令不是重复动作。它们分别解决两个问题:

  1. make:把源码编译、链接成目标平台可用的库和工具。
  2. 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=
临时安装根

这一步的价值是:

  1. 不把 AArch64 目标库安装到 x86_64 构建主机的系统目录。
  2. 可以直接检查最终会安装哪些文件。
  3. 可以把 <LELY_STAGE> 打包进 SDK 或 rootfs 生成流程。
  4. 可以让后续应用把 <LELY_STAGE> 当成 Lely 的临时 sysroot/staging root。
  5. 可以保留 --prefix 的最终部署路径语义。

DESTDIRprefix 不要混用

二者职责不同:

项目 作用 是否进入 .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)" 进入了 docincludesrcpythonpkgconfigtools 等目录。真正发生大量编译和链接的是 srctools
#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 目标。docincludepkgconfig 等目录里可能没有需要编译的内容,所以会看到:

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。

在这类交叉构建中,只要后续没有 errorNo such fileundefined 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 文件描述一个库的:

  1. 头文件目录。
  2. 库文件目录。
  3. 链接库名。
  4. 版本号。
  5. 依赖库关系。
  6. 静态链接时可能额外需要的依赖。

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-coapppkg-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 --cflagspkg-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

再根据它的 RequiresLibsCflags 输出完整参数。

本次查询输出说明

脱敏后的查询输出是:

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 中的其他库,例如 libsocketcanglib-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_DIRPKG_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 对后续应用编译是可见的。

常见问题

因为 libtool 需要在安装阶段确认库的最终 rpath、依赖关系和安装目录语义。使用 DESTDIR 时,它会把临时 staging 根和最终 prefix 区分开。日志出现 relink warning 并不等于失败,关键看最终是否安装出 .so.a.la 和符号链接。

为什么 pkg-config --libs liblely-coapp 输出了一串 Lely 库

因为 liblely-coapp 是高层 C++ 应用库,它依赖 io2evcocanutillibc 等底层库。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 中出现 includeliblib/pkgconfigbinetc
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 库进行交叉编译。

参考资料