把外部 Make 静态库接入 Zephyr 固件构建

已有 C 库可能只带 Makefile,而 Zephyr 应用由 CMake 驱动。把两者接起来,并不是把任意生成的 .a 文件放进链接命令:外部库必须使用与固件一致的目标编译器、归档器、架构和 ABI 选项,构建系统也必须知道归档何时生成、应用何时可以链接。

本文依据 Zephyr 4.4.0 的 External Library 示例完整页面及该版本伴随源码译编。发布维护方为 Zephyr Project,页面未见个人作者署名;C 源文件归属 Intel Corporation,Makefile 还包含 Nordic Semiconductor ASA 的版权声明。核对日期为 2026 年 10 月 5 日。本文采用的演练环境是 Linux、Zephyr 4.4.0、SDK 1.0.1 和 qemu_x86;这是固定的演练范围,本文未安装 SDK、构建或运行 QEMU。

Zephyr CMake 导出 CC、AR、include、definitions 和目标 CFLAGS,经 ExternalProject 调用 Make 生成 libmylib.a,再由 app 依赖并链接归档生成 zephyr.elf。
原创技术示意图:参数传递保证目标一致,构建依赖保证归档先于应用链接。

示例目录里的每个文件做什么

官方示例路径是 samples/application_development/external_lib。库源码已经随 Zephyr 仓库提交,本例不需要在构建时从网络下载业务代码或第三方包。

external_lib/
├── CMakeLists.txt
├── prj.conf
├── sample.yaml
├── src/main.c
└── mylib/
    ├── Makefile
    ├── include/mylib.h
    └── src/mylib.c

main.c 属于 Zephyr 应用,调用库公开的 mylib_hello_world()。库自己的 Makefile 负责把 mylib.c 编译成对象,再归档为 libmylib.a。顶层 CMake 使用 ExternalProject 驱动这个过程,并把结果包装为可链接的 imported library。

先把目标编译信息交给外部构建系统

以下是官方 CMakeLists.txt 的完整功能内容,英文说明注释整理为正文,其余命令与版本源文件保持一致。代码标记为 Apache-2.0。

# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(external_lib)
target_sources(app PRIVATE src/main.c)

zephyr_get_include_directories_for_lang_as_string(       C includes)
zephyr_get_system_include_directories_for_lang_as_string(C system_includes)
zephyr_get_compile_definitions_for_lang_as_string(       C definitions)
zephyr_get_compile_options_for_lang_as_string(           C options)
if(DEFINED CMAKE_C_COMPILER_TARGET)
  set(target_flag "--target=${CMAKE_C_COMPILER_TARGET}")
endif()
set(external_project_cflags
  "${target_flag} ${includes} ${definitions} ${options} ${system_includes}"
)
include(ExternalProject)
set(mylib_src_dir   ${CMAKE_CURRENT_SOURCE_DIR}/mylib)
set(mylib_build_dir ${CMAKE_CURRENT_BINARY_DIR}/mylib)
set(MYLIB_LIB_DIR     ${mylib_build_dir}/lib)
set(MYLIB_INCLUDE_DIR ${mylib_src_dir}/include)

