Lely CANopen 交叉构建说明:为什么执行 make install DESTDIR="${PWD}/stage",以及 lib/pkgconfig 的作用

@[toc]

在这里插入图片描述

结论

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

1
2
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 等编译和链接参数。

先看完整流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
flowchart TD
A[加载 Yocto SDK 环境] --> B[配置 lely-core: ../configure]
B --> C[生成 Makefile / config.h / libtool]
C --> D[make -j]
D --> E[编译 .lo / .o]
E --> F[链接 .la / .so / .a / 工具]
F --> G[make install DESTDIR=stage]
G --> H[安装 include/lely 头文件]
G --> I[安装 lib 动态库/静态库]
G --> J[安装 lib/pkgconfig/*.pc]
G --> K[安装 bin 工具]
G --> L[安装 etc 默认 DCF]
J --> M[后续应用通过 pkg-config 获取 cflags/libs]
H --> N[形成可打包/可部署 staging 目录]
I --> N
K --> N
L --> N

对应关系可以简化成:

阶段 命令 主要作用 输出位置
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/...,便于检查安装结果、生成文件清单或打包部署。

本次流程可以抽象成:

1
2
3
DESTDIR          = <LELY_STAGE>
prefix = <INSTALL_PREFIX>
实际写入路径 = <LELY_STAGE><INSTALL_PREFIX>

例如:

1
2
3
4
5
<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
1
2
3
4
5
flowchart LR
A[configure --prefix=<INSTALL_PREFIX>] --> C[记录最终安装路径语义]
B[make install DESTDIR=<LELY_STAGE>] --> D[临时安装根]
C --> E[<LELY_STAGE><INSTALL_PREFIX>]
D --> E

这一步的价值是:

  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,更常见的配置应改成:

1
2
3
4
5
6
7
8
9
10
../configure \
--host=aarch64-poky-linux \
--prefix=/usr \
--disable-python \
--disable-cython \
--disable-tests \
--disable-unit-tests

make -j"$(nproc)"
make install DESTDIR="${PWD}/stage"

这样安装树会是:

1
2
3
4
stage/usr/include
stage/usr/lib
stage/usr/lib/pkgconfig
stage/usr/bin

而不是:

1
2
3
4
stage/<INSTALL_PREFIX>/include
stage/<INSTALL_PREFIX>/lib
stage/<INSTALL_PREFIX>/lib/pkgconfig
stage/<INSTALL_PREFIX>/bin

make 构建了什么

日志显示 make -j"$(nproc)" 进入了 docincludesrcpythonpkgconfigtools 等目录。真正发生大量编译和链接的是 srctools

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
graph TD
libc[liblely-libc] --> util[liblely-util]
libc --> tap[liblely-tap]
libc --> can[liblely-can]
util --> can
libc --> co[liblely-co]
util --> co
can --> co
libc --> ev[liblely-ev]
util --> ev
can --> io[liblely-io]
util --> io
libc --> io2[liblely-io2]
util --> io2
can --> io2
ev --> io2
co --> coapp[liblely-coapp]
io2 --> coapp
ev --> coapp
co --> tools[can2udp / coctl / cocat / cocatd / dcf2c]
io --> tools

主要库产物

构建产物 类型 作用
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

1
2
3
make  all-recursive
Making all in src
Making all in tools

表示 Automake 生成的顶层 Makefile 正在递归进入子目录执行 all 目标。docincludepkgconfig 等目录里可能没有需要编译的内容,所以会看到:

1
Nothing to be done for 'all'.

这不是错误,只表示该目录在当前目标下没有新增工作。

CC / CXX / CCLD / CXXLD

日志前缀 含义
CC 使用 C 编译器编译 C 源文件。
CXX 使用 C++ 编译器编译 C++ 源文件。
CCLD 使用 C 链接器驱动进行链接。
CXXLD 使用 C++ 链接器驱动进行链接。

示例:

1
2
3
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 库时,可能出现:

1
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 ...'

示例:

1
libtool: warning: remember to run 'libtool --finish <INSTALL_PREFIX>/lib'

含义是 libtool 提示最终安装到真实运行目录后,可能还需要让系统动态链接器识别库路径,例如更新动态链接器缓存或设置运行时库搜索路径。

在 staging 场景下,这条 warning 需要按部署方式判断:

部署方式 是否需要手动处理
打包进 rootfs 的标准库目录,如 /usr/lib 通常由 rootfs/package 机制处理
部署到非标准目录,如 <INSTALL_PREFIX>/lib 需要确保目标板运行时能找到 .so
只用于交叉编译应用,不在构建主机运行目标程序 构建主机上通常不需要执行 libtool --finish

目标板运行时可能需要:

1
export LD_LIBRARY_PATH=<INSTALL_PREFIX>/lib:${LD_LIBRARY_PATH}

或者把库路径加入目标系统的动态链接器配置。实际做法取决于 rootfs、发行版和部署策略。

make install DESTDIR 安装了什么

日志显示安装阶段主要复制了以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
graph TD
S[<LELY_STAGE><INSTALL_PREFIX>] --> I[include/lely/...]
S --> L[lib/]
S --> P[lib/pkgconfig/]
S --> B[bin/]
S --> E[etc/]
I --> IH[公共 C/C++ 头文件]
L --> SO[动态库 .so.* 和 .so 符号链接]
L --> A[静态库 .a]
L --> LA[libtool .la]
P --> PC[liblely-*.pc]
B --> T[can2udp / coctl / cocat / cocatd / dcf2c]
E --> CFG[coctl.dcf / cocatd.dcf]

目标安装树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<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 文件结构如下:

1
2
3
4
5
6
7
8
9
10
11
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 文件:

1
2
3
4
5
6
7
8
9
10
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 库。

1
2
3
4
5
6
7
8
9
10
11
flowchart TD
APP[你的 CANopen C++ 应用] --> PC[pkg-config liblely-coapp]
PC --> CFLAGS[-I<LELY_STAGE><INSTALL_PREFIX>/include]
PC --> LIBS[-L<LELY_STAGE><INSTALL_PREFIX>/lib -llely-coapp ...]
LIBS --> COAPP[liblely-coapp]
COAPP --> IO2[liblely-io2]
COAPP --> EV[liblely-ev]
COAPP --> CO[liblely-co]
CO --> CAN[liblely-can]
CO --> UTIL[liblely-util]
UTIL --> LIBC[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 只检查是否存在 脚本判断时

后续应用可以这样使用:

1
2
3
${CXX} main.cpp -o canopen_app \
$(pkg-config --cflags liblely-coapp) \
$(pkg-config --libs liblely-coapp)

如果是 Makefile:

1
2
CXXFLAGS += $(shell pkg-config --cflags liblely-coapp)
LDLIBS += $(shell pkg-config --libs liblely-coapp)

如果使用 Autotools,也可以通过 PKG_CHECK_MODULES 让配置阶段自动检查并填充 <PREFIX>_CFLAGS<PREFIX>_LIBS

加入本次 pkg-config 环境变量说明

你补充的操作可以脱敏整理为:

1
2
3
4
5
6
7
8
9
10
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 中写的是:

1
2
3
4
5
prefix=<INSTALL_PREFIX>
includedir=${prefix}/include
libdir=${prefix}/lib
Cflags: -I${includedir}
Libs: -L${libdir} -llely-coapp

设置:

1
export PKG_CONFIG_SYSROOT_DIR=<LELY_STAGE>

查询结果就会变成:

1
2
-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 文件。

本次设置可以拆开看:

1
2
3
4
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 目录读取:

1
<LELY_STAGE><INSTALL_PREFIX>/lib/pkgconfig/liblely-coapp.pc

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

本次查询输出说明

脱敏后的查询输出是:

1
2
3
4
5
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 文件,可以执行:

1
2
3
4
pkg-config --path liblely-coapp
pkg-config --modversion liblely-coapp
pkg-config --print-errors --exists liblely-coapp
pkg-config --cflags --libs liblely-coapp

期望 --path 输出在:

1
<LELY_STAGE><INSTALL_PREFIX>/lib/pkgconfig/liblely-coapp.pc

如果输出跑到 /usr/lib/pkgconfig/usr/share/pkgconfig 或主机其他路径,说明交叉编译环境有污染风险。

后续应用如何使用 staged Lely

编译一个依赖 liblely-coapp 的 C++ 应用

1
2
3
4
5
6
7
8
9
10
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 项目示例

1
2
3
4
5
6
7
8
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 安装树

1
find <LELY_STAGE><INSTALL_PREFIX> -maxdepth 3 -type f | sort

重点检查:

1
2
3
4
5
<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

检查库架构

1
2
file <LELY_STAGE><INSTALL_PREFIX>/lib/liblely-coapp.so.*
file <LELY_STAGE><INSTALL_PREFIX>/bin/dcf2c

期望结果应显示目标架构,例如 AArch64,而不是构建主机架构。

检查 pkg-config 输出

1
2
3
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

推荐的最终命令模板

下面是脱敏后的推荐命令模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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 自动拿到正确的交叉编译参数。

你补充的:

1
2
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 库进行交叉编译。

参考资料