前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AIoT应用创新大赛--我的项目我做主,使用GN+Ninja来完成构建系统(VSCode开发)

AIoT应用创新大赛--我的项目我做主,使用GN+Ninja来完成构建系统(VSCode开发)

原创
作者头像
忙碌的死龙
修改2022-03-15 10:48:53
1.5K0
修改2022-03-15 10:48:53
举报
文章被收录于专栏:RT1060开发RT1060开发

背景

自从21年接触了OpenHarmony后,就对GN+Ninja的构建系统特别感兴趣,然后自己尝试着做了一个简化版的构建系统。而本次比赛中,如果不考虑使用官方IDE的话,又不想用makefile(主要是不会写),所以还是尝试着用GN+Ninja完成了rt1062的构建系统。windows下未验证相关配置内容,理论上可以使用。

项目地址

https://gitee.com/walker2048/rt1060_play/tree/gn%2Bninja/

本次比赛的内容都会在这个地址开源

源码目录结构说明

代码语言:shell
复制
.
├── build               #编译构建配置文件
├── components          #常规组件(与硬件无关的组件)
├── hardware            #硬件相关代码
├── out                 #编译产物目录(运行编译命令后生成)
├── solutions           #应用程序目录
└── TinyOS              #腾讯TinyOS内核目录

对于喜欢瞎折腾的人来说,没有使用自己最熟悉的目录和源码结构更开心的事儿了(我的项目我做主,折腾不嫌事儿多)。毕竟熟悉一个RTOS也是需要花费很多时间的。好在腾讯TinyOS的定位就是轻量化代码,简化代码功能和配置,能轻松的适应。如果大家不喜欢这样的目录结构,只需要自己修改对应的目录,并更新依赖配置即可。同时记得修改build/config/compiler/BUILD.gn文件中的 include_dirs字段内容,更新头文件目录就可以了。难度并不高。

GN + Ninja构建环境的优势

  1. 代码依赖树清晰明了
  2. GN语法容易看得懂
  3. 构建脚本分工明确
  4. 编译参数可见
  5. 构建速度快

综上所述,GN + Ninja可以成为个人或者公司考虑新的构建系统时,一个非常优质的选择方案。

使用方法

1、 构建配置命令(使用export BOARD=TencentOS_tiny_EVB_AIoT命令先设定好BOARD环境变量,然后在bash环境下执行):

代码语言:shell
复制
gn gen out/${BOARD} --args="product = \"${BOARD}\""

命令解析:若修改了构建脚本(如BUILD.gn文件),应先执行gn gen命令生成新的ninja构建文件,ninja才知道构建内容有变化了。一般不用和ninja命令拆分。out/${BOARD} 为指定生成的编译构建配置目录, --args="product = \"${BOARD}\""为传递给gn的参数,告诉gn为product名称是${BOARD}环境变量配置相关参数(如查找真实的executable对象和相关依赖,以及编译参数)。

2、编译构建命令:

代码语言:shell
复制
ninja -C out/${BOARD}

3、建议使用方式:

我比较懒,喜欢直接在~/.bashrc中配置好BOARD环境变量,并设置命令别名:

代码语言:shell
复制
alias gbuild='gn gen out/${BOARD} --args="product = \"${BOARD}\"" && ninja -C out/${BOARD}'
alias gdesc='gn desc out/${BOARD} --args="product = \"${BOARD}\"" //hardware/board/${BOARD}'
alias gformat='find . -name *\.gni | xargs gn format && find . -name BUILD.gn | xargs gn format'

例如以上分别是gbuild命令,执行了构建配置命令(一般在几毫秒到几十毫秒级别)和编译构建命令(全新构建在11秒左右,增量编译不到1秒,比官方工具快多了);gdesc命令,用于获取编译配置相关的依赖树,编译参数等内容;gformat命令,用于格式化gn配置文件。

也许有朋友会问怎样清理编译产物,直接rm -rf out目录即可。

