编译框架

更新时间:2023-11-27 08:33:37下载pdf

涂鸦 TuyaOS 基于 NDK Android.mk 的语法,实现了一套模块化的编译体系,您可以基于这套编译体系直接编译固件。也可以在按照规则在 IDE 中新增文件、文件夹,编译系统会自动加载在特定位置的新文件、文件夹,进行编译。

编译体系

在 TuyaOS 开发框架中,编译体系主要由以下内容组成的:

  • 位于根目录的 build_app.sh文件。
  • 位于根目录的 Makefile 文件。
  • 位于 ./scripts/mk/ 目录下的 *.mk 文件。
  • 位于各个源码目录下的 local.mk 文件。
  • 位于 vendor/$(TARGET_PLATFORM)/tuyaos/tuyaos_adapter 目录下的 build.sh 文件。

build_app.sh

build_app.sh 是应用编译入口,从 IDE 直接一键编译,会调用此脚本。build_app.sh 脚本需要提供一些参数,参数如下:

sh build_app.sh $1 $2 $3 $4
  • $1:应用工程路径,如 apps/tuyaos_demo_quickstart
  • $2:应用工程名,如 tuyaos_demo_quickstart
  • $3:版本号,格式 XX.XX.XX,如 1.0.0
  • $4:用户指令 (可选),如 clean

build_app.sh 编译出来的固件,保存在 $1/output 路径下,命名为 $2_xx_$3.bin ,其中 xx 为用途,如生产固件为 QIO,升级固件为 UG,用户区固件为 UA 等。

Makefile

Makefile 是编译体系的入口,它汇聚了所有的 *.mklocal.mk。您在执行编译的时候,从 IDE 直接一键编译,或者是在命令行输入 build_app.sh 并提供参数,或者是在命令行输入 make,最终都会执行 Makefile,实现对整个 TuyaOS 的编译、链接。

在 IDE 上直接执行 make 命令的时候,需要进行选择要编译的应用程序,并且版本会默认为 1.0.0

local.mk

local.mk 用于描述文件所在目录的编译方式,包括源文件、头文件、编译选项、目标文件。它实际上是一个微小的 GNU MakeFile 片段,TuyaOS 编译体系会解析 local.mk,然后按照 local.mk 的描述,对源码进行编译。

以下是一个 local.mk 的示例:

LOCAL_PATH := $(call my-dir)    # 当前文件所在目录
#----
include $(CLEAR_VARS)    # 清除 LOCAL_xxx 变量
LOCAL_MODULE := $(notdir $(LOCAL_PATH))    # 当前模块名

# 模块对外头文件(只能是目录)
# 加载至 CFLAGS 中提供给其他组件使用;打包进 SDK 产物中;
LOCAL_TUYA_SDK_INC := $(LOCAL_PATH)/include

# 模块对外 CFLAGS:其他组件编译时可感知到
LOCAL_TUYA_SDK_CFLAGS :=

# 模块源代码
LOCAL_SRC_FILES := $(shell find $(LOCAL_PATH)/src -name "*.c" -o -name "*.cpp" -o -name "*.cc")

# 模块内部 CFLAGS:仅供本组件使用
LOCAL_CFLAGS :=

# 全局变量赋值
TUYA_SDK_INC += $(LOCAL_TUYA_SDK_INC)  # 此行勿修改
TUYA_SDK_CFLAGS += $(LOCAL_TUYA_SDK_CFLAGS)  # 此行勿修改

include $(BUILD_STATIC_LIBRARY)    # 生成静态库
include $(BUILD_SHARED_LIBRARY)    # 生成动态库
#----

local.mk 文件在 IDE 中一般存在于 ./apps/$(APP_NAME) 目录以及 ./vendor/$(TARGET_PLATFORM)/tuyaos/tuyaos_adapter/ 目录下。

*.mk

*.mk 有很多个,保存在 ./scripts/mk/ 目录下。主要分为两类:

  • 第一类:描述编译目标的 mk 文件:这些 mk 文件主要描述了编译 TuyaOS 和编译应用的时候需要用到的各种目标。

    例如 ./scripts/mk/app.mk 文件描述了编译固件的时候,会编译那些目标。显式目标有:

    • app_comp_static:将./apps/$(APP_NAME)/libs/libtuyaapp_components.a 拷贝到 ./libs/目录下。
    • app_driv_static:将./apps/$(APP_NAME)/libs/libtuyaapp_drivers.a 拷贝到 ./libs/目录下。
    • app_adapter_static:将 $(OUTPUT_DIR)/lib/libtuyaos_adapter.a 拷贝到./libs/目录下。

    除了以上显式的目标之外,还有通过 local.mk 描述的编译:

    • ./vendor/$(TARGET_PLATFORM)/tuyaos/tuyaos_adapter/local.mk 编译成 $(OUTPUT_DIR)/lib/libtuyaos_adapter.a
    • ./apps/$(APP_NAME)/local.mk 编译成 $(OUTPUT_DIR)/lib/lib$(APP_NAME).a

    这些目标都是 app_by_name 的依赖项,在执行 make 命令的时候,默认目标是 appapp 会让您选择合适的编译对象,然后调用 app_by_name,对 app_comp_staticapp_driv_staticapp_adapter_static 进行编译,然后调用位于 vendor/$(TARGET_PLATFORM)/tuyaos/tuyaos_adapter 目录下的 build.sh 来进行链接操作。

  • 第二类:编译过程需要使用的一些规则和操作:如 ./scripts/xmake.mk 里会提供编译过程需要的各种函数;需要编译静态库只要在文件末尾 include xmake_static.mk;编译一个动态库只要在文件末尾 include xmake_shared.mk 等。