if(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
  set(submake "$(MAKE)")
else()
  set(submake "make")
endif()
ExternalProject_Add(
  mylib_project
  PREFIX     ${mylib_build_dir}
  SOURCE_DIR ${mylib_src_dir}
  BINARY_DIR ${mylib_src_dir}
  CONFIGURE_COMMAND ""
  BUILD_COMMAND
  ${submake}
  PREFIX=${mylib_build_dir}
  CC=${CMAKE_C_COMPILER}
  AR=${CMAKE_AR}
  CFLAGS=${external_project_cflags}
  INSTALL_COMMAND ""
  BUILD_BYPRODUCTS ${MYLIB_LIB_DIR}/libmylib.a
)
add_library(mylib_lib STATIC IMPORTED GLOBAL)
add_dependencies(mylib_lib mylib_project)
set_target_properties(mylib_lib PROPERTIES
  IMPORTED_LOCATION ${MYLIB_LIB_DIR}/libmylib.a)
set_target_properties(mylib_lib PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES ${MYLIB_INCLUDE_DIR})
target_link_libraries(app PUBLIC mylib_lib)

四个 zephyr_get_…_for_lang_as_string 调用收集 C 编译所需的普通包含目录、系统包含目录、宏定义和编译选项;若设置了 CMAKE_C_COMPILER_TARGET,还传入 --target=…。这些字符串共同成为外部 Make 的 CFLAGS。

仅仅传递 CC 还不够。同一编译器可生成不同指令集、调用约定、浮点 ABI 或布局的目标文件;库与应用必须一致。也不能认为任何第三方库都接受全部 Zephyr 参数:原例注释明确指出,有的外部构建系统能够独立配置目标,有的与某些选项不兼容,需要过滤。集成真实库时,应核对它自己的构建接口,不要机械复制参数串。

ExternalProject 的目录、产物和依赖

mylib_src_dir 指向仓库里的库源码;mylib_build_dir 指向当前应用构建目录下的 mylib。虽然 BINARY_DIR 被设为源码根目录,让 Make 从那里启动,但传给 Make 的 PREFIX 把对象与归档输出放到构建树里。默认使用名为 build 的构建目录时,对应路径为:

build/mylib/obj/mylib.o
build/mylib/lib/libmylib.a
build/zephyr/zephyr.elf

CONFIGURE_COMMAND "" 和 INSTALL_COMMAND "" 分别跳过独立配置、安装阶段,本例直接调用 Make。BUILD_BYPRODUCTS 告诉上层构建系统该步骤会生成 libmylib.a,这对 Ninja 等需要明确依赖图的生成器尤其关键。

归档不是由 CMake 的普通源码目标直接生成,因此建立 STATIC IMPORTED GLOBAL 目标 mylib_lib,用 IMPORTED_LOCATION 指明归档路径,再通过 add_dependencies 要求 mylib_project 先完成。INTERFACE_INCLUDE_DIRECTORIES 把公开头文件目录传递给使用者,最后把 mylib_lib 链接到 app。产物声明、构建依赖和链接关系缺一不可:只写链接路径,容易遇到并行构建时文件尚未产生;只写构建依赖,又不会自动把库符号带进固件。

当顶层生成器是 Unix Makefiles 时,示例使用 $(MAKE) 递归调用子 Make;其他生成器则查找 PATH 里的 make。因此即便顶层用 Ninja,GNU Make 仍然是依赖。不要因为缺少 make 就改为用宿主默认 gcc 手工生成库。

外部 Makefile 如何编译和归档

官方 Makefile 如下。这里保留原版权与清理目标供静态理解;其中 clean 含递归删除,本文没有执行。实际使用前必须确认对象和归档路径都局限在专用构建目录。

#
# Copyright (c) 2017 Intel Corporation
# Copyright (c) 2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
PREFIX ?= .
OBJ_DIR ?= $(PREFIX)/obj
LIB_DIR ?= $(PREFIX)/lib

ifeq ($(findstring Windows, $(OS)),Windows)
    DEL := rmdir /S /Q
    MKDIR := mkdir
else
    DEL := rm -rf
    MKDIR := mkdir -p
endif

all:
	-$(MKDIR) "$(OBJ_DIR)"
	-$(MKDIR) "$(LIB_DIR)"
	$(CC) -c $(CFLAGS) -MD -Iinclude src/mylib.c -o $(OBJ_DIR)/mylib.o
	$(AR) -rcs $(LIB_DIR)/libmylib.a $(OBJ_DIR)/mylib.o
clean:
	$(DEL) "$(OBJ_DIR)" "$(LIB_DIR)"

?= 提供可覆盖的默认路径;CMake 传入 PREFIX 后,对象与归档就在构建树中。Make recipe 行必须保留制表符缩进。$(CC) -c 仅编译,$(AR) -rcs 把对象放进静态归档;-Iinclude 提供库自身公开头文件目录,-MD 生成编译依赖文件。

这里有几个真实的工程边界。创建目录命令前面的减号会让 Make 忽略该命令的失败;它方便处理目录已存在的情形,也可能掩盖权限或路径问题。编译和归档行里的部分路径未加引号,带空格的工作目录或工具链路径可能解析错误。参数通过 Make 与 shell 解释,不能让不可信输入控制 CC、AR、CFLAGS 或目录变量。外部构建脚本必须作为能执行命令的代码审查,不能当成纯数据。

这个教学 Makefile 没有消费 -MD 生成的依赖文件,顶层 ExternalProject 也使用自己的步骤与时间戳管理。不要仅凭第一次构建成功,就推定每次修改都能正确触发增量构建。变更库头文件、源文件或编译参数后,应检查是否重建了库;可在新的专用构建目录做完整验证,避免复用陈旧归档。本文没有声称已复现某种增量构建失败。

应用和库的最小接口

以下三个源文件共同展示调用链,逻辑与官方版本一致:

/* mylib/include/mylib.h
 * Copyright (c) 2017 Intel Corporation
 * SPDX-License-Identifier: Apache-2.0
 */
#ifndef _MYLIB_H_
#define _MYLIB_H_
int mylib_hello_world(void);
#endif
/* mylib/src/mylib.c
 * Copyright (c) 2017 Intel Corporation
 * SPDX-License-Identifier: Apache-2.0
 */
#include "mylib.h"
#include <stdio.h>

int mylib_hello_world(void)
{
    printf("mylib says: Hello World!\n");
    return 0;
}
/* src/main.c
 * Copyright (c) 2017 Intel Corporation
 * SPDX-License-Identifier: Apache-2.0
 */
#include <zephyr/kernel.h>
#include <stdio.h>
#include <mylib.h>

int main(void)
{
    printf("Hello World!\n");
    mylib_hello_world();
    return 0;
}

prj.conf 只有下面一项,启用标准输出到控制台:

CONFIG_STDOUT_CONSOLE=y

这些 C 代码只打印固定字符串,没有接收不可信格式字符串、解析外部数据或使用秘密。这个结论仅覆盖所读到的极小例子,不是对任意接入库或整个 Zephyr 依赖树的安全背书。

固定环境下如何安排一次验证

在已完成官方工具链准备的隔离 Linux 工作区,确认使用 Zephyr 4.4.0 和预先核对的 SDK,并且 GNU Make 可用。选择一个新建、无个人文件和凭据的构建目录。本文演练采用 qemu_x86,因此可参考如下构建与运行命令;这些命令由本文针对该约定环境给出,不是本文执行记录:

west build -b qemu_x86 -d build_external_lib \
  samples/application_development/external_lib
west build -d build_external_lib -t run

这里显式指定了 build_external_lib,相应产物应是 build_external_lib/mylib/lib/libmylib.a 与 build_external_lib/zephyr/zephyr.elf。检查编译日志中的 CC、AR 和目标参数是否来自同一 Zephyr 配置,再检查最终固件是否调用了库。

官方 sample.yaml 把 native_sim 列为 integration platform,console harness 匹配以下两行。它支持理解示例预期验证什么,不能当作 qemu_x86 已实测通过的证据:

Hello World!
mylib says: Hello World!

如果没有库那一行,要区分“库没有构建”“没有链接”“主程序没有调用”“控制台输出配置不对”;如果链接报目标格式或 ABI 错误,先检查外部 Make 是否真正用了传入的目标编译器和选项,而不是先给链接器添加忽略错误的参数。替换真实库后还需检查许可证、依赖、内存分配模型、线程与中断上下文要求。

Windows 说明与版本边界

原文另有 Windows 提示:GNU Make 必须在 PATH 中,可以通过已有的 Chocolatey 安装方式 choco install make,或从 GnuWin32 的 Make 页面取得 Binaries 与 Dependencies 并解压到同一目录,也可以使用 Complete package 安装方式。本文保留这些信息作为来源说明,不把旧的预编译下载渠道自动视为今天的推荐环境;安装来源、版本与 PATH 中实际执行的文件须独立核对。本文主流程采用 Linux,未运行任何安装命令。

文档页面显示本页源码最后更新于 2024 年 8 月 23 日、生成于 2026 年 4 月 14 日;读取的是 4.4.0 固定页面。换用其他版本时,应重新核对 CMake 辅助函数、工具链与 board target 名称。SDK 1.0.1 是本文选定的组合,本文未独立完成其安装及兼容性实测。

来源、修改与许可

源页由 Zephyr Project members and individual contributors 维护,页面版权为 © 2015–2026。本文完整翻译源页说明,并基于 v4.4.0 示例源码扩展讲解 CMake、Makefile、头文件、实现、应用、配置与测试定义。代码保留 Intel / Nordic 归属并采用 Apache License 2.0;许可证全文见文末。Zephyr 许可说明列有其他组件例外,不能把本例许可推广到全部第三方库。

修改包括中文说明、格式整理、固定环境命令与安全边界,未声称改写后的示例经过执行测试;没有提供未经验证的“安全 Makefile 替代版”。原创配图不是软件运行截图。

原始示例测试定义与代码许可

这是v4.4.0原始 sample.yaml,其integration platform为native_sim;前文qemu_x86演练命令是编辑补充,仍未执行。来源:固定版本测试定义。

sample:
  name: External Library
tests:
  sample.app_dev.external_lib:
    integration_platforms:
      - native_sim
    tags: external
    harness: console
    harness_config:
      type: multi_line
      regex:
        - "Hello World!"
        - "mylib says: Hello World!"

下列完整Apache License2.0适用于前述标有SPDX声明的示例代码。保留 Copyright (c)2017 Intel Corporation及Makefile的 Copyright (c)2022 Nordic Semiconductor ASA;文档来源版权为©2015–2026 Zephyr Project members and individual contributors。中文说明、格式整理和已注明的演练参数为未完纪于2026-10-05所作修改。其他组件的许可仍需分别判断。

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容