4、烧录命令:

烧录使用pyocd进行烧录,执行命令(可使用elf文件,hex文件进行烧录,两者都是带了地址的,不需要指定烧录地址)

代码语言:shell
复制
pyocd flash out/${BOARD}/bin/${BOARD}.hex

5、添加源码

完成功能不可避免的需要修改源码,添加c源文件和.h头文件。我们先说一下添加c源文件,可以在组件目录添加c源文件,只需要在组件的BUILD.gn配置文件中修改sources字段内容即可。若需要新增组件,可以复制其他组件的BUILD.gn文件过来,修改组件名称,组件依赖(deps),源文件(sources)等内容即可。若需要添加.h头文件,分为两种情况。1、改文件仅在组件内使用,此时不需要定义头文件目录(按相对路径引用即可);2、若该头文件为组件对外接口定义文件,则需要在前面提到的build/config/compiler/BUILD.gn文件中的 修改include_dirs字段内容。

对于不想了解细节的同学,只需要关注前面的内容即可,有兴趣了解GN构建系统的,可以往下看。

=====================================================================

接下来我们来说明一下GN构建配置文件和一些知识点。由于gn在国内项目应用的非常少,中文资料是少得可怜,想学习gn知识的,只能通过gn help命令和官网文档(基本也和help命令差不多),以及实际应用来学习。好在gn的配置文件是可读性比较高的,理解一些基本的知识点就可以用了。

GN的组件依赖