build.sh

build.sh 是 TuyaOS 在编译完成之后,把生成的库文件保存在 ./libs/ 目录之后,再进行一些芯片原厂组件编译、使用生成的这些库文件链接、打包成固件。

  • 原厂组件编译:每个原厂都有一些自己的可以开源的组件,如驱动等,可以供您进行修改。这些不属于 TuyaOS 的内容,因此在 TuyaOS 编译之后,再进行编译。

  • 链接:原厂开源组件编译完成之后,此时您拥有了 TuyaOS 编译生成的库文件、原厂开源编译的库文件、原厂闭源的库文件,可以进行二进制文件的生成。

  • 打包生成固件:二进制文件生成之后,按照用途,需要把 BootLoader、固件、配置文件等按需打包生成各种场景下需要的固件,并保存到对应的文件夹之下。

    在链接阶段,TuyaOS 编译产生的,存放于 ./libs 目录下的 *.a 文件,可以按照此顺序添加到最后的 LDFLAGS 中:-l$(APP_BIN_NAME) -ltuyaapp_components -ltuyaapp_drivers -ltuyaos -ltuyaos_adapter

编译操作

TuyaOS 在 IDE 上支持三种编译方式:

  • 方式一:在 IDE 界面上选择 software/TuyaOS/apps/ 目录下子目录,右键菜单选择 build project
  • 方式二:在命令行输入 sh build_app.sh $1 $2 $3 $4,参数请参考 build_app.sh 介绍
  • 方式三:在命令行输入 make,需要进行选择要编译的应用程序,并且版本会默认为 1.0.0

编译方式的内部操作流程如下图所示:

编译框架

编译扩展

在执行 TuyaOS 编译操作的时候,TuyaOS 编译体系会自动的加载默认位置的 local.mk,然后按照 local.mk 的描述去编译这些位置里的源码。这些位置如下表所示。

文件夹 用途 编译产物
apps/$(APP_NAME) 可执行程序目录,一般来说包含程序入口 lib$(APP_NAME).a
vendor/$(TARGET_PLATFORM)/tuyaos/tuyaos_adapter TuyaOS kernel Adapter 实现 libtuyaos_adapter.a

新增文件(夹)

新增源文件放到 apps/$(APP_NAME)/src 目录下,编译系统会自动查找,加入到编译体系中进行编译。

apps/$(APP_NAME)/ 路径中有个 local.mk 文件会通过 Shell 脚本自动遍历 apps/$(APP_NAME)/src/ 下所有的源文件,并添加到 LOCAL_SRC_FILES 变量中进行编译,生成 lib$(APP_NAME).a 库。

使用原厂接口

如果您在开发过程中,想使用芯片原厂提供的功能接口,在使用的地方增加头文件引用,调用函数之外,还需要把函数的声明的头文件路径加入到编译搜索的列表中。此时,可以在对应目录中的 local.mk 文件里新增对应接口的头文件路径。示例如下:

# 模块源代码
LOCAL_SRC_FILES := $(shell find $(LOCAL_PATH)/src -name "*.c" -o -name "*.cpp" -o -name "*.cc")

# 模块内部 CFLAGS:仅供本组件使用
LOCAL_CFLAGS += -I$(ROOT_DIR)/vendor/bk7231n/bk7231n_os/beken378/func/user_driver
LOCAL_CFLAGS += -I$(ROOT_DIR)/vendor/bk7231n/bk7231n_os/beken378/driver/include
LOCAL_CFLAGS += -I$(ROOT_DIR)/vendor/bk7231n/bk7231n_os/beken378/common
LOCAL_CFLAGS += -I$(ROOT_DIR)/vendor/bk7231n/bk7231n_os/beken378/app/config
LOCAL_CFLAGS += -I$(ROOT_DIR)/vendor/bk7231n/bk7231n_os/beken378/driver/common
LOCAL_CFLAGS += -I$(ROOT_DIR)/vendor/bk7231n/bk7231n_os/beken378/os/include
LOCAL_CFLAGS += -I$(ROOT_DIR)/vendor/bk7231n/bk7231n_os/beken378/os/FreeRTOSv9.0.0

支持与帮助

在开发过程遇到问题,您可以登录 TuyaOS 开发者论坛 联网单品开发版块 进行沟通咨询。