GN构建系统,它的依赖树根节点是executable类型的对象,然后在这个对象的依赖组件上,延伸至末端组件。例如本项目的依赖树展开为如下内容(可通过命令gn desc out/${BOARD} --args="product = \"${BOARD}\"" //hardware/board/${BOARD} deps --tree来获得,该命令的${BOARD}环境变量为TencentOS_tiny_EVB_AIoT)

代码语言:shell
复制
//TinyOS:TinyOS
  //TinyOS/arch/arm/arm-v7m/common:common
  //TinyOS/arch/arm/arm-v7m/cortex-m7:cortex-m7
  //TinyOS/kernel:kernel
//components:components
  //components/drivers:drivers
  //components/lists:lists
  //components/uart:uart
  //components/utilities:utilities
//hardware/board/TencentOS_tiny_EVB_AIoT:startup
//hardware/board/TencentOS_tiny_EVB_AIoT/device:device
//hardware/board/TencentOS_tiny_EVB_AIoT/xip:xip
//solutions/helloworld:helloworld

例如第一行的//TinyOS:TinyOS,这是executable对象所引用的第一个依赖组件,它的路径是根目录下的TinyOS目录,在此目录下的BUILD.gn配置文件中,使用的是TinyOS同名的对象。下面的例子会说明GN的组件常见配置。

GN组件配置文件语法说明

举个例子,我们拿TinyOS组件的配置文件作为例子(文件路径为TinyOS/BUILD.gn)

代码语言:shell
复制
source_set("TinyOS") {
  deps = [
    "arch/arm/arm-v7m/common",
    "arch/arm/arm-v7m/cortex-m7",
    "kernel",
  ]
}

在这个文件里,我们定义了一个名称为TinyOS的源代码集合对象,为什么要命名成跟文件夹名称一致的对象名称呢?这是GN其中一个规则:若上级指定依赖时,仅给出了路径,那默认的组件对象名称就是该路径最后的文件夹名称(隐式调用)。例如上面所示,TinyOS组件下依赖有三个组件,分别是arch路径的common组件和cortex-m7组件,以及kernel组件。这三个组件文件夹下,也有BUILD.gn配置文件,里面定义了组件名称和组件包含内容(若有依赖则添加deps依赖,无依赖则添加源码)。deps意思是定义本组件依赖的组件名称

末端组件配置语法说明

kernel组件下包含的源码定义如下(文件路径为TinyOS/kernel/BUILD.gn)。本文件中,组件名称与文件夹一致,上级依赖调用时不需要指定组件名称。若组件名称与文件夹名称不一致,则需指定组件名称。如本例中,source_set("kernel"),如果想定义成另一个组件名称(同目录有不同的组件,并且目录名称与组件名称不相符的情况下),可以改成source_set("kernel_name"),而上级组件调用时,应在目录后添加组件名称,比如"kernel:kernel_name",前面是依赖组件的相对目录,后面是组件名称(显式调用)。

代码语言:shell
复制
source_set("kernel") {
  sources = [
    "core/tos_barrier.c",
    "core/tos_binary_heap.c",
    "core/tos_bitmap.c",
    "core/tos_char_fifo.c",
    "core/tos_completion.c",
    "core/tos_countdownlatch.c",
    "core/tos_event.c",
    "core/tos_global.c",
    "core/tos_mail_queue.c",
    "core/tos_message_queue.c",
    "core/tos_mmblk.c",
    "core/tos_mmheap.c",
    "core/tos_mutex.c",
    "core/tos_pend.c",
    "core/tos_priority_mail_queue.c",
    "core/tos_priority_message_queue.c",
    "core/tos_priority_queue.c",
    "core/tos_ring_queue.c",
    "core/tos_robin.c",
    "core/tos_rwlock.c",
    "core/tos_sched.c",
    "core/tos_sem.c",
    "core/tos_stopwatch.c",
    "core/tos_sys.c",
    "core/tos_task.c",
    "core/tos_tick.c",
    "core/tos_time.c",
    "core/tos_timer.c",
    "pm/tos_pm.c",
    "pm/tos_tickless.c",
  ]
}

如上所示,GN的组件依赖,相对传统makefile和cmake文件来说,更容易看懂,也更容易阅读和维护。从组件解耦来说,真正能做到所有的组件都能通过健康的依赖完成整个项目构建的(没有循环依赖和恶性依赖),功能解耦就做的不错了。

构建脚本分析

代码语言:shell
复制
build/                          #构建脚本目录
├── config                      #针对产品的配置目录
│   ├── compiler                                   
│   │   └── BUILD.gn            #编译器常见配置内容,例如编译优化设置等
│   ├── product.gni             #在这里设置产品相关的一些编译判断参数
│   └── rt1062 
│       └── BUILD.gn            #rt1062相关的宏定义和专用编译参数
└── toolchain
    ├── BUILD.gn                #工具链配置
    ├── clang.gni               #预留给clang的配置文件
    └── gcc.gni                 #arm-none-eabi-gcc配置,编译命令构成配置
.gn                             #GN读取的根配置,一般只引用其他文件
BUILD.gn                        #这里的配置为的是以后方便添加开发板
BUILDCONFIG.gn                  #在这里将各种工具和开发板的配置串起来

基本上只需要修改build目录就能完成编译参数的变更,对应功能也拆分得比较细,如果能详细做注释就更好了。因为我不是专业的工程师,有很多注释内容不准确,甚至错误也有可能。

以上就是本次分享的内容,gn的功能相对来说也会有点复杂,但是用习惯之后,就很舒服了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 项目地址
  • 源码目录结构说明
  • GN + Ninja构建环境的优势
  • 使用方法
  • GN的组件依赖
  • GN组件配置文件语法说明
  • 末端组件配置语法说明
  • 构建脚本分析
相关产品与服务
物联网通信
腾讯云物联网通信(Internet of Things Hub, IoT Hub)旨在提供一个安全、稳定、高效的连接平台,帮助开发者低成本、快速地实现“设备-设备”、“设备-用户应用”、“设备-云服务”之间可靠、高并发的数据通信。物联网通信平台不仅可以实现设备之间的互动、设备的数据上报和配置下发,还可以基于规则引擎和腾讯云产品打通,方便快捷地实现海量设备数据的存储、计算以及智能分析。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档